libzypp  10.5.0
MediaISO.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 
00014 #include "zypp/base/Logger.h"
00015 #include "zypp/media/Mount.h"
00016 
00017 #include "zypp/media/MediaISO.h"
00018 
00019 
00020 #define LOSETUP_TOOL_PATH "/sbin/losetup"
00021 
00022 using std::string;
00023 using std::endl;
00024 
00026 namespace zypp
00027 { 
00028 
00030   namespace media
00031   { 
00032 
00034     //
00035     // MediaISO Url:
00036     //
00037     //   Schema: iso
00038     //   Path name: subdir to the location of desired files inside
00039     //              of the ISO.
00040     //   Query parameters:
00041     //     url:        The iso filename source media url pointing
00042     //                 to a directory containing the ISO file.
00043     //     mnt:        Prefered attach point for source media url.
00044     //     iso:        The name of the iso file.
00045     //     filesystem: Optional, defaults to "auto".
00046     //
00048     MediaISO::MediaISO(const Url      &url_r,
00049                        const Pathname &attach_point_hint_r)
00050       : MediaHandler(url_r, attach_point_hint_r,
00051                      url_r.getPathName(), // urlpath below attachpoint
00052                      false)               // does_download
00053     {
00054       MIL << "MediaISO::MediaISO(" << url_r << ", "
00055           << attach_point_hint_r << ")" << std::endl;
00056 
00057       _isofile    = _url.getQueryParam("iso");
00058       if( _isofile.empty())
00059       {
00060         ERR << "Media url does not contain iso filename" << std::endl;
00061         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00062       }
00063 
00064       _filesystem = _url.getQueryParam("filesystem");
00065       if( _filesystem.empty())
00066         _filesystem = "auto";
00067 
00068       std::string arg;
00069       zypp::Url   src;
00070       try
00071       {
00072         // this percent-decodes the query parameter, it must be later encoded
00073         // again before used in a Url object
00074         arg = _url.getQueryParam("url");
00075         if( arg.empty() && _isofile.dirname().absolute())
00076         {
00077           src = std::string("dir:///");
00078           src.setPathName(url::encode(_isofile.dirname().asString(), URL_SAFE_CHARS));
00079           _isofile = _isofile.basename();
00080         }
00081         else
00082         {
00083           src = url::encode(arg, URL_SAFE_CHARS);
00084         }
00085       }
00086       catch(const zypp::url::UrlException &e)
00087       {
00088         ZYPP_CAUGHT(e);
00089         ERR << "Unable to parse iso filename source media url" << std::endl;
00090         MediaBadUrlException ne(_url);
00091         ne.remember(e);
00092         ZYPP_THROW(ne);
00093       }
00094       if( !src.isValid())
00095       {
00096         ERR << "Invalid iso filename source media url" << std::endl;
00097         ZYPP_THROW(MediaBadUrlException(src));
00098       }
00099       if( src.getScheme() == "iso")
00100       {
00101         ERR << "ISO filename source media url with iso scheme (nested iso): "
00102             << src.asString() << std::endl;
00103         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
00104       }
00105       else
00106       if( !(src.getScheme() == "hd"   ||
00107             src.getScheme() == "dir"  ||
00108             src.getScheme() == "file" ||
00109             src.getScheme() == "nfs"  ||
00110             src.getScheme() == "nfs4" ||
00111             src.getScheme() == "smb"  ||
00112             src.getScheme() == "cifs"))
00113       {
00114         ERR << "ISO filename source media url scheme is not supported: "
00115             << src.asString() << std::endl;
00116         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
00117       }
00118 
00119       MediaManager manager;
00120 
00121       _parentId = manager.open(src, _url.getQueryParam("mnt"));
00122     }
00123 
00124     // ---------------------------------------------------------------
00125     MediaISO::~MediaISO()
00126     {
00127       try
00128       {
00129         release();
00130 
00131         if( _parentId)
00132         {
00133           DBG << "Closing parent handler..." << std::endl;
00134           MediaManager manager;
00135           if(manager.isOpen(_parentId))
00136             manager.close(_parentId);
00137           _parentId = 0;
00138         }
00139       }
00140       catch( ... )
00141       {}
00142     }
00143 
00144     // ---------------------------------------------------------------
00145     bool
00146     MediaISO::isAttached() const
00147     {
00148       return checkAttached(false);
00149     }
00150 
00151     // ---------------------------------------------------------------
00152     string MediaISO::findUnusedLoopDevice()
00153     {
00154       const char* argv[] =
00155       {
00156         LOSETUP_TOOL_PATH,
00157         "-f",
00158         NULL
00159       };
00160       ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
00161 
00162       string out = losetup.receiveLine();
00163       string device = out.substr(0, out.size() - 1); // remove the trailing endl
00164       for(; out.length(); out = losetup.receiveLine())
00165         DBG << "losetup: " << out;
00166 
00167       if (losetup.close() != 0)
00168       {
00169         ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
00170         ZYPP_THROW(MediaNoLoopDeviceException(_url));
00171       }
00172 
00173       DBG << "found " << device << endl;
00174       return device;
00175     }
00176 
00177     // ---------------------------------------------------------------
00178     void MediaISO::attachTo(bool next)
00179     {
00180       if(next)
00181         ZYPP_THROW(MediaNotSupportedException(_url));
00182 
00183       MediaManager manager;
00184       manager.attach(_parentId);
00185 
00186       try
00187       {
00188         manager.provideFile(_parentId, _isofile);
00189       }
00190       catch(const MediaException &e1)
00191       {
00192         ZYPP_CAUGHT(e1);
00193         try
00194         {
00195           manager.release(_parentId);
00196         }
00197         catch(const MediaException &e2)
00198         {
00199           ZYPP_CAUGHT(e2);
00200         }
00201 
00202         MediaMountException e3(
00203           "Unable to find iso filename on source media",
00204           _url.asString(), attachPoint().asString()
00205         );
00206         e3.remember(e1);
00207         ZYPP_THROW(e3);
00208       }
00209 
00210       // if the provided file is a symlink, expand it (#274651)
00211       // (this will probably work only for file/dir and cd/dvd schemes)
00212       Pathname isofile = expandlink(manager.localPath(_parentId, _isofile));
00213       if( isofile.empty() || !PathInfo(isofile).isFile())
00214       {
00215         ZYPP_THROW(MediaNotSupportedException(_url));
00216       }
00217 
00219       string loopdev = findUnusedLoopDevice(); // (bnc #428009)
00220 
00221       MediaSourceRef media( new MediaSource("iso",  loopdev));
00222       PathInfo dinfo(loopdev);
00223       if( dinfo.isBlk())
00224       {
00225         media->maj_nr = dinfo.major();
00226         media->min_nr = dinfo.minor();
00227       }
00228       else
00229         ERR << loopdev << " is not a block device" << endl;
00230 
00231       AttachedMedia  ret( findAttachedMedia( media));
00232       if( ret.mediaSource &&
00233           ret.attachPoint &&
00234           !ret.attachPoint->empty())
00235       {
00236         DBG << "Using a shared media "
00237             << ret.mediaSource->name
00238             << " attached on "
00239             << ret.attachPoint->path
00240             << std::endl;
00241         removeAttachPoint();
00242         setAttachPoint(ret.attachPoint);
00243         setMediaSource(ret.mediaSource);
00244         return;
00245       }
00246 
00247       std::string mountpoint = attachPoint().asString();
00248       if( !isUseableAttachPoint(attachPoint()))
00249       {
00250         mountpoint = createAttachPoint().asString();
00251         if( mountpoint.empty())
00252           ZYPP_THROW( MediaBadAttachPointException(url()));
00253         setAttachPoint( mountpoint, true);
00254       }
00255 
00256       std::string mountopts("ro,loop=" + loopdev);
00257 
00258       Mount mount;
00259       mount.mount(isofile.asString(), mountpoint,
00260                   _filesystem, mountopts);
00261 
00262       setMediaSource(media);
00263 
00264       // wait for /etc/mtab update ...
00265       // (shouldn't be needed)
00266       int limit = 3;
00267       bool mountsucceeded;
00268       while( !(mountsucceeded=isAttached()) && --limit)
00269       {
00270         sleep(1);
00271       }
00272 
00273       if( !mountsucceeded)
00274       {
00275         setMediaSource(MediaSourceRef());
00276         try
00277         {
00278           mount.umount(attachPoint().asString());
00279           manager.release(_parentId);
00280         }
00281         catch (const MediaException & excpt_r)
00282         {
00283           ZYPP_CAUGHT(excpt_r);
00284         }
00285         ZYPP_THROW(MediaMountException(
00286           "Unable to verify that the media was mounted",
00287           isofile.asString(), mountpoint
00288         ));
00289       }
00290     }
00291 
00292     // ---------------------------------------------------------------
00293 
00294     void MediaISO::releaseFrom(const std::string & ejectDev)
00295     {
00296       Mount mount;
00297       mount.umount(attachPoint().asString());
00298 
00299       if( _parentId)
00300       {
00301         // Unmounting the iso already succeeded,
00302         // so don't let exceptions escape.
00303         MediaManager manager;
00304         try
00305         {
00306           manager.release(_parentId);
00307         }
00308         catch ( const Exception & excpt_r )
00309         {
00310           ZYPP_CAUGHT( excpt_r );
00311           WAR << "Not been able to cleanup the parent mount." << endl;
00312         }
00313       }
00314       // else:
00315       // the media manager has reset the _parentId
00316       // and will destroy the parent handler itself.
00317     }
00318 
00319     // ---------------------------------------------------------------
00320     void MediaISO::getFile(const Pathname &filename) const
00321     {
00322       MediaHandler::getFile(filename);
00323     }
00324 
00325     // ---------------------------------------------------------------
00326     void MediaISO::getDir(const Pathname &dirname,
00327                            bool            recurse_r) const
00328     {
00329       MediaHandler::getDir(dirname, recurse_r);
00330     }
00331 
00332     // ---------------------------------------------------------------
00333     void MediaISO::getDirInfo(std::list<std::string> &retlist,
00334                                const Pathname         &dirname,
00335                                bool                    dots) const
00336     {
00337       MediaHandler::getDirInfo( retlist, dirname, dots );
00338     }
00339 
00340     // ---------------------------------------------------------------
00341     void MediaISO::getDirInfo(filesystem::DirContent &retlist,
00342                                const Pathname         &dirname,
00343                                bool                    dots) const
00344     {
00345       MediaHandler::getDirInfo(retlist, dirname, dots);
00346     }
00347 
00348     bool MediaISO::getDoesFileExist( const Pathname & filename ) const
00349     {
00350       return MediaHandler::getDoesFileExist( filename );
00351     }
00352 
00354   } // namespace media
00356 
00358 } // namespace zypp
00360 
00361 // vim: set ts=2 sts=2 sw=2 ai et:
00362