libzypp  10.5.0
RepoProvideFile.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include <fstream>
00014 #include <sstream>
00015 #include <set>
00016 
00017 #include "zypp/base/Gettext.h"
00018 #include "zypp/base/Logger.h"
00019 #include "zypp/base/String.h"
00020 #include "zypp/base/UserRequestException.h"
00021 #include "zypp/repo/RepoProvideFile.h"
00022 #include "zypp/ZYppCallbacks.h"
00023 #include "zypp/MediaSetAccess.h"
00024 #include "zypp/ZConfig.h"
00025 #include "zypp/ZYppFactory.h"
00026 #include "zypp/repo/SUSEMediaVerifier.h"
00027 #include "zypp/repo/RepoException.h"
00028 
00029 #include "zypp/repo/SUSEMediaVerifier.h"
00030 #include "zypp/repo/RepoException.h"
00031 #include "zypp/FileChecker.h"
00032 #include "zypp/Fetcher.h"
00033 
00034 using std::endl;
00035 using std::set;
00036 
00038 namespace zypp
00039 { 
00040 
00041   namespace repo
00042   { 
00043 
00045     //
00046     //  provideFile
00047     //
00049 
00051     namespace
00052     { 
00053 
00059       struct DownloadFileReportHack : public callback::ReceiveReport<media::DownloadProgressReport>
00060       {
00061         typedef callback::ReceiveReport<ReportType> BaseType;
00062         typedef function<bool(int)>                 RedirectType;
00063 
00064         DownloadFileReportHack( RedirectType redirect_r )
00065         : _oldRec( Distributor::instance().getReceiver() )
00066         , _redirect( redirect_r )
00067         { connect(); }
00068         ~DownloadFileReportHack()
00069         { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
00070 
00071         virtual void start( const Url & file, Pathname localfile )
00072         {
00073           if ( _oldRec )
00074             _oldRec->start( file, localfile );
00075           else
00076             BaseType::start( file, localfile );
00077         }
00078 
00079         virtual bool progress( int value, const Url & file, double dbps_avg = -1, double dbps_current = -1 )
00080         {
00081           bool ret = true;
00082           if ( _oldRec )
00083             ret &= _oldRec->progress( value, file, dbps_avg, dbps_current );
00084           if ( _redirect )
00085             ret &= _redirect( value );
00086           return ret;
00087         }
00088 
00089         virtual Action problem( const Url & file, Error error, const std::string & description )
00090         {
00091           if ( _oldRec )
00092             return _oldRec->problem( file, error, description );
00093           return BaseType::problem( file, error, description );
00094         }
00095         virtual void finish( const Url & file, Error error, const std::string & reason )
00096         {
00097           if ( _oldRec )
00098             _oldRec->finish( file, error, reason );
00099           else
00100             BaseType::finish( file, error, reason );
00101         }
00102 
00103         private:
00104           Receiver * _oldRec;
00105           RedirectType _redirect;
00106       };
00107 
00109     } // namespace
00111 
00112     ManagedFile provideFile( RepoInfo repo_r,
00113                              const OnMediaLocation & loc_r,
00114                              const ProvideFilePolicy & policy_r )
00115     {
00116       RepoMediaAccess access;
00117       return access.provideFile(repo_r, loc_r, policy_r );
00118     }
00119 
00121     class RepoMediaAccess::Impl
00122     {
00123     public:
00124       Impl( const ProvideFilePolicy & defaultPolicy_r )
00125         : _defaultPolicy( defaultPolicy_r )
00126       {}
00127 
00128       ~Impl()
00129       {
00130         std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
00131         for ( it = _medias.begin();
00132               it != _medias.end();
00133               ++it )
00134         {
00135           it->second->release();
00136         }
00137       }
00138 
00146       shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url, RepoInfo repo )
00147       {
00148         std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
00149         it = _medias.find(url);
00150         shared_ptr<MediaSetAccess> media;
00151         if ( it != _medias.end() )
00152         {
00153           media = it->second;
00154         }
00155         else
00156         {
00157           media.reset( new MediaSetAccess(url) );
00158           _medias[url] = media;
00159         }
00160         setVerifierForRepo( repo, media );
00161         return media;
00162       }
00163 
00164       private:
00165         void setVerifierForRepo( RepoInfo repo, shared_ptr<MediaSetAccess> media )
00166         {
00167           // Always set the MediaSetAccess label.
00168           media->setLabel( repo.name() );
00169 
00170           // set a verifier if the repository has it
00171 
00172           Pathname mediafile = repo.metadataPath() + "/media.1/media";
00173           if ( ! repo.metadataPath().empty() )
00174           {
00175             if ( PathInfo(mediafile).isExist() )
00176             {
00177               std::map<shared_ptr<MediaSetAccess>, RepoInfo>::const_iterator it;
00178               it = _verifier.find(media);
00179               if ( it != _verifier.end() )
00180               {
00181                 if ( it->second.alias() == repo.alias() )
00182                 {
00183                   // this media is already using this repo verifier
00184                   return;
00185                 }
00186               }
00187 
00188               std::ifstream str(mediafile.asString().c_str());
00189               std::string vendor;
00190               std::string mediaid;
00191               std::string buffer;
00192               if ( str )
00193               {
00194                 getline(str, vendor);
00195                 getline(str, mediaid);
00196                 getline(str, buffer);
00197 
00198                 unsigned media_nr = str::strtonum<unsigned>(buffer);
00199                 MIL << "Repository '" << repo.alias() << "' has " << media_nr << " medias"<< endl;
00200 
00201                 for ( unsigned i=1; i <= media_nr; ++i )
00202                 {
00203                   media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
00204 
00205                   media->setVerifier( i, verifier);
00206                 }
00207                 _verifier[media] = repo;
00208               }
00209               else
00210               {
00211                 ZYPP_THROW(RepoMetadataException(repo));
00212               }
00213             }
00214             else
00215             {
00216               WAR << "No media verifier for repo '" << repo.alias() << "' media/media.1 does not exist in '" << repo.metadataPath() << "'" << endl;
00217             }
00218           }
00219           else
00220           {
00221             WAR << "'" << repo.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
00222           }
00223         }
00224 
00225       private:
00226         std::map<shared_ptr<MediaSetAccess>, RepoInfo> _verifier;
00227         std::map<Url, shared_ptr<MediaSetAccess> > _medias;
00228 
00229       public:
00230         ProvideFilePolicy _defaultPolicy;
00231     };
00233 
00234 
00235     RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
00236       : _impl( new Impl( defaultPolicy_r ) )
00237     {}
00238 
00239     RepoMediaAccess::~RepoMediaAccess()
00240     {}
00241 
00242     void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
00243     { _impl->_defaultPolicy = policy_r; }
00244 
00245     const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
00246     { return _impl->_defaultPolicy; }
00247 
00248     ManagedFile RepoMediaAccess::provideFile( RepoInfo repo_r,
00249                                               const OnMediaLocation & loc_r,
00250                                               const ProvideFilePolicy & policy_r )
00251     {
00252       MIL << loc_r << endl;
00253       // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
00254       // and redirect download progress triggers to call the ProvideFilePolicy
00255       // callback.
00256       DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
00257 
00258       RepoException repo_excpt(repo_r,
00259                                str::form(_("Can't provide file '%s' from repository '%s'"),
00260                                loc_r.filename().c_str(),
00261                                repo_r.alias().c_str() ) );
00262 
00263       if ( repo_r.baseUrlsEmpty() )
00264       {
00265         repo_excpt.remember(RepoException(_("No url in repository.")));
00266         ZYPP_THROW(repo_excpt);
00267       }
00268 
00269       Fetcher fetcher;
00270       fetcher.addCachePath( repo_r.packagesPath() );
00271       MIL << "Added cache path " << repo_r.packagesPath() << endl;
00272 
00273       // Test whether download destination is writable, if not
00274       // switch into the tmpspace (e.g. bnc#755239, download and
00275       // install srpms as user).
00276       Pathname destinationDir( repo_r.packagesPath() );
00277 
00278       PathInfo pi( destinationDir );
00279       if ( ! pi.isExist() )
00280       {
00281         // try to create it...
00282         assert_dir( destinationDir );
00283         pi();
00284       }
00285       if ( ! pi.userMayW() )
00286       {
00287         WAR << "Destination dir '" << destinationDir << "' is not user writable, using tmp space." << endl;
00288         destinationDir = getZYpp()->tmpPath() / destinationDir;
00289         assert_dir( destinationDir );
00290         fetcher.addCachePath( destinationDir );
00291         MIL << "Added cache path " << destinationDir << endl;
00292       }
00293 
00294       for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
00295             it != repo_r.baseUrlsEnd();
00296             /* incremented in the loop */ )
00297       {
00298         Url url( *it );
00299         ++it;
00300         try
00301         {
00302           MIL << "Providing file of repo '" << repo_r.alias()
00303               << "' from " << url << endl;
00304           shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
00305 
00306           fetcher.enqueue( loc_r );
00307 
00308           // FIXME: works for packages only
00309           fetcher.start( destinationDir, *access );
00310 
00311           // reached if no exception has been thrown, so this is the correct file
00312           ManagedFile ret( destinationDir + loc_r.filename() );
00313 
00314           std::string scheme( url.getScheme() );
00315           if ( !repo_r.keepPackages() )
00316           {
00317             ret.setDispose( filesystem::unlink );
00318           }
00319 
00320           if ( loc_r.checksum().empty() )
00321           {
00322             // no checksum in metadata
00323             WAR << "No checksum in metadata " << loc_r << endl;
00324           }
00325           else
00326           {
00327             std::ifstream input( ret->asString().c_str() );
00328             CheckSum retChecksum( loc_r.checksum().type(), input );
00329             input.close();
00330 
00331             if ( loc_r.checksum() != retChecksum )
00332             {
00333               // failed integity check
00334               std::ostringstream err;
00335               err << "File " << ret << " fails integrity check. Expected: [" << loc_r.checksum() << "] Got: [";
00336               if ( retChecksum.empty() )
00337                 err << "Failed to compute checksum";
00338               else
00339                 err << retChecksum;
00340               err << "]";
00341 
00342               WAR << err.str() << endl;
00343 
00344               if ( policy_r.failOnChecksumError() )
00345                 ZYPP_THROW( FileCheckException( err.str() ) );
00346               else
00347                 WAR << "NO failOnChecksumError: " << err.str() << endl;
00348             }
00349           }
00350 
00351           MIL << "provideFile at " << ret << endl;
00352           return ret;
00353         }
00354         catch ( const SkipRequestException &e )
00355         {
00356           ZYPP_CAUGHT( e );
00357           ZYPP_RETHROW(e);
00358         }
00359         catch ( const AbortRequestException &e )
00360         {
00361           ZYPP_CAUGHT( e );
00362           ZYPP_RETHROW(e);
00363         }
00364         catch ( const Exception &e )
00365         {
00366           ZYPP_CAUGHT( e );
00367 
00368           repo_excpt.remember(e);
00369 
00370           WAR << "Trying next url" << endl;
00371           continue;
00372         }
00373       } // iteration over urls
00374 
00375       ZYPP_THROW(repo_excpt);
00376       return ManagedFile(); // not reached
00377     }
00378 
00380   } // namespace repo
00383 } // namespace zypp