1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/repo/PackageProvider.cc
15 #include "zypp/repo/PackageDelta.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/UserRequestException.h"
19 #include "zypp/base/NonCopyable.h"
20 #include "zypp/repo/PackageProvider.h"
21 #include "zypp/repo/Applydeltarpm.h"
22 #include "zypp/repo/PackageDelta.h"
24 #include "zypp/TmpPath.h"
25 #include "zypp/ZConfig.h"
26 #include "zypp/RepoInfo.h"
27 #include "zypp/RepoManager.h"
28 #include "zypp/SrcPackage.h"
30 #include "zypp/ZYppFactory.h"
31 #include "zypp/Target.h"
32 #include "zypp/target/rpm/RpmDb.h"
33 #include "zypp/FileChecker.h"
37 ///////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////
43 ///////////////////////////////////////////////////////////////////
44 /// \class RpmSigCheckException
45 /// \brief Exception thrown by \ref PackageProviderImpl::rpmSigFileChecker
46 ///////////////////////////////////////////////////////////////////
47 class RpmSigCheckException : public FileCheckException
50 RpmSigCheckException( repo::DownloadResolvableReport::Action action_r, std::string msg_r = "RpmSigCheckException" )
51 : FileCheckException( std::move(msg_r) )
52 , _action( std::move(action_r) )
55 /** Users final decision how to proceed */
56 const repo::DownloadResolvableReport::Action & action() const
60 repo::DownloadResolvableReport::Action _action;
64 ///////////////////////////////////////////////////////////////////
65 // class PackageProviderPolicy
66 ///////////////////////////////////////////////////////////////////
68 bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
70 const Arch & arch_r ) const
72 if ( _queryInstalledCB )
73 return _queryInstalledCB( name_r, ed_r, arch_r );
77 ///////////////////////////////////////////////////////////////////
78 /// \class PackageProvider::Impl
79 /// \brief PackageProvider implementation interface.
80 ///////////////////////////////////////////////////////////////////
81 struct PackageProvider::Impl : private base::NonCopyable
86 /** Provide the package.
90 virtual ManagedFile providePackage() const = 0;
92 /** Provide the package if it is cached. */
93 virtual ManagedFile providePackageFromCache() const = 0;
95 /** Whether the package is cached. */
96 virtual bool isCached() const = 0;
99 ///////////////////////////////////////////////////////////////////
100 /// \class PackageProviderImpl<TPackage>
101 /// \brief PackageProvider implementation for \c Package and \c SrcPackage
102 ///////////////////////////////////////////////////////////////////
103 template <class TPackage>
104 class PackageProviderImpl : public PackageProvider::Impl
106 typedef typename TPackage::constPtr TPackagePtr; // Package or SrcPackage
107 typedef callback::UserData UserData;
109 /** Ctor taking the Package to provide. */
110 PackageProviderImpl( RepoMediaAccess & access_r, const TPackagePtr & package_r,
111 const PackageProviderPolicy & policy_r )
112 : _policy( policy_r )
113 , _package( package_r )
114 , _access( access_r )
118 virtual ~PackageProviderImpl() {}
121 /** Provide the package.
122 * The basic workflow.
125 virtual ManagedFile providePackage() const;
127 /** Provide the package if it is cached. */
128 virtual ManagedFile providePackageFromCache() const
130 ManagedFile ret( doProvidePackageFromCache() );
131 if ( ! ( ret->empty() || _package->repoInfo().keepPackages() ) )
132 ret.setDispose( filesystem::unlink );
136 /** Whether the package is cached. */
137 virtual bool isCached() const
138 { return ! doProvidePackageFromCache()->empty(); }
141 typedef PackageProviderImpl<TPackage> Base;
142 typedef callback::SendReport<repo::DownloadResolvableReport> Report;
144 /** Lookup the final rpm in cache.
146 * A cache hit will return a non empty ManagedFile and an empty one on cache miss.
148 * \note File disposal depending on the repos keepPackages setting
149 * are not set here, but in \ref providePackage or \ref providePackageFromCache.
151 ManagedFile doProvidePackageFromCache() const
152 { return ManagedFile( _package->cachedLocation() ); }
154 /** Actually provide the final rpm.
155 * Report start/problem/finish and retry loop are hadled by \ref providePackage.
156 * Here you trigger just progress and delta/plugin callbacks as needed.
158 * Proxy method for progressPackageDownload is provided here.
160 * ProvideFilePolicy policy;
161 * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
162 * return _access.provideFile( _package->repoInfo(), loc, policy );
165 * \note The provided default implementation retrieves the packages default
168 virtual ManagedFile doProvidePackage() const
171 OnMediaLocation loc = _package->location();
173 ProvideFilePolicy policy;
174 policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
175 policy.fileChecker( bind( &Base::rpmSigFileChecker, this, _1 ) );
176 return _access.provideFile( _package->repoInfo(), loc, policy );
180 /** Access to the DownloadResolvableReport */
181 Report & report() const
184 /** Redirect ProvideFilePolicy package download progress to this. */
185 bool progressPackageDownload( int value ) const
186 { return report()->progress( value, _package ); }
189 /** \name Validate a rpm packages signature.
191 * This is the \ref FileChecker passed down to the \ref Fetcher to validate
192 * a provided rpm package. This builtin checker includes the workflow
193 * communicating with the user in case of a problem with the package
196 * \throws RpmSigCheckException if the package is not accepted, propagating
197 * the users decision how to proceed (\ref DownloadResolvableReport::Action).
199 * \note This check is also needed, if the the rpm is built locally by using
200 * delta rpms! \ref \see RpmPackageProvider
203 void rpmSigFileChecker( const Pathname & file_r ) const
205 const RepoInfo & info = _package->repoInfo();
206 if ( info.pkgGpgCheck() )
208 UserData userData( "pkgGpgCheck" );
209 ResObject::constPtr roptr( _package ); // gcc6 needs it more explcit. Has problem deducing
210 userData.set( "ResObject", roptr ); // a type for '_package->asKind<ResObject>()'...
211 /*legacy:*/userData.set( "Package", roptr->asKind<Package>() );
212 userData.set( "Localpath", file_r );
213 RpmDb::CheckPackageResult res = packageSigCheck( file_r, info.pkgGpgCheckIsMandatory(), userData );
215 // publish the checkresult, even if it is OK. Apps may want to report something...
216 report()->pkgGpgCheck( userData );
218 if ( res != RpmDb::CHK_OK )
220 if ( userData.hasvalue( "Action" ) ) // pkgGpgCheck report provided an user error action
222 resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
224 else if ( userData.haskey( "Action" ) ) // pkgGpgCheck requests the default problem report (wo. details)
226 defaultReportSignatureError( res );
228 else // no advice from user => usedefaults
232 case RpmDb::CHK_OK: // Signature is OK
235 case RpmDb::CHK_NOKEY: // Public key is unavailable
236 case RpmDb::CHK_NOTFOUND: // Signature is unknown type
237 case RpmDb::CHK_FAIL: // Signature does not verify
238 case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
239 case RpmDb::CHK_ERROR: // File does not exist or can't be opened
240 case RpmDb::CHK_NOSIG: // File is unsigned
242 // report problem (w. details), throw if to abort, else retry/ignore
243 defaultReportSignatureError( res, str::Str() << userData.get<RpmDb::CheckPackageDetail>( "CheckPackageDetail" ) );
251 typedef target::rpm::RpmDb RpmDb;
253 /** Actual rpm package signature check. */
254 RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, bool isMandatory_r, UserData & userData ) const
257 _target = getZYpp()->getTarget();
259 RpmDb::CheckPackageResult ret = RpmDb::CHK_ERROR;
260 RpmDb::CheckPackageDetail detail;
263 ret = _target->rpmDb().checkPackageSignature( path_r, detail );
264 if ( ret == RpmDb::CHK_NOSIG && !isMandatory_r )
266 WAR << "Relax CHK_NOSIG: Config says unsigned packages are OK" << endl;
271 detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
273 userData.set( "CheckPackageResult", ret );
274 userData.set( "CheckPackageDetail", std::move(detail) );
278 /** React on signature verification error user action.
279 * \note: IGNORE == accept insecure file (no SkipRequestException!)
281 void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
285 case repo::DownloadResolvableReport::IGNORE:
286 WAR << _package->asUserString() << ": " << "User requested to accept insecure file" << endl;
289 case repo::DownloadResolvableReport::RETRY:
290 case repo::DownloadResolvableReport::ABORT:
291 ZYPP_THROW(RpmSigCheckException(action_r,"Signature verification failed"));
296 /** Default signature verification error handling. */
297 void defaultReportSignatureError( RpmDb::CheckPackageResult ret, const std::string & detail_r = std::string() ) const
300 msg << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret;
301 if ( ! detail_r.empty() )
302 msg << "\n" << detail_r;
303 resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg.str() ) );
308 PackageProviderPolicy _policy;
309 TPackagePtr _package;
310 RepoMediaAccess & _access;
313 typedef shared_ptr<void> ScopedGuard;
315 ScopedGuard newReport() const
317 _report.reset( new Report );
318 // Use a custom deleter calling _report.reset() when guard goes out of
319 // scope (cast required as reset is overloaded). We want report to end
320 // when leaving providePackage and not wait for *this going out of scope.
321 return shared_ptr<void>( static_cast<void*>(0),
322 bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
327 mutable shared_ptr<Report> _report;
328 mutable Target_Ptr _target;
330 ///////////////////////////////////////////////////////////////////
332 template <class TPackage>
333 ManagedFile PackageProviderImpl<TPackage>::providePackage() const
335 ScopedGuard guardReport( newReport() );
337 // check for cache hit:
338 ManagedFile ret( providePackageFromCache() );
339 if ( ! ret->empty() )
341 MIL << "provided Package from cache " << _package << " at " << ret << endl;
342 report()->infoInCache( _package, ret );
343 return ret; // <-- cache hit
346 // HERE: cache misss, check toplevel cache or do download:
347 RepoInfo info = _package->repoInfo();
349 // Check toplevel cache
351 RepoManagerOptions topCache;
352 if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath ) // not using toplevel cache
354 const OnMediaLocation & loc( _package->location() );
355 if ( ! loc.checksum().empty() ) // no cache hit without checksum
357 PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
358 if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
360 report()->start( _package, pi.path().asFileUrl() );
361 const Pathname & dest( info.packagesPath() / loc.filename() );
362 if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
364 ret = ManagedFile( dest );
365 if ( ! info.keepPackages() )
366 ret.setDispose( filesystem::unlink );
368 MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
369 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
370 return ret; // <-- toplevel cache hit
377 // FIXME we only support the first url for now.
378 if ( info.baseUrlsEmpty() )
379 ZYPP_THROW(Exception("No url in repository."));
381 MIL << "provide Package " << _package << endl;
382 Url url = * info.baseUrlsBegin();
386 if ( ! ret->empty() )
388 ret.setDispose( filesystem::unlink );
391 report()->start( _package, url );
394 ret = doProvidePackage();
396 catch ( const UserRequestException & excpt )
398 ERR << "Failed to provide Package " << _package << endl;
400 ZYPP_RETHROW( excpt );
402 catch ( const RpmSigCheckException & excpt )
404 ERR << "Failed to provide Package " << _package << endl;
407 // Signature verification error was already reported by the
408 // rpmSigFileChecker. Just handle the users action decision:
409 switch ( excpt.action() )
411 case repo::DownloadResolvableReport::RETRY:
414 case repo::DownloadResolvableReport::IGNORE:
415 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
418 case repo::DownloadResolvableReport::ABORT:
419 ZYPP_THROW(AbortRequestException("User requested to abort"));
424 catch ( const FileCheckException & excpt )
426 ERR << "Failed to provide Package " << _package << endl;
429 const std::string & package_str = _package->asUserString();
430 // TranslatorExplanation %s = package being checked for integrity
431 switch ( report()->problem( _package, repo::DownloadResolvableReport::INVALID, str::form(_("Package %s seems to be corrupted during transfer. Do you want to retry retrieval?"), package_str.c_str() ) ) )
433 case repo::DownloadResolvableReport::RETRY:
436 case repo::DownloadResolvableReport::IGNORE:
437 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
440 case repo::DownloadResolvableReport::ABORT:
441 ZYPP_THROW(AbortRequestException("User requested to abort"));
446 catch ( const Exception & excpt )
448 ERR << "Failed to provide Package " << _package << endl;
451 // Aything else gets reported
452 const std::string & package_str = _package->asUserString();
454 // TranslatorExplanation %s = name of the package being processed.
455 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
456 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
458 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
460 case repo::DownloadResolvableReport::RETRY:
463 case repo::DownloadResolvableReport::IGNORE:
464 ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
467 case repo::DownloadResolvableReport::ABORT:
468 ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
475 // bsc#1045735: Be sure no invalid files stay in the cache!
476 if ( ! ret->empty() )
477 ret.setDispose( filesystem::unlink );
481 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
482 MIL << "provided Package " << _package << " at " << ret << endl;
487 ///////////////////////////////////////////////////////////////////
488 /// \class RpmPackageProvider
489 /// \brief RPM PackageProvider implementation (with deltarpm processing).
490 ///////////////////////////////////////////////////////////////////
491 class RpmPackageProvider : public PackageProviderImpl<Package>
494 RpmPackageProvider( RepoMediaAccess & access_r,
495 const Package::constPtr & package_r,
496 const DeltaCandidates & deltas_r,
497 const PackageProviderPolicy & policy_r )
498 : PackageProviderImpl<Package>( access_r, package_r, policy_r )
499 , _deltas( deltas_r )
503 virtual ManagedFile doProvidePackage() const;
506 typedef packagedelta::DeltaRpm DeltaRpm;
508 ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
510 bool progressDeltaDownload( int value ) const
511 { return report()->progressDeltaDownload( value ); }
513 void progressDeltaApply( int value ) const
514 { return report()->progressDeltaApply( value ); }
516 bool queryInstalled( const Edition & ed_r = Edition() ) const
517 { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
520 DeltaCandidates _deltas;
522 ///////////////////////////////////////////////////////////////////
524 ManagedFile RpmPackageProvider::doProvidePackage() const
526 // check whether to process patch/delta rpms
527 // FIXME we only check the first url for now.
528 if ( ZConfig::instance().download_use_deltarpm()
529 && ( _package->repoInfo().url().schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
531 std::list<DeltaRpm> deltaRpms;
532 _deltas.deltaRpms( _package ).swap( deltaRpms );
534 if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
536 for_( it, deltaRpms.begin(), deltaRpms.end())
538 DBG << "tryDelta " << *it << endl;
539 ManagedFile ret( tryDelta( *it ) );
540 if ( ! ret->empty() )
546 // no patch/delta -> provide full package
547 return Base::doProvidePackage();
550 ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
552 if ( delta_r.baseversion().edition() != Edition::noedition
553 && ! queryInstalled( delta_r.baseversion().edition() ) )
554 return ManagedFile();
556 if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
557 return ManagedFile();
559 report()->startDeltaDownload( delta_r.location().filename(),
560 delta_r.location().downloadSize() );
564 ProvideFilePolicy policy;
565 policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
566 delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
568 catch ( const Exception & excpt )
570 report()->problemDeltaDownload( excpt.asUserHistory() );
571 return ManagedFile();
573 report()->finishDeltaDownload();
575 report()->startDeltaApply( delta );
576 if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
578 report()->problemDeltaApply( _("applydeltarpm check failed.") );
579 return ManagedFile();
583 Pathname cachedest( _package->repoInfo().packagesPath() / _package->location().filename() );
584 Pathname builddest( cachedest.extend( ".drpm" ) );
586 if ( ! applydeltarpm::provide( delta, builddest,
587 bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
589 report()->problemDeltaApply( _("applydeltarpm failed.") );
590 return ManagedFile();
592 ManagedFile builddestCleanup( builddest, filesystem::unlink );
593 report()->finishDeltaApply();
595 // Check and move it into the cache
596 // Here the rpm itself is ready. If the packages sigcheck fails, it
597 // makes no sense to return a ManagedFile() and fallback to download the
598 // full rpm. It won't be different. So let the exceptions escape...
599 rpmSigFileChecker( builddest );
600 if ( filesystem::hardlinkCopy( builddest, cachedest ) != 0 )
601 ZYPP_THROW( Exception( str::Str() << "Can't hardlink/copy " << builddest << " to " << cachedest ) );
603 return ManagedFile( cachedest, filesystem::unlink );
606 ///////////////////////////////////////////////////////////////////
607 // class PackageProvider
608 ///////////////////////////////////////////////////////////////////
611 inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const PoolItem & pi_r,
612 const DeltaCandidates & deltas_r,
613 const PackageProviderPolicy & policy_r )
615 if ( pi_r.satSolvable().isKind<Package>() )
616 return new RpmPackageProvider( access_r, pi_r->asKind<Package>(), deltas_r, policy_r );
617 else if ( pi_r.satSolvable().isKind<SrcPackage>() )
618 return new PackageProviderImpl<SrcPackage>( access_r, pi_r->asKind<SrcPackage>(), policy_r );
620 ZYPP_THROW( Exception( str::Str() << "Don't know how to cache non-package " << pi_r.satSolvable().asUserString() ) );
623 inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const PoolItem & pi_r,
624 const PackageProviderPolicy & policy_r )
626 if ( pi_r.satSolvable().isKind<Package>() )
627 return new PackageProviderImpl<Package>( access_r, pi_r->asKind<Package>(), policy_r );
628 else if ( pi_r.satSolvable().isKind<SrcPackage>() )
629 return new PackageProviderImpl<SrcPackage>( access_r, pi_r->asKind<SrcPackage>(), policy_r );
631 ZYPP_THROW( Exception( str::Str() << "Don't know how to cache non-package " << pi_r.satSolvable().asUserString() ) );
634 inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const Package::constPtr & package_r,
635 const DeltaCandidates & deltas_r,
636 const PackageProviderPolicy & policy_r )
637 { return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r ); }
639 } // namespace factory
640 ///////////////////////////////////////////////////////////////////
642 PackageProvider::PackageProvider( RepoMediaAccess & access_r, const PoolItem & pi_r,
643 const DeltaCandidates & deltas_r, const PackageProviderPolicy & policy_r )
645 : _pimpl( factory::make( access_r, pi_r, deltas_r, policy_r ) )
648 PackageProvider::PackageProvider( RepoMediaAccess & access_r, const PoolItem & pi_r,
649 const PackageProviderPolicy & policy_r )
650 : _pimpl( factory::make( access_r, pi_r, policy_r ) )
654 PackageProvider::PackageProvider( RepoMediaAccess & access_r,
655 const Package::constPtr & package_r,
656 const DeltaCandidates & deltas_r,
657 const PackageProviderPolicy & policy_r )
658 : _pimpl( factory::make( access_r, package_r, deltas_r, policy_r ) )
661 PackageProvider::~PackageProvider()
664 ManagedFile PackageProvider::providePackage() const
665 { return _pimpl->providePackage(); }
667 ManagedFile PackageProvider::providePackageFromCache() const
668 { return _pimpl->providePackageFromCache(); }
670 bool PackageProvider::isCached() const
671 { return _pimpl->isCached(); }
674 ///////////////////////////////////////////////////////////////////
676 ///////////////////////////////////////////////////////////////////