Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / MediaSetAccess.cc
index ca26bfb..66bc72f 100644 (file)
@@ -11,6 +11,8 @@
 #include <fstream>
 
 #include "zypp/base/LogTools.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/UserRequestException.h"
 #include "zypp/ZYppCallbacks.h"
 #include "zypp/MediaSetAccess.h"
 #include "zypp/PathInfo.h"
@@ -21,201 +23,300 @@ using namespace std;
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(MediaSetAccess);
+
 ///////////////////////////////////////////////////////////////////
 
-  ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
-    : _checksum(checksum)
+  MediaSetAccess::MediaSetAccess(const Url &url,
+                                 const Pathname & prefered_attach_point)
+      : _url(url)
+      , _prefAttachPoint(prefered_attach_point)
+  {}
+
+  MediaSetAccess::MediaSetAccess(const std::string & label_r,
+                                 const Url &url,
+                                 const Pathname & prefered_attach_point)
+      : _url(url)
+      , _prefAttachPoint(prefered_attach_point)
+      , _label( label_r )
+  {}
+
+  MediaSetAccess::~MediaSetAccess()
   {
+    try
+    {
+      release();
+    }
+    catch(...) {} // don't let exception escape a dtor.
   }
 
-  bool ChecksumFileChecker::operator()( const Pathname &file )
+
+  void MediaSetAccess::setVerifier( unsigned media_nr, media::MediaVerifierRef verifier )
   {
-    // FIXME probably this funcionality should be in CheckSum itself
-    CheckSum real_checksum( _checksum.type(), filesystem::checksum( file, _checksum.type() ));
-    if ( real_checksum == _checksum )
+    if (_medias.find(media_nr) != _medias.end())
     {
-      return true;
+      // the media already exists, set theverifier
+      media::MediaAccessId id = _medias[media_nr];
+      media::MediaManager media_mgr;
+      media_mgr.addVerifier( id, verifier );
+      // remove any saved verifier for this media
+      _verifiers.erase(media_nr);
     }
     else
     {
-      ERR << "Got " << real_checksum << ", expected " << _checksum << std::endl;
-      return false;
+      // save the verifier in the map, and set it when
+      // the media number is first attached
+      _verifiers[media_nr] = verifier;
     }
   }
 
-  bool NullFileChecker::operator()(const Pathname &file )
+  void MediaSetAccess::releaseFile( const OnMediaLocation & on_media_file )
   {
-    return true;
+    releaseFile( on_media_file.filename(), on_media_file.medianr() );
   }
 
-  MediaSetAccess::MediaSetAccess(  const Url &url, const Pathname &path )
-      : _url(url),
-        _path(path)
-  {
-    MIL << "initializing.." << std::endl;
-    //std::vector<media::MediaVerifierRef> single_media;
-    //single_media[0] = media::MediaVerifierRef(new media::NoVerifier());
-    //_verifiers = single_media;
-  }
-  
-  MediaSetAccess::~MediaSetAccess()
+  void MediaSetAccess::releaseFile( const Pathname & file, unsigned media_nr)
   {
+    media::MediaManager media_mgr;
+    media::MediaAccessId media;
+
+    media = getMediaAccessId( media_nr);
+    DBG << "Going to release file " << file
+        << " from media number " << media_nr << endl;
+
+    if ( ! media_mgr.isAttached(media) )
+      return; //disattached media is free
+
+    media_mgr.releaseFile (media, file);
   }
 
-  void MediaSetAccess::setVerifiers( const std::vector<media::MediaVerifierRef> &verifiers )
+  void MediaSetAccess::dirInfo( filesystem::DirContent &retlist, const Pathname &dirname,
+                                bool dots, unsigned media_nr )
   {
-    _verifiers = verifiers;
+    media::MediaManager media_mgr;
+    media::MediaAccessId media;
+    media = getMediaAccessId(media_nr);
+
+    // try to attach the media
+    if ( ! media_mgr.isAttached(media) )
+        media_mgr.attach(media);
+
+    media_mgr.dirInfo(media, retlist, dirname, dots);
   }
 
-//       callback::SendReport<source::DownloadFileReport> report;
-//       DownloadProgressFileReceiver download_report( report );
-//       SourceFactory source_factory;
-//       Url file_url( url().asString() + file_r.asString() );
-//       report->start( source_factory.createFrom(this), file_url );
-//       callback::TempConnect<media::DownloadProgressReport> tmp_download( download_report );
-//       Pathname file = provideJustFile( file_r, media_nr, cached, checkonly );
-//       report->finish( file_url, source::DownloadFileReport::NO_ERROR, "" );
-//       return file;
+  struct ProvideFileOperation
+  {
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      media_mgr.provideFile(media, file);
+      result = media_mgr.localPath(media, file);
+    }
+  };
 
+  struct ProvideDirTreeOperation
+  {
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      media_mgr.provideDirTree(media, file);
+      result = media_mgr.localPath(media, file);
+    }
+  };
 
-  void MediaSetAccess::providePossiblyCachedMetadataFile( const Pathname &file_to_download, unsigned medianr, const Pathname &destination, const Pathname &cached_file, const CheckSum &checksum )
+  struct ProvideDirOperation
   {
-    Url file_url( _url.asString() + file_to_download.asString() );
-    // if we have a cached file and its the same
-    if ( PathInfo(cached_file).isExist() && (! checksum.empty()) && is_checksum( cached_file, checksum ) )
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
     {
-      MIL << "file " << file_url << " found in previous cache. Using cached copy." << std::endl;
-      // checksum is already checked.
-      // we could later implement double failover and try to download if file copy fails.
-      if ( filesystem::copy(cached_file, destination) != 0 )
-        ZYPP_THROW(Exception("Can't copy " + cached_file.asString() + " to " + destination.asString()));
+      media::MediaManager media_mgr;
+      media_mgr.provideDir(media, file);
+      result = media_mgr.localPath(media, file);
     }
-    else
+  };
+
+  struct ProvideFileExistenceOperation
+  {
+    bool result;
+    ProvideFileExistenceOperation()
+        : result(false)
+    {}
+
+    void operator()( media::MediaAccessId media, const Pathname &file )
     {
-      // we dont have it or its not the same, download it.
-      Pathname downloaded_file = provideFile( file_to_download, medianr, ChecksumFileChecker(checksum) );
-      
-      if ( filesystem::copy(downloaded_file, destination) != 0 )
-        ZYPP_THROW(Exception("Can't copy " + downloaded_file.asString() + " to " + destination.asString()));      
+      media::MediaManager media_mgr;
+      result = media_mgr.doesFileExist(media, file);
     }
+  };
+
+
+
+  Pathname MediaSetAccess::provideFile( const OnMediaLocation & resource, ProvideFileOptions options, const Pathname &deltafile )
+  {
+    ProvideFileOperation op;
+    provide( boost::ref(op), resource, options, deltafile );
+    return op.result;
   }
 
-  Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr )
+  Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, ProvideFileOptions options )
   {
-    return provideFileInternal( file, media_nr, false, false);
+    OnMediaLocation resource;
+    ProvideFileOperation op;
+    resource.setLocation(file, media_nr);
+    provide( boost::ref(op), resource, options, Pathname() );
+    return op.result;
   }
 
-  Pathname  MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, FileChecker checker )
+  bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
   {
-    Pathname p = provideFileInternal( file, media_nr, false, false);
-    
-    if ( ! checker(p) )
-    {
-      ZYPP_THROW(Exception("Error checker"));
-    }
-    return p;
+    ProvideFileExistenceOperation op;
+    OnMediaLocation resource;
+    resource.setLocation(file, media_nr);
+    provide( boost::ref(op), resource, PROVIDE_DEFAULT, Pathname());
+    return op.result;
   }
 
-  Pathname MediaSetAccess::provideFileInternal(const Pathname & file, unsigned media_nr, bool cached, bool checkonly )
+  void MediaSetAccess::provide( ProvideOperation op,
+                                const OnMediaLocation &resource,
+                                ProvideFileOptions options,
+                                const Pathname &deltafile )
   {
+    Pathname file(resource.filename());
+    unsigned media_nr(resource.medianr());
+
     callback::SendReport<media::MediaChangeReport> report;
     media::MediaManager media_mgr;
-    // get the mediaId, but don't try to attach it here
-    media::MediaAccessId media = getMediaAccessId( media_nr);
-      
+
+    media::MediaAccessId media;
+
     do
     {
+      // get the mediaId, but don't try to attach it here
+      media = getMediaAccessId( media_nr);
+      bool deltafileset = false;
+
       try
       {
-        DBG << "Going to try provide file " << file << " from " << media_nr << endl;        
+        DBG << "Going to try to provide " << (resource.optional() ? "optional" : "") << " file " << file
+            << " from media number " << media_nr << endl;
         // try to attach the media
         if ( ! media_mgr.isAttached(media) )
-        media_mgr.attach(media);
-        media_mgr.provideFile (media, file, false, false);
+          media_mgr.attach(media);
+       media_mgr.setDeltafile(media, deltafile);
+       deltafileset = true;
+        op(media, file);
+       media_mgr.setDeltafile(media, Pathname());
         break;
       }
-      catch ( Exception & excp )
+      catch ( media::MediaException & excp )
       {
         ZYPP_CAUGHT(excp);
-        media::MediaChangeReport::Action user;
+       if (deltafileset)
+         media_mgr.setDeltafile(media, Pathname());
+        media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
+        unsigned int devindex = 0;
+        vector<string> devices;
+        media_mgr.getDetectedDevices(media, devices, devindex);
+
         do
         {
-          DBG << "Media couldn't provide file " << file << " , releasing." << endl;
-          try
-          {
-            media_mgr.release (media, false);
-          }
-          catch (const Exception & excpt_r)
-          {
-              ZYPP_CAUGHT(excpt_r);
-              MIL << "Failed to release media " << media << endl;
-          }
-          
-          MIL << "Releasing all medias of all sources" << endl;
-          try
-          {
-            //zypp::SourceManager::sourceManager()->releaseAllSources();
-          }
-          catch (const zypp::Exception& excpt_r)
+          if (user != media::MediaChangeReport::EJECT) // no use in calling this again
           {
-              ZYPP_CAUGHT(excpt_r);
-              ERR << "Failed to release all sources" << endl;
+            DBG << "Media couldn't provide file " << file << " , releasing." << endl;
+            try
+            {
+              media_mgr.release(media);
+            }
+            catch (const Exception & excpt_r)
+            {
+                ZYPP_CAUGHT(excpt_r);
+                MIL << "Failed to release media " << media << endl;
+            }
           }
 
           // set up the reason
           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
-          
+
           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
               typeid(excp) == typeid( media::MediaNotAFileException ) )
           {
             reason = media::MediaChangeReport::NOT_FOUND;
-          } 
+          }
           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
               typeid(excp) == typeid( media::MediaNotAttachedException) )
           {
             reason = media::MediaChangeReport::WRONG;
           }
+          else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
+                   typeid(excp) == typeid( media::MediaTemporaryProblemException))
+          {
+            reason = media::MediaChangeReport::IO_SOFT;
+          }
+
+          // Propagate the original error if _no_ callback receiver is connected, or
+         // non_interactive mode (for optional files) is used (except for wrong media).
+          if ( ! callback::SendReport<media::MediaChangeReport>::connected()
+            || (( options & PROVIDE_NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG ) )
+          {
+              MIL << "Can't provide file. Non-Interactive mode." << endl;
+              ZYPP_RETHROW(excp);
+          }
+          else
+          {
+            // release all media before requesting another (#336881)
+            media_mgr.releaseAll();
 
-          user  = checkonly ? media::MediaChangeReport::ABORT :
-            report->requestMedia (
-              Source_Ref::noSource,
+            user = report->requestMedia (
+              _url,
               media_nr,
+              _label,
               reason,
-              excp.asUserString()
+              excp.asUserHistory(),
+              devices,
+              devindex
             );
+          }
 
-          DBG << "ProvideFile exception caught, callback answer: " << user << endl;
+          MIL << "ProvideFile exception caught, callback answer: " << user << endl;
 
           if( user == media::MediaChangeReport::ABORT )
           {
             DBG << "Aborting" << endl;
-            ZYPP_RETHROW ( excp );
+            AbortRequestException aexcp("Aborting requested by user");
+            aexcp.remember(excp);
+            ZYPP_THROW(aexcp);
           }
           else if ( user == media::MediaChangeReport::IGNORE )
           {
             DBG << "Skipping" << endl;
-            ZYPP_THROW ( source::SkipRequestedException("User-requested skipping of a file") );
-          }
+           SkipRequestException nexcp("User-requested skipping of a file");
+           nexcp.remember(excp);
+           ZYPP_THROW(nexcp);
+         }
           else if ( user == media::MediaChangeReport::EJECT )
           {
             DBG << "Eject: try to release" << endl;
-            try
-            {
-              //zypp::SourceManager::sourceManager()->releaseAllSources();
-            }
-            catch (const zypp::Exception& excpt_r)
-            {
-              ZYPP_CAUGHT(excpt_r);
-              ERR << "Failed to release all sources" << endl;
-            }
-            media_mgr.release (media, true); // one more release needed for eject
-            // FIXME: this will not work, probably
+           try
+           {
+             media_mgr.releaseAll();
+             media_mgr.release (media, devindex < devices.size() ? devices[devindex] : "");
+           }
+           catch ( const Exception & e)
+           {
+             ZYPP_CAUGHT(e);
+           }
           }
           else if ( user == media::MediaChangeReport::RETRY  ||
             user == media::MediaChangeReport::CHANGE_URL )
           {
             // retry
             DBG << "Going to try again" << endl;
+            // invalidate current media access id
+            media_mgr.close(media);
+            _medias.erase(media_nr);
 
             // not attaching, media set will do that for us
             // this could generate uncaught exception (#158620)
@@ -231,59 +332,82 @@ namespace zypp
 
       // retry or change URL
     } while( true );
+  }
 
-    return media_mgr.localPath( media, file );
+  Pathname MediaSetAccess::provideDir(const Pathname & dir,
+                                      bool recursive,
+                                      unsigned media_nr,
+                                      ProvideFileOptions options )
+  {
+    OnMediaLocation resource;
+    resource.setLocation(dir, media_nr);
+    if ( recursive )
+    {
+        ProvideDirTreeOperation op;
+        provide( boost::ref(op), resource, options, Pathname());
+        return op.result;
+    }
+    ProvideDirOperation op;
+    provide( boost::ref(op), resource, options, Pathname());
+    return op.result;
   }
 
   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
   {
+    if ( _medias.find( medianr ) != _medias.end() )
+    {
+      return _medias[medianr];
+    }
+
+    Url url( medianr > 1 ? rewriteUrl( _url, medianr ) : _url );
     media::MediaManager media_mgr;
+    media::MediaAccessId id = media_mgr.open( url, _prefAttachPoint );
+    _medias[medianr] = id;
 
-    if (medias.find(medianr) != medias.end())
+    try
     {
-      media::MediaAccessId id = medias[medianr];
-      //if (! noattach && ! media_mgr.isAttached(id))
-      //media_mgr.attach(id);
-      return id;
+      if ( _verifiers.find(medianr) != _verifiers.end() )
+      {
+        // a verifier is set for this media
+        // FIXME check the case where the verifier exists
+        // but we have no access id for the media
+        media_mgr.delVerifier( id );
+        media_mgr.addVerifier( id, _verifiers[medianr] );
+        // remove any saved verifier for this media
+        _verifiers.erase( medianr );
+      }
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      WAR << "Verifier not found" << endl;
     }
-    Url url;
-    url = rewriteUrl (_url, medianr);
-    media::MediaAccessId id = media_mgr.open(url, _path);
-    //try {
-    //  MIL << "Adding media verifier" << endl;
-    //  media_mgr.delVerifier(id);
-    //  media_mgr.addVerifier(id, _source.verifier(medianr));
-    //}
-    //catch (const Exception & excpt_r)
-    //{
-    //  ZYPP_CAUGHT(excpt_r);
-    //  WAR << "Verifier not found" << endl;
-    //}
-    medias[medianr] = id;
-    
-    //if (! noattach)
-    //  media_mgr.attach(id);
 
     return id;
   }
 
+
   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
   {
     std::string scheme = url_r.getScheme();
     if (scheme == "cd" || scheme == "dvd")
-    return url_r;
+      return url_r;
 
     DBG << "Rewriting url " << url_r << endl;
 
     if( scheme == "iso")
     {
+      // TODO the iso parameter will not be required in the future, this
+      // code has to be adapted together with the MediaISO change.
+      // maybe some MediaISOURL interface should be used.
       std::string isofile = url_r.getQueryParam("iso");
-      boost::regex e("^(.*(cd|dvd))([0-9]+)(\\.iso)$", boost::regex::icase);
-      boost::smatch what;
-      if(boost::regex_match(isofile, what, e, boost::match_extra))
+      str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
+
+      str::smatch what;
+      if(str::regex_match(isofile, what, e))
       {
         Url url( url_r);
-        isofile = what[1] + str::numstring(medianr) + what[4];
+        isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
         url.setQueryParam("iso", isofile);
         DBG << "Url rewrite result: " << url << endl;
         return url;
@@ -292,12 +416,12 @@ namespace zypp
     else
     {
       std::string pathname = url_r.getPathName();
-      boost::regex e("^(.*(cd|dvd))([0-9]+)(/?)$", boost::regex::icase);
-      boost::smatch what;
-      if(boost::regex_match(pathname, what, e, boost::match_extra))
+      str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
+      str::smatch what;
+      if(str::regex_match(pathname, what, e))
       {
         Url url( url_r);
-        pathname = what[1] + str::numstring(medianr) + what[4];
+        pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
         url.setPathName(pathname);
         DBG << "Url rewrite result: " << url << endl;
         return url;
@@ -306,40 +430,20 @@ namespace zypp
     return url_r;
   }
 
-  std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
+  void MediaSetAccess::release()
   {
-    return str;
+    DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
+    media::MediaManager manager;
+    for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
+      manager.release(m->second, "");
   }
 
-//     media::MediaVerifierRef MediaSetAccess::verifier(unsigned media_nr)
-//     { return media::MediaVerifierRef(new media::NoVerifier()); }
-
-  MediaVerifier::MediaVerifier(const std::string & vendor_r, const std::string & id_r, const media::MediaNr media_nr)
-    : _media_vendor(vendor_r)
-      , _media_id(id_r)
-      , _media_nr(media_nr)
-  {}
-
-  bool MediaVerifier::isDesiredMedia(const media::MediaAccessRef &ref)
+  std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
   {
-    if (_media_vendor.empty() || _media_id.empty())
-      return true;
-
-      Pathname media_file = "/media." + str::numstring(_media_nr) + "/media";
-      ref->provideFile (media_file);
-      media_file = ref->localPath(media_file);
-      std::ifstream str(media_file.asString().c_str());
-      std::string vendor;
-      std::string id;
-
-#warning check the stream status
-      getline(str, vendor);
-      getline(str, id);
-
-      return (vendor == _media_vendor && id == _media_id );
+    str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
+    return str;
   }
 
-
 /////////////////////////////////////////////////////////////////
-} // namespace source
+} // namespace zypp
 ///////////////////////////////////////////////////////////////////