Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / media / MediaISO.cc
index 2ee7e8f..9c07c1d 100644 (file)
@@ -9,11 +9,18 @@
 /** \file zypp/media/MediaISO.cc
  *
  */
-#include "zypp/media/MediaISO.h"
+#include <iostream>
+
 #include "zypp/base/Logger.h"
 #include "zypp/media/Mount.h"
 
-#include <iostream>
+#include "zypp/media/MediaISO.h"
+
+
+#define LOSETUP_TOOL_PATH "/sbin/losetup"
+
+using std::string;
+using std::endl;
 
 //////////////////////////////////////////////////////////////////////
 namespace zypp
@@ -62,23 +69,27 @@ namespace zypp
       zypp::Url   src;
       try
       {
+        // this percent-decodes the query parameter, it must be later encoded
+        // again before used in a Url object
         arg = _url.getQueryParam("url");
         if( arg.empty() && _isofile.dirname().absolute())
         {
           src = std::string("dir:///");
-          src.setPathName(_isofile.dirname().asString());
+          src.setPathName(url::encode(_isofile.dirname().asString(), URL_SAFE_CHARS));
           _isofile = _isofile.basename();
         }
         else
         {
-          src = arg;
+          src = url::encode(arg, URL_SAFE_CHARS);
         }
       }
       catch(const zypp::url::UrlException &e)
       {
         ZYPP_CAUGHT(e);
         ERR << "Unable to parse iso filename source media url" << std::endl;
-        ZYPP_THROW(MediaBadUrlException(_url));
+       MediaBadUrlException ne(_url);
+       ne.remember(e);
+       ZYPP_THROW(ne);
       }
       if( !src.isValid())
       {
@@ -91,12 +102,12 @@ namespace zypp
             << src.asString() << std::endl;
         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
       }
-#if 1
       else
       if( !(src.getScheme() == "hd"   ||
             src.getScheme() == "dir"  ||
             src.getScheme() == "file" ||
             src.getScheme() == "nfs"  ||
+            src.getScheme() == "nfs4" ||
             src.getScheme() == "smb"  ||
             src.getScheme() == "cifs"))
       {
@@ -104,7 +115,6 @@ namespace zypp
             << src.asString() << std::endl;
         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
       }
-#endif
 
       MediaManager manager;
 
@@ -139,13 +149,39 @@ namespace zypp
     }
 
     // ---------------------------------------------------------------
+    string MediaISO::findUnusedLoopDevice()
+    {
+      const char* argv[] =
+      {
+        LOSETUP_TOOL_PATH,
+        "-f",
+        NULL
+      };
+      ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
+
+      string out = losetup.receiveLine();
+      string device = out.substr(0, out.size() - 1); // remove the trailing endl
+      for(; out.length(); out = losetup.receiveLine())
+        DBG << "losetup: " << out;
+
+      if (losetup.close() != 0)
+      {
+        ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
+        ZYPP_THROW(MediaNoLoopDeviceException(_url));
+      }
+
+      DBG << "found " << device << endl;
+      return device;
+    }
+
+    // ---------------------------------------------------------------
     void MediaISO::attachTo(bool next)
     {
       if(next)
         ZYPP_THROW(MediaNotSupportedException(_url));
 
       MediaManager manager;
-      manager.attach(_parentId, false);
+      manager.attach(_parentId);
 
       try
       {
@@ -156,28 +192,41 @@ namespace zypp
         ZYPP_CAUGHT(e1);
         try
         {
-          manager.release(_parentId, false);
+          manager.release(_parentId);
         }
         catch(const MediaException &e2)
         {
           ZYPP_CAUGHT(e2);
         }
-        ZYPP_THROW(MediaMountException(
+
+       MediaMountException e3(
           "Unable to find iso filename on source media",
           _url.asString(), attachPoint().asString()
-        ));
+        );
+       e3.remember(e1);
+       ZYPP_THROW(e3);
       }
 
-      Pathname isofile = manager.localPath(_parentId, _isofile);
-      PathInfo isoinfo( isofile, PathInfo::LSTAT);
-      if( !isoinfo.isFile())
+      // if the provided file is a symlink, expand it (#274651)
+      // (this will probably work only for file/dir and cd/dvd schemes)
+      Pathname isofile = expandlink(manager.localPath(_parentId, _isofile));
+      if( isofile.empty() || !PathInfo(isofile).isFile())
       {
         ZYPP_THROW(MediaNotSupportedException(_url));
       }
 
-      MediaSourceRef media( new MediaSource(
-        "iso", isofile.asString()
-      ));
+      //! \todo make this thread-safe - another thread might pick up the same device
+      string loopdev = findUnusedLoopDevice(); // (bnc #428009)
+
+      MediaSourceRef media( new MediaSource("iso",  loopdev));
+      PathInfo dinfo(loopdev);
+      if( dinfo.isBlk())
+      {
+        media->maj_nr = dinfo.devMajor();
+        media->min_nr = dinfo.devMinor();
+      }
+      else
+        ERR << loopdev << " is not a block device" << endl;
 
       AttachedMedia  ret( findAttachedMedia( media));
       if( ret.mediaSource &&
@@ -195,16 +244,12 @@ namespace zypp
         return;
       }
 
-      std::string mountpoint = attachPoint().asString();
-      if( !isUseableAttachPoint(attachPoint()))
+      if( !isUseableAttachPoint( attachPoint() ) )
       {
-        mountpoint = createAttachPoint().asString();
-        if( mountpoint.empty())
-          ZYPP_THROW( MediaBadAttachPointException(url()));
-        setAttachPoint( mountpoint, true);
+       setAttachPoint( createAttachPoint(), true );
       }
-
-      std::string mountopts("ro,loop");
+      std::string mountpoint( attachPoint().asString() );
+      std::string mountopts("ro,loop=" + loopdev);
 
       Mount mount;
       mount.mount(isofile.asString(), mountpoint,
@@ -233,22 +278,34 @@ namespace zypp
         {
           ZYPP_CAUGHT(excpt_r);
         }
-        ZYPP_THROW(MediaMountException(isofile.asString(), mountpoint,
-          "Unable to verify that the media was mounted"
+        ZYPP_THROW(MediaMountException(
+          "Unable to verify that the media was mounted",
+          isofile.asString(), mountpoint
         ));
       }
     }
 
     // ---------------------------------------------------------------
-    void MediaISO::releaseFrom(bool eject)
+
+    void MediaISO::releaseFrom(const std::string & ejectDev)
     {
       Mount mount;
       mount.umount(attachPoint().asString());
 
       if( _parentId)
       {
+        // Unmounting the iso already succeeded,
+        // so don't let exceptions escape.
         MediaManager manager;
-        manager.release(_parentId);
+        try
+        {
+          manager.release(_parentId);
+        }
+        catch ( const Exception & excpt_r )
+        {
+          ZYPP_CAUGHT( excpt_r );
+          WAR << "Not been able to cleanup the parent mount." << endl;
+        }
       }
       // else:
       // the media manager has reset the _parentId
@@ -284,6 +341,10 @@ namespace zypp
       MediaHandler::getDirInfo(retlist, dirname, dots);
     }
 
+    bool MediaISO::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
 
     //////////////////////////////////////////////////////////////////
   } // namespace media