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 method for progressPackageDownload is provided here.
129 * ProvideFilePolicy policy;
130 * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
131 * return _access.provideFile( _package->repoInfo(), loc, policy );
134 * \note The provided default implementation retrieves the packages default
137 virtual ManagedFile doProvidePackage() const = 0;
140 /** Access to the DownloadResolvableReport */
141 Report & report() const
144 /** Redirect ProvideFilePolicy package download progress to this. */
145 bool progressPackageDownload( int value ) const
146 { return report()->progress( value, _package ); }
148 typedef target::rpm::RpmDb RpmDb;
150 RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
153 _target = getZYpp()->getTarget();
155 RpmDb::CheckPackageResult ret = RpmDb::CHK_ERROR;
156 RpmDb::CheckPackageDetail detail;
158 ret = _target->rpmDb().checkPackage( path_r, detail );
160 detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
162 userData.set( "CheckPackageResult", ret );
163 userData.set( "CheckPackageDetail", std::move(detail) );
167 /** React on signature verification error user action
168 * \note: IGNORE == accept insecure file (no SkipRequestException!)
170 void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
174 case repo::DownloadResolvableReport::RETRY:
177 case repo::DownloadResolvableReport::IGNORE:
178 WAR << _package->asUserString() << ": " << "User requested to accept insecure file" << endl;
181 case repo::DownloadResolvableReport::ABORT:
182 ZYPP_THROW(AbortRequestException("User requested to abort"));
187 /** Default signature verification error handling. */
188 void defaultReportSignatureError( RpmDb::CheckPackageResult ret, const std::string & detail_r = std::string() ) const
191 msg << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret;
192 if ( ! detail_r.empty() )
193 msg << "\n" << detail_r;
194 resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg.str() ) );
198 PackageProviderPolicy _policy;
199 Package::constPtr _package;
200 DeltaCandidates _deltas;
201 RepoMediaAccess & _access;
204 typedef shared_ptr<void> ScopedGuard;
206 ScopedGuard newReport() const
208 _report.reset( new Report );
209 // Use a custom deleter calling _report.reset() when guard goes out of
210 // scope (cast required as reset is overloaded). We want report to end
211 // when leaving providePackage and not wait for *this going out of scope.
212 return shared_ptr<void>( static_cast<void*>(0),
213 bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
218 mutable shared_ptr<Report> _report;
219 mutable Target_Ptr _target;
221 ///////////////////////////////////////////////////////////////////
223 /** Default implementation (cache miss). */
224 ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
225 { return ManagedFile(); }
227 /** Default implementation (provide full package) */
228 ManagedFile PackageProvider::Impl::doProvidePackage() const
231 OnMediaLocation loc = _package->location();
233 ProvideFilePolicy policy;
234 policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
235 return _access.provideFile( _package->repoInfo(), loc, policy );
238 ///////////////////////////////////////////////////////////////////
240 ManagedFile PackageProvider::Impl::providePackage() const
242 ScopedGuard guardReport( newReport() );
244 // check for cache hit:
245 ManagedFile ret( providePackageFromCache() );
246 if ( ! ret->empty() )
248 MIL << "provided Package from cache " << _package << " at " << ret << endl;
249 report()->infoInCache( _package, ret );
250 return ret; // <-- cache hit
253 // HERE: cache misss, check toplevel cache or do download:
254 RepoInfo info = _package->repoInfo();
256 // Check toplevel cache
258 RepoManagerOptions topCache;
259 if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath ) // not using toplevel cache
261 const OnMediaLocation & loc( _package->location() );
262 if ( ! loc.checksum().empty() ) // no cache hit without checksum
264 PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
265 if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
267 report()->start( _package, pi.path().asFileUrl() );
268 const Pathname & dest( info.packagesPath() / loc.filename() );
269 if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
271 ret = ManagedFile( dest );
272 if ( ! info.keepPackages() )
273 ret.setDispose( filesystem::unlink );
275 MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
276 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
277 return ret; // <-- toplevel cache hit
284 // FIXME we only support the first url for now.
285 if ( info.baseUrlsEmpty() )
286 ZYPP_THROW(Exception("No url in repository."));
288 MIL << "provide Package " << _package << endl;
289 Url url = * info.baseUrlsBegin();
292 if ( ! ret->empty() )
294 ret.setDispose( filesystem::unlink );
297 report()->start( _package, url );
300 ret = doProvidePackage();
302 if ( info.pkgGpgCheck() )
304 UserData userData( "pkgGpgCheck" );
305 userData.set( "Package", _package );
306 userData.set( "Localpath", ret.value() );
307 RpmDb::CheckPackageResult res = packageSigCheck( ret, userData );
308 // publish the checkresult, even if it is OK. Apps may want to report something...
309 report()->pkgGpgCheck( userData );
310 USR << "CHK: " << res << endl;
311 if ( res != RpmDb::CHK_OK )
313 if ( userData.hasvalue( "Action" ) ) // pkgGpgCheck report provided an user error action
315 resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
317 else if ( userData.haskey( "Action" ) ) // pkgGpgCheck requests the default problem report (wo. details)
319 defaultReportSignatureError( res );
321 else // no advice from user => usedefaults
325 case RpmDb::CHK_OK: // Signature is OK
328 case RpmDb::CHK_NOKEY: // Public key is unavailable
329 case RpmDb::CHK_NOTFOUND: // Signature is unknown type
330 case RpmDb::CHK_FAIL: // Signature does not verify
331 case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
332 case RpmDb::CHK_ERROR: // File does not exist or can't be opened
334 // report problem (w. details), throw if to abort, else retry/ignore
335 defaultReportSignatureError( res, str::Str() << userData.get<RpmDb::CheckPackageDetail>( "CheckPackageDetail" ) );
342 catch ( const UserRequestException & excpt )
344 ERR << "Failed to provide Package " << _package << endl;
346 ZYPP_RETHROW( excpt );
348 catch ( const FileCheckException & excpt )
350 ERR << "Failed to provide Package " << _package << endl;
353 const std::string & package_str = _package->asUserString();
354 // TranslatorExplanation %s = package being checked for integrity
355 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() ) ) )
357 case repo::DownloadResolvableReport::RETRY:
360 case repo::DownloadResolvableReport::IGNORE:
361 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
363 case repo::DownloadResolvableReport::ABORT:
364 ZYPP_THROW(AbortRequestException("User requested to abort"));
371 catch ( const Exception & excpt )
373 ERR << "Failed to provide Package " << _package << endl;
376 // Aything else gets reported
377 const std::string & package_str = _package->asUserString();
379 // TranslatorExplanation %s = name of the package being processed.
380 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
381 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
383 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
385 case repo::DownloadResolvableReport::RETRY:
388 case repo::DownloadResolvableReport::IGNORE:
389 ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
391 case repo::DownloadResolvableReport::ABORT:
392 ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
395 ZYPP_RETHROW( excpt );
402 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
403 MIL << "provided Package " << _package << " at " << ret << endl;
408 ///////////////////////////////////////////////////////////////////
409 /// \class RpmPackageProvider
410 /// \brief RPM PackageProvider implementation.
411 ///////////////////////////////////////////////////////////////////
412 class RpmPackageProvider : public PackageProvider::Impl
415 RpmPackageProvider( RepoMediaAccess & access_r,
416 const Package::constPtr & package_r,
417 const DeltaCandidates & deltas_r,
418 const PackageProviderPolicy & policy_r )
419 : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
423 virtual ManagedFile doProvidePackageFromCache() const;
425 virtual ManagedFile doProvidePackage() const;
428 typedef packagedelta::DeltaRpm DeltaRpm;
430 ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
432 bool progressDeltaDownload( int value ) const
433 { return report()->progressDeltaDownload( value ); }
435 void progressDeltaApply( int value ) const
436 { return report()->progressDeltaApply( value ); }
438 bool queryInstalled( const Edition & ed_r = Edition() ) const
439 { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
441 ///////////////////////////////////////////////////////////////////
443 ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
445 return ManagedFile( _package->cachedLocation() );
448 ManagedFile RpmPackageProvider::doProvidePackage() const
451 RepoInfo info = _package->repoInfo();
452 // FIXME we only support the first url for now.
453 if ( info.baseUrlsEmpty() )
454 ZYPP_THROW(Exception("No url in repository."));
456 url = * info.baseUrlsBegin();
458 // check whether to process patch/delta rpms
459 if ( ZConfig::instance().download_use_deltarpm()
460 && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
462 std::list<DeltaRpm> deltaRpms;
463 _deltas.deltaRpms( _package ).swap( deltaRpms );
465 if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
467 for_( it, deltaRpms.begin(), deltaRpms.end())
469 DBG << "tryDelta " << *it << endl;
470 ManagedFile ret( tryDelta( *it ) );
471 if ( ! ret->empty() )
477 // no patch/delta -> provide full package
478 return Base::doProvidePackage();
481 ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
483 if ( delta_r.baseversion().edition() != Edition::noedition
484 && ! queryInstalled( delta_r.baseversion().edition() ) )
485 return ManagedFile();
487 if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
488 return ManagedFile();
490 report()->startDeltaDownload( delta_r.location().filename(),
491 delta_r.location().downloadSize() );
495 ProvideFilePolicy policy;
496 policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
497 delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
499 catch ( const Exception & excpt )
501 report()->problemDeltaDownload( excpt.asUserHistory() );
502 return ManagedFile();
504 report()->finishDeltaDownload();
506 report()->startDeltaApply( delta );
507 if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
509 report()->problemDeltaApply( _("applydeltarpm check failed.") );
510 return ManagedFile();
513 // build the package and put it into the cache
514 Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
516 if ( ! applydeltarpm::provide( delta, destination,
517 bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
519 report()->problemDeltaApply( _("applydeltarpm failed.") );
520 return ManagedFile();
522 report()->finishDeltaApply();
524 return ManagedFile( destination, filesystem::unlink );
528 ///////////////////////////////////////////////////////////////////
529 /// \class PluginPackageProvider
530 /// \brief Plugin PackageProvider implementation.
532 /// Basically downloads the default package and calls a
533 /// 'stem'2rpm plugin to cteate the final .rpm package.
534 ///////////////////////////////////////////////////////////////////
535 class PluginPackageProvider : public PackageProvider::Impl
538 PluginPackageProvider( const std::string & stem_r,
539 RepoMediaAccess & access_r,
540 const Package::constPtr & package_r,
541 const DeltaCandidates & deltas_r,
542 const PackageProviderPolicy & policy_r )
543 : Base( access_r, package_r, deltas_r, policy_r )
547 virtual ManagedFile doProvidePackageFromCache() const
549 return Base::doProvidePackageFromCache();
552 virtual ManagedFile doProvidePackage() const
554 return Base::doProvidePackage();
557 ///////////////////////////////////////////////////////////////////
560 ///////////////////////////////////////////////////////////////////
561 // class PackageProvider
562 ///////////////////////////////////////////////////////////////////
564 PackageProvider::Impl * PackageProvider::Impl::factoryMake( RepoMediaAccess & access_r,
565 const Package::constPtr & package_r,
566 const DeltaCandidates & deltas_r,
567 const PackageProviderPolicy & policy_r )
569 return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
572 PackageProvider::PackageProvider( RepoMediaAccess & access_r,
573 const Package::constPtr & package_r,
574 const DeltaCandidates & deltas_r,
575 const PackageProviderPolicy & policy_r )
576 : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
579 PackageProvider::~PackageProvider()
582 ManagedFile PackageProvider::providePackage() const
583 { return _pimpl->providePackage(); }
585 ManagedFile PackageProvider::providePackageFromCache() const
586 { return _pimpl->providePackageFromCache(); }
588 bool PackageProvider::isCached() const
589 { return _pimpl->isCached(); }
592 ///////////////////////////////////////////////////////////////////
594 ///////////////////////////////////////////////////////////////////