Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / Fetcher.cc
index c6bf217..ef8e95b 100644 (file)
@@ -27,6 +27,8 @@
 #include "zypp/parser/susetags/ContentFileReader.h"
 #include "zypp/parser/susetags/RepoIndex.h"
 
+using namespace std;
+
 #undef ZYPP_BASE_LOGGER_LOGGROUP
 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp:fetcher"
 
@@ -106,7 +108,7 @@ namespace zypp
     OnMediaLocation location;
     Pathname deltafile;
     //CompositeFileChecker checkers;
-    std::list<FileChecker> checkers;
+    list<FileChecker> checkers;
     Flags flags;
   };
 
@@ -184,16 +186,17 @@ namespace zypp
       void getDirectoryContent( MediaSetAccess &media, const OnMediaLocation &resource, filesystem::DirContent &content );
 
       /**
-       * Tries to locate the file represented by job by looking at
-       * the cache (matching checksum is mandatory). Returns the
-       * location of the cached file or an empty \ref Pathname.
+       * tries to provide the file represented by job into dest_dir by
+       * looking at the cache. If success, returns true, and the desired
+       * file should be available on dest_dir
        */
-      Pathname locateInCache( const OnMediaLocation & resource_r, const Pathname & destDir_r );
+      bool provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir );
       /**
-       * Validates the provided file against its checkers.
+       * Validates the job against is checkers, by using the file instance
+       * on dest_dir
        * \throws Exception
        */
-      void validate( const Pathname & localfile_r, const std::list<FileChecker> & checkers_r );
+      void validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers );
 
       /**
        * scan the directory and adds the individual jobs
@@ -211,7 +214,7 @@ namespace zypp
       /**
        * Provide the resource to \ref dest_dir
        */
-      void provideToDest( MediaSetAccess & media_r, const Pathname & destDir_r , const FetcherJob_Ptr & jobp_r );
+      void provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir , const Pathname &deltafile);
 
   private:
     friend Impl * rwcowClone<Impl>( const Impl * rhs );
@@ -219,13 +222,13 @@ namespace zypp
     Impl * clone() const
     { return new Impl( *this ); }
 
-    std::list<FetcherJob_Ptr>   _resources;
+    list<FetcherJob_Ptr>   _resources;
     std::set<FetcherIndex_Ptr,SameFetcherIndex> _indexes;
     std::set<Pathname> _caches;
     // checksums read from the indexes
-    std::map<std::string, CheckSum> _checksums;
+    map<string, CheckSum> _checksums;
     // cache of dir contents
-    std::map<std::string, filesystem::DirContent> _dircontent;
+    map<string, filesystem::DirContent> _dircontent;
 
     Fetcher::Options _options;
   };
@@ -329,48 +332,77 @@ namespace zypp
 
   }
 
-  Pathname Fetcher::Impl::locateInCache( const OnMediaLocation & resource_r, const Pathname & destDir_r )
+  // tries to provide resource to dest_dir from any of the configured additional
+  // cache paths where the file may already be present. returns true if the
+  // file was provided from the cache.
+  bool Fetcher::Impl::provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir )
   {
-    Pathname ret;
-    // No checksum - no match
-    if ( resource_r.checksum().empty() )
-      return ret;
+    Pathname dest_full_path = dest_dir + resource.filename();
 
     // first check in the destination directory
-    Pathname cacheLocation = destDir_r / resource_r.filename();
-    if ( PathInfo(cacheLocation).isExist() && is_checksum( cacheLocation, resource_r.checksum() ) )
+    if ( PathInfo(dest_full_path).isExist() )
     {
-      swap( ret, cacheLocation );
-      return ret;
+      if ( is_checksum( dest_full_path, resource.checksum() )
+           && (! resource.checksum().empty() ) )
+          return true;
     }
 
     MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
-    for( const Pathname & cacheDir : _caches )
+    for_ ( it_cache, _caches.begin(), _caches.end() )
     {
-      cacheLocation = cacheDir / resource_r.filename();
-      if ( PathInfo(cacheLocation).isExist() && is_checksum( cacheLocation, resource_r.checksum() ) )
+      // does the current file exists in the current cache?
+      Pathname cached_file = *it_cache + resource.filename();
+      if ( PathInfo( cached_file ).isExist() )
       {
-       MIL << "file " << resource_r.filename() << " found in cache " << cacheDir << endl;
-       swap( ret, cacheLocation );
-       return ret;
-      }
-    }
+        DBG << "File '" << cached_file << "' exist, testing checksum " << resource.checksum() << endl;
+         // check the checksum
+        if ( is_checksum( cached_file, resource.checksum() ) && (! resource.checksum().empty() ) )
+        {
+          // cached
+          MIL << "file " << resource.filename() << " found in previous cache. Using cached copy." << endl;
+          // checksum is already checked.
+          // we could later implement double failover and try to download if file copy fails.
+           // replicate the complete path in the target directory
+          if( dest_full_path != cached_file )
+          {
+            if ( assert_dir( dest_full_path.dirname() ) != 0 )
+              ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
 
-    return ret;
+            if ( filesystem::hardlinkCopy(cached_file, dest_full_path ) != 0 )
+            {
+              ERR << "Can't hardlink/copy " << cached_file + " to " + dest_dir << endl;
+              continue;
+            }
+          }
+          // found in cache
+          return true;
+        }
+      }
+    } // iterate over caches
+    return false;
   }
 
-  void Fetcher::Impl::validate( const Pathname & localfile_r, const std::list<FileChecker> & checkers_r )
+    void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
   {
+    // no matter where did we got the file, try to validate it:
+    Pathname localfile = dest_dir + resource.filename();
+    // call the checker function
     try
     {
-      MIL << "Checking job [" << localfile_r << "] (" << checkers_r.size() << " checkers )" << endl;
+      MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
 
-      for ( const FileChecker & chkfnc : checkers_r )
+      for ( list<FileChecker>::const_iterator it = checkers.begin();
+            it != checkers.end();
+            ++it )
       {
-        if ( chkfnc )
-          chkfnc( localfile_r );
+        if (*it)
+        {
+          (*it)(localfile);
+        }
         else
-          ERR << "Invalid checker for '" << localfile_r << "'" << endl;
+        {
+          ERR << "Invalid checker for '" << localfile << "'" << endl;
+        }
       }
 
     }
@@ -384,7 +416,7 @@ namespace zypp
     }
     catch (...)
     {
-      ZYPP_THROW(Exception("Unknown error while validating " + localfile_r.asString()));
+      ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
     }
   }
 
@@ -503,59 +535,60 @@ namespace zypp
       }
   }
 
-  void Fetcher::Impl::provideToDest( MediaSetAccess & media_r, const Pathname & destDir_r , const FetcherJob_Ptr & jobp_r )
+  void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir, const Pathname &deltafile )
   {
-    const OnMediaLocation & resource( jobp_r->location );
+    bool got_from_cache = false;
 
-    try
+    // start look in cache
+    got_from_cache = provideFromCache(resource, dest_dir);
+
+    if ( ! got_from_cache )
     {
-      scoped_ptr<MediaSetAccess::ReleaseFileGuard> releaseFileGuard; // will take care provided files get released
+      MIL << "Not found in cache, downloading" << endl;
 
-      // get cached file (by checksum) or provide from media
-      Pathname tmpFile = locateInCache( resource, destDir_r );
-      if ( tmpFile.empty() )
+      // try to get the file from the net
+      try
       {
-       MIL << "Not found in cache, retrieving..." << endl;
-       tmpFile = media_r.provideFile( resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, jobp_r->deltafile );
-       releaseFileGuard.reset( new MediaSetAccess::ReleaseFileGuard( media_r, resource ) ); // release it when we leave the block
-      }
+        Pathname tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, deltafile );
 
-      // The final destination: locateInCache also checks destFullPath!
-      // If we find a cache match (by checksum) at destFullPath, take
-      // care it gets deleted, in case the validation fails.
-      ManagedFile destFullPath( destDir_r / resource.filename() );
-      if ( tmpFile == destFullPath )
-       destFullPath.setDispose( filesystem::unlink );
+        Pathname dest_full_path = dest_dir + resource.filename();
 
-      // validate the file (throws if not valid)
-      validate( tmpFile, jobp_r->checkers );
+        if ( assert_dir( dest_full_path.dirname() ) != 0 )
+              ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
+        if ( filesystem::hardlinkCopy( tmp_file, dest_full_path ) != 0 )
+        {
+          if ( ! PathInfo(tmp_file).isExist() )
+              ERR << tmp_file << " does not exist" << endl;
+          if ( ! PathInfo(dest_full_path.dirname()).isExist() )
+              ERR << dest_full_path.dirname() << " does not exist" << endl;
 
-      // move it to the final destination
-      if ( tmpFile == destFullPath )
-       destFullPath.resetDispose();    // keep it!
-      else
-      {
-       if ( assert_dir( destFullPath->dirname() ) != 0 )
-         ZYPP_THROW( Exception( "Can't create " + destFullPath->dirname().asString() ) );
+          media.releaseFile(resource); //not needed anymore, only eat space
+          ZYPP_THROW( Exception("Can't hardlink/copy " + tmp_file.asString() + " to " + dest_dir.asString()));
+        }
 
-       if ( filesystem::hardlinkCopy( tmpFile, destFullPath ) != 0 )
-         ZYPP_THROW( Exception( "Can't hardlink/copy " + tmpFile.asString() + " to " + destDir_r.asString() ) );
-      }
-    }
-    catch ( Exception & excpt )
-    {
-      if ( resource.optional() )
-      {
-       ZYPP_CAUGHT( excpt );
-       WAR << "optional resource " << resource << " could not be transferred." << endl;
-       return;
+        media.releaseFile(resource); //not needed anymore, only eat space
       }
-      else
+      catch (Exception & excpt_r)
       {
-       excpt.remember( "Can't provide " + resource.filename().asString() );
-       ZYPP_RETHROW( excpt );
+        if ( resource.optional() )
+        {
+           ZYPP_CAUGHT(excpt_r);
+            WAR << "optional resource " << resource << " could not be transferred" << endl;
+            return;
+        }
+        else
+        {
+           excpt_r.remember("Can't provide " + resource.filename().asString() );
+            ZYPP_RETHROW(excpt_r);
+        }
       }
     }
+    else
+    {
+      // We got the file from cache
+      // continue with next file
+        return;
+    }
   }
 
   // helper class to consume a content file
@@ -722,12 +755,13 @@ namespace zypp
 
     downloadAndReadIndexList(media, dest_dir);
 
-    for ( const FetcherJob_Ptr & jobp : _resources )
+    for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
     {
-      if ( jobp->flags & FetcherJob::Directory )
+
+      if ( (*it_res)->flags & FetcherJob::Directory )
       {
-          const OnMediaLocation location(jobp->location);
-          addDirJobs(media, location, dest_dir, jobp->flags);
+          const OnMediaLocation location((*it_res)->location);
+          addDirJobs(media, location, dest_dir, (*it_res)->flags);
           continue;
       }
 
@@ -741,14 +775,14 @@ namespace zypp
           // index for each file. We look only in the directory
           // where the file is. this is expensive of course.
           filesystem::DirContent content;
-          getDirectoryContent(media, jobp->location.filename().dirname(), content);
+          getDirectoryContent(media, (*it_res)->location.filename().dirname(), content);
           // this method test for the option flags so indexes are added
           // only if the options are enabled
           MIL << "Autodiscovering signed indexes on '"
-              << jobp->location.filename().dirname() << "' for '"
-              << jobp->location.filename() << "'" << endl;
+              << (*it_res)->location.filename().dirname() << "' for '"
+              << (*it_res)->location.filename() << "'" << endl;
 
-          autoaddIndexes(content, media, jobp->location.filename().dirname(), dest_dir);
+          autoaddIndexes(content, media, (*it_res)->location.filename().dirname(), dest_dir);
 
           // also look in the root of the media
           content.clear();
@@ -757,44 +791,50 @@ namespace zypp
           // only if the options are enabled
           MIL << "Autodiscovering signed indexes on '"
               << "/" << "' for '"
-              << jobp->location.filename() << "'" << endl;
+              << (*it_res)->location.filename() << "'" << endl;
 
           autoaddIndexes(content, media, Pathname("/"), dest_dir);
       }
 
+      provideToDest(media, (*it_res)->location, dest_dir, (*it_res)->deltafile);
+
+      // if the file was not transferred, and no exception, just
+      // return, as it was an optional file
+      if ( ! PathInfo(dest_dir + (*it_res)->location.filename()).isExist() )
+          continue;
+
       // if the checksum is empty, but the checksum is in one of the
       // indexes checksum, then add a checker
-      if ( jobp->location.checksum().empty() )
+      if ( (*it_res)->location.checksum().empty() )
       {
-          if ( _checksums.find(jobp->location.filename().asString())
+          if ( _checksums.find((*it_res)->location.filename().asString())
                != _checksums.end() )
           {
-              CheckSum chksm = _checksums[jobp->location.filename().asString()];
+              CheckSum chksm = _checksums[(*it_res)->location.filename().asString()];
               ChecksumFileChecker digest_check(chksm);
-              jobp->checkers.push_back(digest_check);
+              (*it_res)->checkers.push_back(digest_check);
           }
           else
           {
               // if the index checksum is empty too, we only add the checker
               // if the  AlwaysVerifyChecksum option is set on
-              if ( jobp->flags & FetcherJob::AlwaysVerifyChecksum )
+              if ( (*it_res)->flags & FetcherJob::AlwaysVerifyChecksum )
               {
                   // add the checker with the empty checksum
-                  ChecksumFileChecker digest_check(jobp->location.checksum());
-                  jobp->checkers.push_back(digest_check);
+                  ChecksumFileChecker digest_check((*it_res)->location.checksum());
+                  (*it_res)->checkers.push_back(digest_check);
               }
           }
       }
       else
       {
           // checksum is not empty, so add a checksum checker
-          ChecksumFileChecker digest_check(jobp->location.checksum());
-          jobp->checkers.push_back(digest_check);
+          ChecksumFileChecker digest_check((*it_res)->location.checksum());
+          (*it_res)->checkers.push_back(digest_check);
       }
 
-      // Provide and validate the file. If the file was not transferred
-      // and no exception was thrown, it was an optional file.
-      provideToDest( media, dest_dir, jobp );
+      // validate job, this throws if not valid
+      validate((*it_res)->location, dest_dir, (*it_res)->checkers);
 
       if ( ! progress.incr() )
         ZYPP_THROW(AbortRequestException());
@@ -804,7 +844,7 @@ namespace zypp
   /** \relates Fetcher::Impl Stream output */
   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
   {
-      for ( std::list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
+      for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
       {
           str << *it_res;
       }