libzypp 17.36.1
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
26 AttachedSyncMediaInfo( MediaSyncFacadeRef parentRef, zypp::media::MediaAccessId mediaId, zypp::Url baseUrl, ProvideMediaSpec mediaSpec, const zypp::Pathname &locPath );
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 {
125 static zypp::Url invalidHandle;
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++ ) {
161 const auto &s = zypp::media::MediaHandlerFactory::handlerType ( *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 )
202 effectiveUrl = zypp::MediaSetAccess::rewriteUrl( effectiveUrl, request.medianr() );
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 );
216 return expected<MediaSyncFacade::MediaHandle>::success( std::move(attachInfo) );
217
218 } catch ( const zypp::media::MediaException &excp ) {
219
220 ZYPP_CAUGHT(excp);
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
250 const auto &mountedDevs = zypp::media::Mount::getEntries();
251 devices.erase( 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 }), devices.end() );
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 (
284 effectiveUrl,
285 request.medianr(),
286 request.label(),
287 reason,
288 excp.asUserHistory(),
289 devices,
290 devindex
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
422 {
423 const auto &useableUrls = sanitizeUrls(urls);
424 if ( useableUrls.empty () )
426 return expected<LazyMediaHandle>::success( shared_this<MediaSyncFacade>(), std::move(useableUrls), request );
427 }
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
449 expected<MediaSyncFacade::Res> MediaSyncFacade::provide(const std::vector<zypp::Url> &urls, const ProvideFileSpec &request)
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);
460 zypp::Pathname fileName(url.getPathName());
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() );
480 zypp::Pathname fileName(url.getPathName());
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
503 return expected<MediaSyncFacade::Res>::success( attachHandle, zypp::ManagedFile( mgr.localPath( handleInfo.mediaId(), fileName ) ) );
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
517 return expected<MediaSyncFacade::Res>::success( attachHandle, locFile );
518 }
519 } catch ( const zypp::Exception &e ) {
520 ZYPP_CAUGHT(e);
522 } catch (...) {
524 }
525 }
526
528 {
529 using namespace zyppng::operators;
530 return attachMediaIfNeeded ( attachHandle )
531 | and_then([weakMe = weak_this<MediaSyncFacade>(), fName = fileName, req = request ]( MediaHandle handle ){
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 {
542 return expected<zypp::CheckSum>::success( zypp::CheckSum( algorithm, zypp::filesystem::checksum ( p, algorithm ) ) );
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}
void setDispose(const Dispose &dispose_r)
Set a new dispose function.
Base class for Exception.
Definition Exception.h:147
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition Exception.cc:127
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:93
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.
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
std::shared_ptr< T > shared_this() const
Definition base.h:113
std::weak_ptr< T > weak_this() const
Definition base.h:123
const std::vector< zypp::Url > & urls() const
const ProvideMediaSpec & spec() const
std::optional< MediaHandle > handle() 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)
::zyppng::LazyMediaHandle< MediaSyncFacade > LazyMediaHandle
Definition mediafacade.h:56
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 > 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::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: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: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