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"
29 #include "zypp/ZYppFactory.h"
30 #include "zypp/Target.h"
31 #include "zypp/target/rpm/RpmDb.h"
32 #include "zypp/FileChecker.h"
36 ///////////////////////////////////////////////////////////////////
39 ///////////////////////////////////////////////////////////////////
42 ///////////////////////////////////////////////////////////////////
43 // class PackageProviderPolicy
44 ///////////////////////////////////////////////////////////////////
46 bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
48 const Arch & arch_r ) const
50 if ( _queryInstalledCB )
51 return _queryInstalledCB( name_r, ed_r, arch_r );
56 ///////////////////////////////////////////////////////////////////
57 /// \class PackageProvider::Impl
58 /// \brief PackageProvider implementation.
59 ///////////////////////////////////////////////////////////////////
60 class PackageProvider::Impl : private base::NonCopyable
62 typedef callback::UserData UserData;
64 /** Ctor taking the Package to provide. */
65 Impl( RepoMediaAccess & access_r,
66 const Package::constPtr & package_r,
67 const DeltaCandidates & deltas_r,
68 const PackageProviderPolicy & policy_r )
70 , _package( package_r )
78 /** Factory method providing the appropriate implementation.
79 * Called by PackageProvider ctor. Returned pointer should be
80 * immediately wrapped into a smartpointer.
82 static Impl * factoryMake( RepoMediaAccess & access_r,
83 const Package::constPtr & package_r,
84 const DeltaCandidates & deltas_r,
85 const PackageProviderPolicy & policy_r );
88 /** Provide the package.
92 ManagedFile providePackage() const;
94 /** Provide the package if it is cached. */
95 ManagedFile providePackageFromCache() const
97 ManagedFile ret( doProvidePackageFromCache() );
98 if ( ! ( ret->empty() || _package->repoInfo().keepPackages() ) )
99 ret.setDispose( filesystem::unlink );
103 /** Whether the package is cached. */
104 bool isCached() const
105 { return ! doProvidePackageFromCache()->empty(); }
108 typedef PackageProvider::Impl Base;
109 typedef callback::SendReport<repo::DownloadResolvableReport> Report;
111 /** Lookup the final rpm in cache.
113 * A non empty ManagedFile will be returned to the caller.
115 * \note File disposal depending on the repos keepPackages setting
116 * are not set here, but in \ref providePackage or \ref providePackageFromCache.
118 * \note The provoided default implementation returns an empty ManagedFile
121 virtual ManagedFile doProvidePackageFromCache() const = 0;
123 /** Actually provide the final rpm.
124 * Report start/problem/finish and retry loop are hadled by \ref providePackage.
125 * Here you trigger just progress and delta/plugin callbacks as needed.
127 * Proxy methods for progressPackageDownload and failOnChecksum are provided here.
128 * Create similar proxies for other progress callbacks in derived classes and link
129 * it to ProvideFilePolicy for download:
131 * ProvideFilePolicy policy;
132 * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
133 * policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
134 * return _access.provideFile( _package->repoInfo(), loc, policy );
137 * \note The provoided default implementation retrieves the packages default
140 virtual ManagedFile doProvidePackage() const = 0;
143 /** Access to the DownloadResolvableReport */
144 Report & report() const
147 /** Redirect ProvideFilePolicy package download progress to this. */
148 bool progressPackageDownload( int value ) const
149 { return report()->progress( value, _package ); }
151 /** Redirect ProvideFilePolicy failOnChecksumError to this if needed. */
152 bool failOnChecksumError() const
154 std::string package_str = _package->name() + "-" + _package->edition().asString();
156 // TranslatorExplanation %s = package being checked for integrity
157 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() ) ) )
159 case repo::DownloadResolvableReport::RETRY:
162 case repo::DownloadResolvableReport::IGNORE:
163 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
165 case repo::DownloadResolvableReport::ABORT:
166 ZYPP_THROW(AbortRequestException("User requested to abort"));
171 return true; // anyway a failure
174 typedef target::rpm::RpmDb RpmDb;
176 RpmDb::checkPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
179 _target = getZYpp()->getTarget();
181 RpmDb::checkPackageResult ret = RpmDb::CHK_ERROR;
182 RpmDb::CheckPackageDetail detail;
184 ret = _target->rpmDb().checkPackage( path_r, detail );
186 detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
188 userData.set( "CheckPackageResult", ret );
189 userData.set( "CheckPackageDetail", std::move(detail) );
193 /** React on signature verrification error user action */
194 void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
196 // TranslatorExplanation %s = package being checked for integrity
199 case repo::DownloadResolvableReport::RETRY:
202 case repo::DownloadResolvableReport::IGNORE:
203 WAR << _package->asUserString() << ": " << "User requested skip of insecure file" << endl;
206 case repo::DownloadResolvableReport::ABORT:
207 ZYPP_THROW(AbortRequestException("User requested to abort"));
212 /** Default signature verrification error handling. */
213 void defaultReportSignatureError( RpmDb::checkPackageResult ret ) const
215 std::string msg( str::Str() << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret );
216 resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg ) );
220 PackageProviderPolicy _policy;
221 Package::constPtr _package;
222 DeltaCandidates _deltas;
223 RepoMediaAccess & _access;
226 typedef shared_ptr<void> ScopedGuard;
228 ScopedGuard newReport() const
230 _report.reset( new Report );
231 // Use a custom deleter calling _report.reset() when guard goes out of
232 // scope (cast required as reset is overloaded). We want report to end
233 // when leaving providePackage and not wait for *this going out of scope.
234 return shared_ptr<void>( static_cast<void*>(0),
235 bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
240 mutable shared_ptr<Report> _report;
241 mutable Target_Ptr _target;
243 ///////////////////////////////////////////////////////////////////
245 /** Default implementation (cache miss). */
246 ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
247 { return ManagedFile(); }
249 /** Default implementation (provide full package) */
250 ManagedFile PackageProvider::Impl::doProvidePackage() const
253 OnMediaLocation loc = _package->location();
255 ProvideFilePolicy policy;
256 policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
257 policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
258 return _access.provideFile( _package->repoInfo(), loc, policy );
261 ///////////////////////////////////////////////////////////////////
263 ManagedFile PackageProvider::Impl::providePackage() const
265 ScopedGuard guardReport( newReport() );
267 // check for cache hit:
268 ManagedFile ret( providePackageFromCache() );
269 if ( ! ret->empty() )
271 MIL << "provided Package from cache " << _package << " at " << ret << endl;
272 report()->infoInCache( _package, ret );
273 return ret; // <-- cache hit
276 // HERE: cache misss, check toplevel cache or do download:
277 RepoInfo info = _package->repoInfo();
279 // Check toplevel cache
281 RepoManagerOptions topCache;
282 if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath ) // not using toplevel cache
284 const OnMediaLocation & loc( _package->location() );
285 if ( ! loc.checksum().empty() ) // no cache hit without checksum
287 PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
288 if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
290 report()->start( _package, pi.path().asFileUrl() );
291 const Pathname & dest( info.packagesPath() / loc.filename() );
292 if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
294 ret = ManagedFile( dest );
295 if ( ! info.keepPackages() )
296 ret.setDispose( filesystem::unlink );
298 MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
299 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
300 return ret; // <-- toplevel cache hit
307 // FIXME we only support the first url for now.
308 if ( info.baseUrlsEmpty() )
309 ZYPP_THROW(Exception("No url in repository."));
311 MIL << "provide Package " << _package << endl;
312 Url url = * info.baseUrlsBegin();
315 if ( ! ret->empty() )
317 ret.setDispose( filesystem::unlink );
320 report()->start( _package, url );
321 try // ELIMINATE try/catch by providing a log-guard
323 ret = doProvidePackage();
325 if ( info.pkgGpgCheck() )
327 UserData userData( "pkgGpgCheck" );
328 userData.set( "Package", _package );
329 userData.set( "Localpath", ret.value() );
330 RpmDb::checkPackageResult res = packageSigCheck( ret, userData );
331 // publish the checkresult, even if it is OK. Apps may want to report something...
332 report()->pkgGpgCheck( userData );
334 if ( res != RpmDb::CHK_OK )
336 if ( userData.hasvalue( "Action" ) ) // pkgGpgCheck provided an user error action
338 resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
340 else // no advice from user => usedefaults
344 case RpmDb::CHK_OK: // Signature is OK
347 case RpmDb::CHK_NOKEY: // Public key is unavailable
348 case RpmDb::CHK_NOTFOUND: // Signature is unknown type
349 case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
350 // should fail in future versions.
353 case RpmDb::CHK_FAIL: // Signature does not verify
354 case RpmDb::CHK_ERROR: // File does not exist or can't be opened
355 // report problem, throw if to abort, else retry/ignore
356 defaultReportSignatureError( res );
363 catch ( const UserRequestException & excpt )
365 // UserRequestException e.g. from failOnChecksumError was already reported.
366 ERR << "Failed to provide Package " << _package << endl;
369 ZYPP_RETHROW( excpt );
372 catch ( const Exception & excpt )
374 ERR << "Failed to provide Package " << _package << endl;
377 // Aything else gets reported
378 std::string package_str = _package->name() + "-" + _package->edition().asString();
380 // TranslatorExplanation %s = name of the package being processed.
381 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
382 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
384 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
386 case repo::DownloadResolvableReport::RETRY:
389 case repo::DownloadResolvableReport::IGNORE:
390 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
392 case repo::DownloadResolvableReport::ABORT:
393 ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
396 ZYPP_RETHROW( excpt );
403 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
404 MIL << "provided Package " << _package << " at " << ret << endl;
409 ///////////////////////////////////////////////////////////////////
410 /// \class RpmPackageProvider
411 /// \brief RPM PackageProvider implementation.
412 ///////////////////////////////////////////////////////////////////
413 class RpmPackageProvider : public PackageProvider::Impl
416 RpmPackageProvider( RepoMediaAccess & access_r,
417 const Package::constPtr & package_r,
418 const DeltaCandidates & deltas_r,
419 const PackageProviderPolicy & policy_r )
420 : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
424 virtual ManagedFile doProvidePackageFromCache() const;
426 virtual ManagedFile doProvidePackage() const;
429 typedef packagedelta::DeltaRpm DeltaRpm;
431 ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
433 bool progressDeltaDownload( int value ) const
434 { return report()->progressDeltaDownload( value ); }
436 void progressDeltaApply( int value ) const
437 { return report()->progressDeltaApply( value ); }
439 bool queryInstalled( const Edition & ed_r = Edition() ) const
440 { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
442 ///////////////////////////////////////////////////////////////////
444 ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
446 return ManagedFile( _package->cachedLocation() );
449 ManagedFile RpmPackageProvider::doProvidePackage() const
452 RepoInfo info = _package->repoInfo();
453 // FIXME we only support the first url for now.
454 if ( info.baseUrlsEmpty() )
455 ZYPP_THROW(Exception("No url in repository."));
457 url = * info.baseUrlsBegin();
459 // check whether to process patch/delta rpms
460 if ( ZConfig::instance().download_use_deltarpm()
461 && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
463 std::list<DeltaRpm> deltaRpms;
464 _deltas.deltaRpms( _package ).swap( deltaRpms );
466 if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
468 for_( it, deltaRpms.begin(), deltaRpms.end())
470 DBG << "tryDelta " << *it << endl;
471 ManagedFile ret( tryDelta( *it ) );
472 if ( ! ret->empty() )
478 // no patch/delta -> provide full package
479 return Base::doProvidePackage();
482 ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
484 if ( delta_r.baseversion().edition() != Edition::noedition
485 && ! queryInstalled( delta_r.baseversion().edition() ) )
486 return ManagedFile();
488 if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
489 return ManagedFile();
491 report()->startDeltaDownload( delta_r.location().filename(),
492 delta_r.location().downloadSize() );
496 ProvideFilePolicy policy;
497 policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
498 delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
500 catch ( const Exception & excpt )
502 report()->problemDeltaDownload( excpt.asUserHistory() );
503 return ManagedFile();
505 report()->finishDeltaDownload();
507 report()->startDeltaApply( delta );
508 if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
510 report()->problemDeltaApply( _("applydeltarpm check failed.") );
511 return ManagedFile();
514 // build the package and put it into the cache
515 Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
517 if ( ! applydeltarpm::provide( delta, destination,
518 bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
520 report()->problemDeltaApply( _("applydeltarpm failed.") );
521 return ManagedFile();
523 report()->finishDeltaApply();
525 return ManagedFile( destination, filesystem::unlink );
529 ///////////////////////////////////////////////////////////////////
530 /// \class PluginPackageProvider
531 /// \brief Plugin PackageProvider implementation.
533 /// Basically downloads the default package and calls a
534 /// 'stem'2rpm plugin to cteate the final .rpm package.
535 ///////////////////////////////////////////////////////////////////
536 class PluginPackageProvider : public PackageProvider::Impl
539 PluginPackageProvider( const std::string & stem_r,
540 RepoMediaAccess & access_r,
541 const Package::constPtr & package_r,
542 const DeltaCandidates & deltas_r,
543 const PackageProviderPolicy & policy_r )
544 : Base( access_r, package_r, deltas_r, policy_r )
548 virtual ManagedFile doProvidePackageFromCache() const
550 return Base::doProvidePackageFromCache();
553 virtual ManagedFile doProvidePackage() const
555 return Base::doProvidePackage();
558 ///////////////////////////////////////////////////////////////////
561 ///////////////////////////////////////////////////////////////////
562 // class PackageProvider
563 ///////////////////////////////////////////////////////////////////
565 PackageProvider::Impl * PackageProvider::Impl::factoryMake( RepoMediaAccess & access_r,
566 const Package::constPtr & package_r,
567 const DeltaCandidates & deltas_r,
568 const PackageProviderPolicy & policy_r )
570 return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
573 PackageProvider::PackageProvider( RepoMediaAccess & access_r,
574 const Package::constPtr & package_r,
575 const DeltaCandidates & deltas_r,
576 const PackageProviderPolicy & policy_r )
577 : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
580 PackageProvider::~PackageProvider()
583 ManagedFile PackageProvider::providePackage() const
584 { return _pimpl->providePackage(); }
586 ManagedFile PackageProvider::providePackageFromCache() const
587 { return _pimpl->providePackageFromCache(); }
589 bool PackageProvider::isCached() const
590 { return _pimpl->isCached(); }
593 ///////////////////////////////////////////////////////////////////
595 ///////////////////////////////////////////////////////////////////