libzypp 17.35.11
mediafacade.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
9#include "mediafacade.h"
11#include <zypp/ZYppCallbacks.h>
12#include <zypp-core/TriBool.h>
14#include <utility>
15#include <zypp-media/ng/ProvideSpec>
16#include <zypp-media/mount.h>
18
19namespace zyppng {
20
22 {
23
24 public:
25
27
29 const ProvideMediaSpec &spec() const;
30 const zypp::Url &url() const;
31 const std::optional<zypp::Pathname> &rootPath() const;
32 MediaSyncFacadeRef parent() const;
33
37 bool isSameMedium ( const std::vector<zypp::Url> &urls, const ProvideMediaSpec &spec );
38
39 // ReferenceCounted interface
40 protected:
41 void unref_to(unsigned int) const override;
42
43 private:
47 MediaSyncFacadeRef _parent;
48 std::optional<zypp::Pathname> _localPath;
49 };
50
52
54 : _id( mediaId )
55 , _attachedUrl(std::move( baseUrl ))
56 , _spec(std::move( mediaSpec ))
57 , _parent(std::move( parentRef ))
58 , _localPath( locPath )
59 {}
60
65
67 {
68 return _spec;
69 }
70
72 {
73 return _attachedUrl;
74 }
75
76 const std::optional<zypp::Pathname> &AttachedSyncMediaInfo::rootPath() const
77 {
78 return _localPath;
79 }
80
81 MediaSyncFacadeRef AttachedSyncMediaInfo::parent() const
82 {
83 return _parent;
84 }
85
86 bool AttachedSyncMediaInfo::isSameMedium(const std::vector<zypp::Url> &urls, const ProvideMediaSpec &spec) {
87
88 const auto check = _spec.isSameMedium(spec);
89 if ( !zypp::indeterminate (check) )
90 return (bool)check;
91
92 // let the URL rule
93 return ( std::find( urls.begin(), urls.end(), _attachedUrl ) != urls.end() );
94 }
95
96 void AttachedSyncMediaInfo::unref_to( unsigned int count ) const
97 {
98 // once count reaches 1 only the MediaSyncFacade holds a reference,
99 // time to release the medium
100 if ( count == 1 ) {
101 _parent->releaseMedium ( this );
102 // !!!! careful from here on out 'this' is most likely invalid !!!!
103 return;
104 }
105 }
106
107
109
110 SyncMediaHandle::SyncMediaHandle(AttachedSyncMediaInfo_Ptr dataPtr) : _data( std::move(dataPtr) )
111 { }
112
113 MediaSyncFacadeRef SyncMediaHandle::parent() const
114 {
115 return _data->parent();
116 }
117
119 {
120 return _data.get() != nullptr;
121 }
122
124 {
126 if ( !_data )
127 return invalidHandle;
128 return _data->url();
129 }
130
131 const std::optional<zypp::Pathname> &SyncMediaHandle::localPath() const
132 {
133 static std::optional<zypp::Pathname> invalidPath;
134 if ( !_data )
135 return invalidPath;
136 return _data->rootPath();
137 }
138
140 {
141 return *_data;
142 }
143
145 : _res( std::move(file) )
146 , _provideHandle( std::move (hdl) )
147 { }
148
150 return _res;
151 }
152
154
155 std::vector<zypp::Url> MediaSyncFacade::sanitizeUrls(const std::vector<zypp::Url> &urls) const
156 {
157 std::vector<zypp::Url> usableMirrs;
158 std::optional<zypp::media::MediaHandlerFactory::MediaHandlerType> handlerType;
159
160 for ( auto mirrIt = urls.begin() ; mirrIt != urls.end(); mirrIt++ ) {
162 if ( !s ) {
163 WAR << "URL: " << *mirrIt << " is not supported, ignoring!" << std::endl;
164 continue;
165 }
166 if ( !handlerType ) {
167 handlerType = *s;
168 usableMirrs.push_back ( *mirrIt );
169 } else {
170 if ( handlerType == *s) {
171 usableMirrs.push_back( *mirrIt );
172 } else {
173 WAR << "URL: " << *mirrIt << " has different handler type than the primary URL: "<< usableMirrs.front() <<", ignoring!" << std::endl;
174 }
175 }
176 }
177
178 if ( !handlerType || usableMirrs.empty() ) {
179 return {};
180 }
181
182 return usableMirrs;
183 }
184
186 {
187
188 bool isVolatile = url.schemeIsVolatile();
189
190 auto effectiveUrl = url;
191
192 std::optional<zypp::media::MediaAccessId> attachId;
194
195 // nothing attached, make a new one
197 do {
198 try {
199 if ( !attachId ) {
200
201 if ( request.medianr() > 1 )
203
204 attachId = mgr.open( effectiveUrl );
205 if ( !request.mediaFile().empty() ) {
206 mgr.addVerifier( *attachId, zypp::media::MediaVerifierRef( new zypp::repo::SUSEMediaVerifier( request.mediaFile(), request.medianr() ) ) );
207 }
208 }
209
210 // attach the medium
211 mgr.attach( *attachId );
212
213 auto locPath = mgr.localPath( *attachId, "/" );
214 auto attachInfo = AttachedSyncMediaInfo_Ptr( new AttachedSyncMediaInfo( shared_this<MediaSyncFacade>(), *attachId, url, request, locPath ) );
215 _attachedMedia.push_back( attachInfo );
217
218 } catch ( const zypp::media::MediaException &excp ) {
219
221
222 // if no one is listening, just return the error as is
225 }
226
227 // default action is to cancel
229
230 do {
231 // here: Manager tried all not attached drives and could not find the desired medium
232 // we need to send the media change report
233
234 // set up the reason
236 if( typeid(excp) == typeid( zypp::media::MediaNotDesiredException) ) {
238 }
239
240 unsigned int devindex = 0;
241
242 std::vector<std::string> devices;
243 mgr.getDetectedDevices(*attachId, devices, devindex);
244
245 std::optional<std::string> currentlyUsed;
246 if ( devices.size() ) currentlyUsed = devices[devindex];
247
248 if ( isVolatile ) {
249 // filter devices that are mounted, aka used, we can not eject them
251 std::remove_if( devices.begin (), devices.end(), [&](const std::string &dev) {
252 zypp::PathInfo devInfo(dev);
253 return std::any_of( mountedDevs.begin (), mountedDevs.end(), [&devInfo]( const zypp::media::MountEntry &e ) {
254 zypp::PathInfo pi( e.src );
255 return ( pi.isBlk() && pi.devMajor() == devInfo.devMajor() && pi.devMinor() == devInfo.devMinor() );
256 });
257 });
258
259 if ( !devices.size () ) {
260 // Jammed, no currently free device
261 MIL << "No free device available, return jammed and try again later ( hopefully) " << std::endl;
262 if ( attachId ) mgr.close ( *attachId );
264 }
265
266 // update index to currenty used dev
267 bool foundCurrent = false;
268 if ( currentlyUsed ) {
269 for ( unsigned int i = 0; i < devices.size(); i++ ) {
270 if ( devices[i] == *currentlyUsed ) {
271 foundCurrent = true;
272 devindex = i;
273 break;
274 }
275 }
276 }
277
278 if ( !foundCurrent ){
279 devindex = 0; // seems 0 is what is set in the handlers too if there is no current
280 }
281 }
282
283 user = report->requestMedia (
285 request.medianr(),
286 request.label(),
287 reason,
288 excp.asUserHistory(),
289 devices,
291 );
292
293 MIL << "ProvideFile exception caught, callback answer: " << user << std::endl;
294
295 switch ( user ) {
297 DBG << "Aborting" << std::endl;
298 if ( attachId ) mgr.close ( *attachId );
299 zypp::AbortRequestException aexcp("Aborting requested by user");
300 aexcp.remember(excp);
302 }
304 DBG << "Skipping" << std::endl;
305 if ( attachId ) mgr.close ( *attachId );
306 zypp::SkipRequestException nexcp("User-requested skipping of a file");
307 nexcp.remember(excp);
309 }
311 DBG << "Eject: try to release" << std::endl;
312 try
313 {
314 // MediaSetAccess does a releaseAll, but we can not release other devices that are in use
315 // media_mgr.releaseAll();
316 mgr.release (*attachId, devindex < devices.size() ? devices[devindex] : "");
317 }
318 catch ( const zypp::Exception & e)
319 {
320 ZYPP_CAUGHT(e);
321 }
322 break;
323 }
326 // retry
327 DBG << "Going to try again" << std::endl;
328
329 // invalidate current media access id
330 if ( attachId ) {
331 mgr.close(*attachId);
332 attachId.reset();
333 }
334
335 // not attaching, media set will do that for us
336 // this could generate uncaught exception (#158620)
337 break;
338 }
339 default: {
340 DBG << "Don't know, let's ABORT" << std::endl;
341 if ( attachId ) mgr.close ( *attachId );
343 }
344 }
345 } while( user == zypp::media::MediaChangeReport::EJECT );
346 } catch ( const zypp::Exception &e ) {
347 ZYPP_CAUGHT(e);
348 if ( attachId ) mgr.close ( *attachId );
350 } catch (...) {
351 // didn't work -> clean up
352 if ( attachId ) mgr.close ( *attachId );
354 }
355 } while ( true );
356 }
357
359 {
360 // rewrite and sanitize the urls if required
361 std::vector<zypp::Url> useableUrls = sanitizeUrls(urls);
362
363 if ( useableUrls.empty () )
365
366 // first try and find a already attached medium
367 auto i = std::find_if( _attachedMedia.begin (), _attachedMedia.end(), [&]( const AttachedSyncMediaInfo_Ptr &medium ) {
368 return medium->isSameMedium( useableUrls, request );
369 });
370
371 if ( i != _attachedMedia.end() ) {
373 }
374
375
376 std::exception_ptr lastError;
377 std::exception_ptr jammedError;
378
379
380 // from here call attachMedia with just one URL
381 // catch errors, if one of the URLS returns JAMMED, remember it
382 // continue to try other URLs, if they all fail and JAMMED was remembered
383 // return it, otherwise the last error
384
385 for ( const auto &url : useableUrls ) {
386 try {
389 ZYPP_CAUGHT(e);
390 if ( !jammedError ) jammedError = std::current_exception ();
391 } catch ( const zypp::Exception &e ) {
392 ZYPP_CAUGHT(e);
393 lastError = std::current_exception();
394 } catch (...) {
395 // didn't work -> clean up
396 lastError = std::current_exception();
397 }
398 }
399
400 if ( jammedError ) {
401 // if we encountered a jammed error return it, there might be still a chance
402 // that invoking the provide again after other pipelines have finished might succeed
404 } else if ( lastError ) {
405 // if we have a error stored, return that one
407 }
408
410 }
411
413 {
414 // this should never happen because every handle has a reference to the media manager, but still add a debug output
415 // so we know in case we have weird behavior.
416 if ( _attachedMedia.size () ) {
417 WAR << "Releasing zyppng::MediaSyncFacade with still valid MediaHandles, this is a bug!" << std::endl;
418 }
419 }
420
428
430 {
431 return prepareMedia( std::vector<zypp::Url>{url}, request );
432 }
433
435 {
436 using namespace zyppng::operators;
437 if ( lazyHandle.attached() )
438 return expected<MediaHandle>::success( *lazyHandle.handle() );
439
440 MIL << "Attaching lazy medium with label: [" << lazyHandle.spec().label() << "]" << std::endl;
441
442 return attachMedia( lazyHandle.urls(), lazyHandle.spec () )
443 | and_then([lazyHandle]( MediaHandle handle ) {
444 lazyHandle._sharedData->_mediaHandle = handle;
445 return expected<MediaHandle>::success( std::move(handle) );
446 });
447 }
448
450 {
451 using namespace zyppng::operators;
452
453 if ( !urls.size() )
454 return expected<MediaSyncFacade::Res>::error( ZYPP_EXCPT_PTR ( zypp::media::MediaException("Can not provide a file without a URL.") ));
455
456 std::optional<expected<MediaSyncFacade::Res>> lastErr;
457 for ( const zypp::Url& file_url : urls ) {
458
459 zypp::Url url(file_url);
461 url.setPathName ("/");
462
464 | and_then( [&, this]( const MediaSyncFacade::MediaHandle& handle ) {
465 return provide( handle, fileName, request.asOnMediaLocation(fileName, 1));
466 });
467
468 if ( res )
469 return res;
470
471 lastErr = res;
472 }
473
474 // we always should have a last error, except if the URLs are empty
475 if ( lastErr )
476 return *lastErr;
477
478 // we should not get here, but if we do simply use the first entry to make a not found error
479 zypp::Url url( urls.front() );
481 url.setPathName ("/");
483
484 }
485
487 {
488 return provide( std::vector<zypp::Url>{url}, request );
489 }
490
492 {
494 const auto &handleInfo = attachHandle.info();
495
496 try {
497 if ( request.checkExistsOnly() ) {
498 if ( !mgr.doesFileExist ( handleInfo.mediaId (), fileName ) ) {
500 }
501
502 // we return a result pointing to a non existant file, since the code just asked us to check if the file exists
504
505 } else {
506 mgr.provideFile( handleInfo.mediaId (), request.asOnMediaLocation( fileName, handleInfo.spec().medianr()) );
507
508 zypp::ManagedFile locFile( mgr.localPath( handleInfo.mediaId(), fileName ) );
509
510 // do not clean up files for now, they are cleaned up anyways on detach
511#if 0
512 // if the file is downloaded we want to clean it up again
513 if ( handleInfo.url().schemeIsDownloading() )
515#endif
516
518 }
519 } catch ( const zypp::Exception &e ) {
520 ZYPP_CAUGHT(e);
522 } catch (...) {
524 }
525 }
526
528 {
529 using namespace zyppng::operators;
532 auto me = weakMe.lock();
533 if ( !me )
534 return expected<Res>::error(ZYPP_EXCPT_PTR(zypp::Exception("Provide was released during a operation")));
535 return me->provide( handle, fName, req);
536 });
537 }
538
540 {
541 try {
543 } catch(...) {
544 return expected<zypp::CheckSum>::error ( std::current_exception () );
545 }
546 }
547
549 {
550 try {
551 // do what Provide would do and make a URL
552 zypp::Url url("copy:///");
553 url.setPathName( source );
554
555 auto sourcePi = zypp::PathInfo(source);
556 if ( !sourcePi.isExist() ) {
558 }
559 if ( !sourcePi.isFile () )
561
562 auto res = zypp::filesystem::hardlinkCopy( source, target.asString() );
563 if ( res == 0 ) {
565 } else {
566 return expected<zypp::ManagedFile>::error ( ZYPP_EXCPT_PTR( zypp::media::MediaException( zypp::str::Str() << "Failed to create file " << target << " errno: " << res ) ) );
567 }
568 } catch(...) {
569 return expected<zypp::ManagedFile>::error ( std::current_exception () );
570 }
571 }
572
574 {
575 // not much to do here, since this will block until the file has been copied we do not need to remember the ProvideRes
576 return copyFile( source.file(), target );
577 }
578
580 {
581 if ( !ptr ) return;
582
583 auto i = std::find_if(_attachedMedia.begin (), _attachedMedia.end(), [&]( const auto &p ) { return p.get() == ptr; } );
584
585 try {
587 mgr.close ( ptr->mediaId() );
588 } catch ( const zypp::Exception & e ) {
589 ZYPP_CAUGHT(e);
590 }
591
592 if ( i != _attachedMedia.end() ) {
593 _attachedMedia.erase(i);
594 } else {
595 ERR << "Releasing unknown medium " << ptr->mediaId () << " should not happen";
596 }
597 }
598
599
600}
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
void reset()
Reset to default Ctor values.
void setDispose(const Dispose &dispose_r)
Set a new dispose function.
Base class for Exception.
Definition Exception.h:147
static Url rewriteUrl(const Url &url_r, const media::MediaNr medianr)
Replaces media number in specified url with given medianr.
Url manipulation class.
Definition Url.h:92
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition Url.cc:608
void setPathName(const std::string &path, EEncoding eflag=zypp::url::E_DECODED)
Set the path name.
Definition Url.cc:768
static bool schemeIsVolatile(const std::string &scheme_r)
cd dvd
Definition Url.cc:473
Base class for reference counted objects.
Wrapper class for stat/lstat.
Definition PathInfo.h:222
const std::string & asString() const
String representation.
Definition Pathname.h:93
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,...
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.
zypp::media::MediaAccessId _id
MediaSyncFacadeRef _parent
std::optional< zypp::Pathname > _localPath
AttachedSyncMediaInfo(MediaSyncFacadeRef parentRef, zypp::media::MediaAccessId mediaId, zypp::Url baseUrl, ProvideMediaSpec mediaSpec, const zypp::Pathname &locPath)
const zypp::Url & url() const
MediaSyncFacadeRef parent() const
bool isSameMedium(const std::vector< zypp::Url > &urls, const ProvideMediaSpec &spec)
const std::optional< zypp::Pathname > & rootPath() const
zypp::media::MediaAccessId mediaId() const
void unref_to(unsigned int) const override
const ProvideMediaSpec & spec() const
Res(MediaHandle hdl, zypp::ManagedFile file)
const zypp::Pathname file() const
expected< Res > provide(const std::vector< zypp::Url > &urls, const ProvideFileSpec &request)
expected< MediaHandle > attachMedia(const std::vector< zypp::Url > &urls, const ProvideMediaSpec &request)
friend class AttachedSyncMediaInfo
Definition mediafacade.h:53
expected< zypp::CheckSum > checksumForFile(const zypp::Pathname &p, const std::string &algorithm)
expected< zypp::ManagedFile > copyFile(const zypp::Pathname &source, const zypp::Pathname &target)
std::vector< AttachedSyncMediaInfo_Ptr > _attachedMedia
expected< MediaHandle > attachMediaIfNeeded(LazyMediaHandle lazyHandle)
std::vector< zypp::Url > sanitizeUrls(const std::vector< zypp::Url > &urls) const
void releaseMedium(const AttachedSyncMediaInfo *ptr)
expected< LazyMediaHandle > prepareMedia(const std::vector< zypp::Url > &urls, const ProvideMediaSpec &request)
zypp::TriBool isSameMedium(const ProvideMediaSpec &other)
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
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:423
Wrapper for const correct access via Smart pointer types.
Definition PtrTypes.h:293
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:212
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:440
#define ZYPP_FWD_EXCPT(EXCPT)
Drops a logline and returns the given Exception as a std::exception_ptr.
Definition Exception.h:432
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:428
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:436
#define DBG
Definition Logger.h:97
#define MIL
Definition Logger.h:98
#define ERR
Definition Logger.h:100
#define WAR
Definition Logger.h:99
#define IMPL_PTR_TYPE(NAME)
#define ZYPP_IMPL_PRIVATE_CONSTR(Class)
Definition zyppglobal.h:222