Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / media / MediaISO.cc
index 7d9fa98..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
@@ -44,6 +51,9 @@ namespace zypp
                      url_r.getPathName(), // urlpath below attachpoint
                      false)               // does_download
     {
+      MIL << "MediaISO::MediaISO(" << url_r << ", "
+          << attach_point_hint_r << ")" << std::endl;
+
       _isofile    = _url.getQueryParam("iso");
       if( _isofile.empty())
       {
@@ -55,16 +65,31 @@ namespace zypp
       if( _filesystem.empty())
         _filesystem = "auto";
 
-      zypp::Url src;
+      std::string arg;
+      zypp::Url   src;
       try
       {
-        src = _url.getQueryParam("url");
+        // 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(url::encode(_isofile.dirname().asString(), URL_SAFE_CHARS));
+          _isofile = _isofile.basename();
+        }
+        else
+        {
+          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())
       {
@@ -77,27 +102,23 @@ namespace zypp
             << src.asString() << std::endl;
         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
       }
-#if 0
-      /*
-      ** FIXME: should we really support this?
-      */
       else
-      if( src.getScheme() == "ftp"   ||
-          src.getScheme() == "http"  ||
-          src.getScheme() == "https")
+      if( !(src.getScheme() == "hd"   ||
+            src.getScheme() == "dir"  ||
+            src.getScheme() == "file" ||
+            src.getScheme() == "nfs"  ||
+            src.getScheme() == "nfs4" ||
+            src.getScheme() == "smb"  ||
+            src.getScheme() == "cifs"))
       {
         ERR << "ISO filename source media url scheme is not supported: "
-            << src.asString() << endl;
+            << src.asString() << std::endl;
         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
       }
-#endif
 
       MediaManager manager;
 
       _parentId = manager.open(src, _url.getQueryParam("mnt"));
-
-      MIL << "MediaISO::MediaISO(" << url_r << ", "
-          << attach_point_hint_r << ")" << std::endl;
     }
 
     // ---------------------------------------------------------------
@@ -106,6 +127,15 @@ namespace zypp
       try
       {
         release();
+
+        if( _parentId)
+        {
+          DBG << "Closing parent handler..." << std::endl;
+          MediaManager manager;
+          if(manager.isOpen(_parentId))
+            manager.close(_parentId);
+          _parentId = 0;
+        }
       }
       catch( ... )
       {}
@@ -115,9 +145,33 @@ namespace zypp
     bool
     MediaISO::isAttached() const
     {
-      MediaManager manager;
-      return checkAttached(false, false);
-      // FIXME: what's about manager.isAttached(_parentId) ?
+      return checkAttached(false);
+    }
+
+    // ---------------------------------------------------------------
+    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;
     }
 
     // ---------------------------------------------------------------
@@ -127,7 +181,7 @@ namespace zypp
         ZYPP_THROW(MediaNotSupportedException(_url));
 
       MediaManager manager;
-      manager.attach(_parentId, false);
+      manager.attach(_parentId);
 
       try
       {
@@ -138,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 &&
@@ -177,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,
@@ -196,9 +259,9 @@ namespace zypp
 
       // wait for /etc/mtab update ...
       // (shouldn't be needed)
-      int limit = 10;
+      int limit = 3;
       bool mountsucceeded;
-      while( !(mountsucceeded=isAttached()) && limit--)
+      while( !(mountsucceeded=isAttached()) && --limit)
       {
         sleep(1);
       }
@@ -215,20 +278,38 @@ 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());
 
-      MediaManager manager;
-      manager.release(_parentId);
+      if( _parentId)
+      {
+        // Unmounting the iso already succeeded,
+        // so don't let exceptions escape.
+        MediaManager manager;
+        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
+      // and will destroy the parent handler itself.
     }
 
     // ---------------------------------------------------------------
@@ -260,6 +341,10 @@ namespace zypp
       MediaHandler::getDirInfo(retlist, dirname, dots);
     }
 
+    bool MediaISO::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
 
     //////////////////////////////////////////////////////////////////
   } // namespace media