libzypp 17.37.17
mediafacade.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
9#include "mediafacade.h"
12#include <zypp/ZYppCallbacks.h>
13#include <zypp-core/TriBool.h>
15#include <utility>
16#include <zypp-media/ng/ProvideSpec>
17#include <zypp-media/mount.h>
19
20namespace zyppng {
21
23 {
24
25 public:
26
28
30 const ProvideMediaSpec &spec() const;
31 zypp::Url url() const;
32 const zypp::MirroredOrigin &origin() const;
33 const std::optional<zypp::Pathname> &rootPath() const;
34 MediaSyncFacadeRef parent() const;
35
40
41 // ReferenceCounted interface
42 protected:
43 void unref_to(unsigned int) const override;
44
45 private:
49 MediaSyncFacadeRef _parent;
50 std::optional<zypp::Pathname> _localPath;
51 };
52
54
56 : _id( mediaId )
57 , _origin( std::move( origin ) )
58 , _spec(std::move( mediaSpec ))
59 , _parent(std::move( parentRef ))
60 , _localPath( locPath )
61 {}
62
67
69 {
70 return _spec;
71 }
72
74 {
75 return _origin.authority().url();
76 }
77
79 {
80 return _origin;
81 }
82
83 const std::optional<zypp::Pathname> &AttachedSyncMediaInfo::rootPath() const
84 {
85 return _localPath;
86 }
87
88 MediaSyncFacadeRef AttachedSyncMediaInfo::parent() const
89 {
90 return _parent;
91 }
92
97
98 void AttachedSyncMediaInfo::unref_to( unsigned int count ) const
99 {
100 // once count reaches 1 only the MediaSyncFacade holds a reference,
101 // time to release the medium
102 if ( count == 1 ) {
103 _parent->releaseMedium ( this );
104 // !!!! careful from here on out 'this' is most likely invalid !!!!
105 return;
106 }
107 }
108
109
111
112 SyncMediaHandle::SyncMediaHandle(AttachedSyncMediaInfo_Ptr dataPtr) : _data( std::move(dataPtr) )
113 { }
114
115 MediaSyncFacadeRef SyncMediaHandle::parent() const
116 {
117 return _data->parent();
118 }
119
121 {
122 return _data.get() != nullptr;
123 }
124
126 {
127 static zypp::Url invalidHandle;
128 if ( !_data ) return invalidHandle;
129 return _data->origin().authority().url();
130 }
131
132 const std::optional<zypp::Pathname> &SyncMediaHandle::localPath() const
133 {
134 static std::optional<zypp::Pathname> invalidPath;
135 if ( !_data )
136 return invalidPath;
137 return _data->rootPath();
138 }
139
141 {
142 return *_data;
143 }
144
146 : _res( std::move(file) )
147 , _provideHandle( std::move (hdl) )
148 { }
149
151 return _res;
152 }
153
155
157 {
158 const auto &handlerType = zypp::media::MediaHandlerFactory::handlerType ( origin.authority().url() );
159 if ( !handlerType ) {
160 ERR << "Authority URL: " << origin.authority() << " is not supported!" << std::endl;
161 return {};
162 }
163
164 zypp::MirroredOrigin sanitized( origin.authority() );
165 for ( const auto &mirror : origin.mirrors() ) {
166 const auto &s = zypp::media::MediaHandlerFactory::handlerType ( mirror.url() );
167 if ( !s ) {
168 WAR << "URL: " << mirror << " is not supported, ignoring!" << std::endl;
169 continue;
170 }
171 if ( handlerType == *s) {
172 sanitized.addMirror(mirror);
173 } else {
174 WAR << "URL: " << mirror << " has different handler type than the authority URL: "<< origin.authority() <<", ignoring!" << std::endl;
175 }
176 }
177
178 return sanitized;
179 }
180
185
187 {
188 // rewrite and sanitize the urls if required
189 zypp::MirroredOrigin sanitizedOrigin = sanitizeUrls(origin);
190
191 if ( !sanitizedOrigin.isValid() )
193
194 if ( request.medianr() > 1 ) {
195 for ( auto &ep : sanitizedOrigin )
196 ep.url() = zypp::MediaSetAccess::rewriteUrl( ep.url(), request.medianr() );
197 }
198
199 // first try and find a already attached medium
200 auto i = std::find_if( _attachedMedia.begin (), _attachedMedia.end(), [&]( const AttachedSyncMediaInfo_Ptr &medium ) {
201 return medium->isSameMedium( sanitizedOrigin, request );
202 });
203
204 if ( i != _attachedMedia.end() ) {
206 }
207
208 bool isVolatile = sanitizedOrigin.authority().url().schemeIsVolatile();
209
210 std::optional<zypp::media::MediaAccessId> attachId;
212
213 // nothing attached, make a new one
215 do {
216 try {
217 if ( !attachId ) {
218 attachId = mgr.open( sanitizedOrigin );
219 if ( !request.mediaFile().empty() ) {
220 mgr.addVerifier( *attachId, zypp::media::MediaVerifierRef( new zypp::repo::SUSEMediaVerifier( request.mediaFile(), request.medianr() ) ) );
221 }
222 }
223
224 // attach the medium
225 mgr.attach( *attachId );
226
227 auto locPath = mgr.localPath( *attachId, "/" );
228 auto attachInfo = AttachedSyncMediaInfo_Ptr( new AttachedSyncMediaInfo( shared_this<MediaSyncFacade>(), *attachId, std::move(sanitizedOrigin), request, locPath ) );
229 _attachedMedia.push_back( attachInfo );
230 return expected<MediaSyncFacade::MediaHandle>::success( std::move(attachInfo) );
231
232 } catch ( const zypp::media::MediaException &excp ) {
233
234 ZYPP_CAUGHT(excp);
235
236 // if no one is listening, just return the error as is
239 }
240
241 // default action is to cancel
243
244 do {
245 // here: Manager tried all not attached drives and could not find the desired medium
246 // we need to send the media change report
247
248 // set up the reason
250 if( typeid(excp) == typeid( zypp::media::MediaNotDesiredException) ) {
252 }
253
254 unsigned int devindex = 0;
255
256 std::vector<std::string> devices;
257 mgr.getDetectedDevices(*attachId, devices, devindex);
258
259 std::optional<std::string> currentlyUsed;
260 if ( devices.size() ) currentlyUsed = devices[devindex];
261
262 if ( isVolatile ) {
263 // filter devices that are mounted, aka used, we can not eject them
264 const auto &mountedDevs = zypp::media::Mount::getEntries();
265 devices.erase( std::remove_if( devices.begin (), devices.end(), [&](const std::string &dev) {
266 zypp::PathInfo devInfo(dev);
267 return std::any_of( mountedDevs.begin (), mountedDevs.end(), [&devInfo]( const zypp::media::MountEntry &e ) {
268 zypp::PathInfo pi( e.src );
269 return ( pi.isBlk() && pi.devMajor() == devInfo.devMajor() && pi.devMinor() == devInfo.devMinor() );
270 });
271 }), devices.end() );
272
273 if ( !devices.size () ) {
274 // Jammed, no currently free device
275 MIL << "No free device available, return jammed and try again later ( hopefully) " << std::endl;
276 if ( attachId ) mgr.close ( *attachId );
278 }
279
280 // update index to currenty used dev
281 bool foundCurrent = false;
282 if ( currentlyUsed ) {
283 for ( unsigned int i = 0; i < devices.size(); i++ ) {
284 if ( devices[i] == *currentlyUsed ) {
285 foundCurrent = true;
286 devindex = i;
287 break;
288 }
289 }
290 }
291
292 if ( !foundCurrent ){
293 devindex = 0; // seems 0 is what is set in the handlers too if there is no current
294 }
295 }
296
297 zypp::Url effectiveUrl = sanitizedOrigin.authority().url();
298
299 user = report->requestMedia (
300 effectiveUrl,
301 request.medianr(),
302 request.label(),
303 reason,
304 excp.asUserHistory(),
305 devices,
306 devindex
307 );
308
309 MIL << "ProvideFile exception caught, callback answer: " << user << std::endl;
310
311 switch ( user ) {
313 DBG << "Aborting" << std::endl;
314 if ( attachId ) mgr.close ( *attachId );
315 zypp::AbortRequestException aexcp("Aborting requested by user");
316 aexcp.remember(excp);
318 }
320 DBG << "Skipping" << std::endl;
321 if ( attachId ) mgr.close ( *attachId );
322 zypp::SkipRequestException nexcp("User-requested skipping of a file");
323 nexcp.remember(excp);
325 }
327 DBG << "Eject: try to release" << std::endl;
328 try
329 {
330 // MediaSetAccess does a releaseAll, but we can not release other devices that are in use
331 // media_mgr.releaseAll();
332 mgr.release (*attachId, devindex < devices.size() ? devices[devindex] : "");
333 }
334 catch ( const zypp::Exception & e)
335 {
336 ZYPP_CAUGHT(e);
337 }
338 break;
339 }
342 // retry
343 DBG << "Going to try again" << std::endl;
344
345 // invalidate current media access id
346 if ( attachId ) {
347 mgr.close(*attachId);
348 attachId.reset();
349 }
350
351 // explicitely setting a URL from the callback wipes all mirrors
352 if ( sanitizedOrigin.authority().url() != effectiveUrl ) {
353 sanitizedOrigin.clearMirrors ();
354 sanitizedOrigin.setAuthority ( effectiveUrl );
355 }
356
357 // not attaching, media set will do that for us
358 // this could generate uncaught exception (#158620)
359 break;
360 }
361 default: {
362 DBG << "Don't know, let's ABORT" << std::endl;
363 if ( attachId ) mgr.close ( *attachId );
365 }
366 }
367 } while( user == zypp::media::MediaChangeReport::EJECT );
368 } catch ( const zypp::Exception &e ) {
369 ZYPP_CAUGHT(e);
370 if ( attachId ) mgr.close ( *attachId );
372 } catch (...) {
373 // didn't work -> clean up
374 if ( attachId ) mgr.close ( *attachId );
376 }
377 } while ( true );
378 }
379
381 {
382 // this should never happen because every handle has a reference to the media manager, but still add a debug output
383 // so we know in case we have weird behavior.
384 if ( _attachedMedia.size () ) {
385 WAR << "Releasing zyppng::MediaSyncFacade with still valid MediaHandles, this is a bug!" << std::endl;
386 }
387 }
388
390 {
391 const auto &sanitizedOrigin = sanitizeUrls(origin);
392 if ( !sanitizedOrigin.isValid() )
394 return expected<LazyMediaHandle>::success( shared_this<MediaSyncFacade>(), std::move(sanitizedOrigin), request );
395 }
396
401
403 {
404 using namespace zyppng::operators;
405 if ( lazyHandle.attached() )
406 return expected<MediaHandle>::success( *lazyHandle.handle() );
407
408 MIL << "Attaching lazy medium with label: [" << lazyHandle.spec().label() << "]" << std::endl;
409
410 return attachMedia( lazyHandle.origin(), lazyHandle.spec () )
411 | and_then([lazyHandle]( MediaHandle handle ) {
412 lazyHandle._sharedData->_mediaHandle = handle;
413 return expected<MediaHandle>::success( std::move(handle) );
414 });
415 }
416
418 {
419 using namespace zyppng::operators;
420
421 if ( !origin.isValid() )
422 return expected<MediaSyncFacade::Res>::error( ZYPP_EXCPT_PTR ( zypp::media::MediaException("Can not provide a file without a URL.") ));
423
424 std::optional<expected<MediaSyncFacade::Res>> lastErr;
425 for ( const zypp::OriginEndpoint& file_url : origin ) {
426
427 zypp::OriginEndpoint ep(file_url);
428 zypp::Pathname fileName(ep.url().getPathName());
429 ep.url().setPathName ("/");
430
432 | and_then( [&, this]( const MediaSyncFacade::MediaHandle& handle ) {
433 return provide( handle, fileName, request.asOnMediaLocation(fileName, 1));
434 });
435
436 if ( res )
437 return res;
438
439 lastErr = res;
440 }
441
442 // we always should have a last error, except if the URLs are empty
443 if ( lastErr )
444 return *lastErr;
445
446 // we should not get here, but if we do simply use the first entry to make a not found error
447 zypp::Url url( origin.authority().url() );
448 zypp::Pathname fileName(url.getPathName());
449 url.setPathName ("/");
451
452 }
453
458
460 {
462 const auto &handleInfo = attachHandle.info();
463
464 try {
465 if ( request.checkExistsOnly() ) {
466 if ( !mgr.doesFileExist ( handleInfo.mediaId (), fileName ) ) {
468 }
469
470 // we return a result pointing to a non existant file, since the code just asked us to check if the file exists
471 return expected<MediaSyncFacade::Res>::success( attachHandle, zypp::ManagedFile( mgr.localPath( handleInfo.mediaId(), fileName ) ) );
472
473 } else {
474 mgr.provideFile( handleInfo.mediaId (), request.asOnMediaLocation( fileName, handleInfo.spec().medianr()) );
475
476 zypp::ManagedFile locFile( mgr.localPath( handleInfo.mediaId(), fileName ) );
477
478 // do not clean up files for now, they are cleaned up anyways on detach
479#if 0
480 // if the file is downloaded we want to clean it up again
481 if ( handleInfo.url().schemeIsDownloading() )
483#endif
484
485 return expected<MediaSyncFacade::Res>::success( attachHandle, locFile );
486 }
487 } catch ( const zypp::Exception &e ) {
488 ZYPP_CAUGHT(e);
490 } catch (...) {
492 }
493 }
494
496 {
497 using namespace zyppng::operators;
498 return attachMediaIfNeeded ( attachHandle )
499 | and_then([weakMe = weak_this<MediaSyncFacade>(), fName = fileName, req = request ]( MediaHandle handle ){
500 auto me = weakMe.lock();
501 if ( !me )
502 return expected<Res>::error(ZYPP_EXCPT_PTR(zypp::Exception("Provide was released during a operation")));
503 return me->provide( handle, fName, req);
504 });
505 }
506
508 {
509 try {
510 return expected<zypp::CheckSum>::success( zypp::CheckSum( algorithm, zypp::filesystem::checksum ( p, algorithm ) ) );
511 } catch(...) {
512 return expected<zypp::CheckSum>::error ( std::current_exception () );
513 }
514 }
515
517 {
518 try {
519 // do what Provide would do and make a URL
520 zypp::Url url("copy:///");
521 url.setPathName( source );
522
523 auto sourcePi = zypp::PathInfo(source);
524 if ( !sourcePi.isExist() ) {
526 }
527 if ( !sourcePi.isFile () )
529
530 auto res = zypp::filesystem::hardlinkCopy( source, target.asString() );
531 if ( res == 0 ) {
533 } else {
534 return expected<zypp::ManagedFile>::error ( ZYPP_EXCPT_PTR( zypp::media::MediaException( zypp::str::Str() << "Failed to create file " << target << " errno: " << res ) ) );
535 }
536 } catch(...) {
537 return expected<zypp::ManagedFile>::error ( std::current_exception () );
538 }
539 }
540
542 {
543 // not much to do here, since this will block until the file has been copied we do not need to remember the ProvideRes
544 return copyFile( source.file(), target );
545 }
546
548 {
549 if ( !ptr ) return;
550
551 auto i = std::find_if(_attachedMedia.begin (), _attachedMedia.end(), [&]( const auto &p ) { return p.get() == ptr; } );
552
553 try {
555 mgr.close ( ptr->mediaId() );
556 } catch ( const zypp::Exception & e ) {
557 ZYPP_CAUGHT(e);
558 }
559
560 if ( i != _attachedMedia.end() ) {
561 _attachedMedia.erase(i);
562 } else {
563 ERR << "Releasing unknown medium " << ptr->mediaId () << " should not happen";
564 }
565 }
566
567
568}
void setDispose(const Dispose &dispose_r)
Set a new dispose function.
Base class for Exception.
Definition Exception.h:153
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition Exception.cc:140
static Url rewriteUrl(const Url &url_r, const media::MediaNr medianr)
Replaces media number in specified url with given medianr.
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
const std::vector< OriginEndpoint > & mirrors() const
void setAuthority(OriginEndpoint newAuthority)
bool addMirror(OriginEndpoint newMirror)
const OriginEndpoint & authority() const
Represents a single, configurable network endpoint, combining a URL with specific access settings.
const zypp::Url & url() const
Url manipulation class.
Definition Url.h:93
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition Url.cc:622
void setPathName(const std::string &path, EEncoding eflag=zypp::url::E_DECODED)
Set the path name.
Definition Url.cc:782
static bool schemeIsVolatile(const std::string &scheme_r)
cd dvd
Definition Url.cc:487
Base class for reference counted objects.
Wrapper class for stat/lstat.
Definition PathInfo.h:226
bool empty() const
Test for an empty path.
Definition Pathname.h:116
Just inherits Exception to separate media exceptions.
static std::optional< MediaHandlerType > handlerType(const Url &url)
Manages access to the 'physical' media, e.g CDROM drives, Disk volumes, directory trees,...
MediaAccessId open(const Url &url, const Pathname &preferred_attach_point="")
Opens the media access for specified with the url.
void attach(MediaAccessId accessId)
Attach the media using the concrete handler (checks all devices).
void close(MediaAccessId accessId)
Close the media access with specified id.
ZYPP_DEPRECATED void provideFile(MediaAccessId accessId, const Pathname &filename, const ByteCount &expectedFileSize) const
void release(MediaAccessId accessId, const std::string &ejectDev="")
Release the attached media and optionally eject.
bool doesFileExist(MediaAccessId accessId, const Pathname &filename) const
FIXME: see MediaAccess class.
Pathname localPath(MediaAccessId accessId, const Pathname &pathname) const
Shortcut for 'localRoot() + pathname', but returns an empty pathname if media is not attached.
void addVerifier(MediaAccessId accessId, const MediaVerifierRef &verifier)
Add verifier implementation for the specified media id.
void getDetectedDevices(MediaAccessId accessId, std::vector< std::string > &devices, unsigned int &index) const
Fill in a vector of detected ejectable devices and the index of the currently attached device within ...
static MountEntries getEntries(const std::string &mtab="")
Return mount entries from /etc/mtab or /etc/fstab file.
Definition mount.cc:169
Implementation of the traditional SUSE media verifier.
bool isSameMedium(const zypp::MirroredOrigin &origin, const ProvideMediaSpec &spec)
zypp::media::MediaAccessId _id
const zypp::MirroredOrigin & origin() const
MediaSyncFacadeRef _parent
std::optional< zypp::Pathname > _localPath
bool isSameMedium(const zypp::MirroredOrigin &origin, const ProvideMediaSpec &spec)
MediaSyncFacadeRef parent() const
zypp::MirroredOrigin _origin
const std::optional< zypp::Pathname > & rootPath() const
zypp::media::MediaAccessId mediaId() const
void unref_to(unsigned int) const override
const ProvideMediaSpec & spec() const
AttachedSyncMediaInfo(MediaSyncFacadeRef parentRef, zypp::media::MediaAccessId mediaId, zypp::MirroredOrigin origin, ProvideMediaSpec mediaSpec, const zypp::Pathname &locPath)
std::shared_ptr< T > shared_this() const
Definition base.h:113
std::weak_ptr< T > weak_this() const
Definition base.h:123
const zypp::MirroredOrigin & origin() const
const ProvideMediaSpec & spec() const
std::optional< MediaHandle > handle() const
Res(MediaHandle hdl, zypp::ManagedFile file)
const zypp::Pathname file() const
zypp::MirroredOrigin sanitizeUrls(const zypp::MirroredOrigin &origin) const
friend class AttachedSyncMediaInfo
Definition mediafacade.h:53
expected< zypp::CheckSum > checksumForFile(const zypp::Pathname &p, const std::string &algorithm)
::zyppng::LazyMediaHandle< MediaSyncFacade > LazyMediaHandle
Definition mediafacade.h:56
expected< Res > provide(const zypp::MirroredOrigin &origin, const ProvideFileSpec &request)
expected< LazyMediaHandle > prepareMedia(const zypp::MirroredOrigin &origin, const ProvideMediaSpec &request)
expected< zypp::ManagedFile > copyFile(const zypp::Pathname &source, const zypp::Pathname &target)
SyncMediaHandle MediaHandle
Definition mediafacade.h:55
std::vector< AttachedSyncMediaInfo_Ptr > _attachedMedia
expected< MediaHandle > attachMedia(const zypp::MirroredOrigin &origin, const ProvideMediaSpec &request)
expected< MediaHandle > attachMediaIfNeeded(LazyMediaHandle lazyHandle)
void releaseMedium(const AttachedSyncMediaInfo *ptr)
zypp::OnMediaLocation asOnMediaLocation(const zypp::Pathname &path, unsigned int mediaNr) const
bool checkExistsOnly() const
unsigned medianr() const
zypp::Pathname mediaFile() const
const std::string & label() const
const std::optional< zypp::Pathname > & localPath() const
const AttachedSyncMediaInfo & info() const
MediaSyncFacadeRef parent() const
const zypp::Url & baseUrl() const
AttachedSyncMediaInfo_Ptr _data
Definition mediafacade.h:40
static expected success(ConsParams &&...params)
Definition expected.h:115
Definition Arch.h:364
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition NonCopyable.h:26
std::string checksum(const Pathname &file, const std::string &algorithm)
Compute a files checksum.
Definition PathInfo.cc:1056
int hardlinkCopy(const Pathname &oldpath, const Pathname &newpath)
Create newpath as hardlink or copy of oldpath.
Definition PathInfo.cc:888
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:705
unsigned int MediaAccessId
Media manager access Id type.
Definition MediaSource.h:30
zypp::RW_pointer< MediaVerifierBase > MediaVerifierRef
A shared reference to the MediaVerifier implementation.
Url details namespace.
Definition UrlBase.cc:58
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition ManagedFile.h:27
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:423
static bool connected()
Definition Callback.h:251
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:463
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:471
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101
#define IMPL_PTR_TYPE(NAME)
#define ZYPP_IMPL_PRIVATE_CONSTR(Class)
Definition zyppglobal.h:222