MediaDISK.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include "zypp/base/Logger.h"
00014 #include "zypp/base/String.h"
00015 #include "zypp/media/Mount.h"
00016 #include "zypp/media/MediaDISK.h"
00017 #include "zypp/media/MediaManager.h"
00018 
00019 #include <iostream>
00020 #include <fstream>
00021 #include <sstream>
00022 
00023 #include <sys/types.h>
00024 #include <sys/mount.h>
00025 #include <errno.h>
00026 #include <dirent.h>
00027 
00028 /*
00029 ** verify devices names as late as possible (while attach)
00030 */
00031 #define DELAYED_VERIFY           1
00032 
00033 /*
00034 ** Reuse foreign (user/automounter) mount points.
00035 ** 0 = don't use, 1 = automounted only, 2 = all
00036 */
00037 #define  REUSE_FOREIGN_MOUNTS    2
00038 
00039 /*
00040 ** Path to the vol_id tool (normal system, instsys)
00041 */
00042 #define VOL_ID_TOOL_PATHS        { "/sbin/vol_id", "/lib/udev/vol_id", NULL}
00043 
00044 using namespace std;
00045 
00046 namespace zypp {
00047   namespace media {
00048 
00050     //
00051     //  CLASS NAME : MediaDISK
00052     //
00054 
00056     //
00057     //
00058     //  METHOD NAME : MediaDISK::MediaDISK
00059     //  METHOD TYPE : Constructor
00060     //
00061     //  DESCRIPTION :
00062     //
00063     MediaDISK::MediaDISK( const Url &      url_r,
00064                           const Pathname & attach_point_hint_r )
00065         : MediaHandler( url_r, attach_point_hint_r,
00066                     url_r.getPathName(), // urlpath below attachpoint
00067                     false ) // does_download
00068     {
00069       MIL << "MediaDISK::MediaDISK(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00070 
00071       _device = Pathname(_url.getQueryParam("device")).asString();
00072       if( _device.empty())
00073       {
00074         ERR << "Media url does not contain a device specification" << std::endl;
00075         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00076       }
00077 #if DELAYED_VERIFY
00078       DBG << "Verify of " << _device << " delayed" << std::endl;
00079 #else
00080       if( !verifyIfDiskVolume( _device))
00081       {
00082         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00083       }
00084 #endif
00085 
00086       _filesystem = _url.getQueryParam("filesystem");
00087       if(_filesystem.empty())
00088         _filesystem="auto";
00089 
00090     }
00091 
00093     //
00094     //  METHOD NAME : MediaDISK::verifyIfDiskVolume
00095     //  METHOD TYPE : void
00096     //
00097     //  DESCRIPTION : Check if specified device file name is
00098     //                a disk volume device or throw an error.
00099     //
00100     bool MediaDISK::verifyIfDiskVolume(const Pathname &dev_name)
00101     {
00102       if( dev_name.empty() ||
00103           dev_name.asString().compare(0, sizeof("/dev/")-1, "/dev/"))
00104       {
00105         ERR << "Specified device name " << dev_name
00106             << " is not allowed" << std::endl;
00107         return false;
00108       }
00109 
00110       PathInfo dev_info(dev_name);
00111       if( !dev_info.isBlk())
00112       {
00113         ERR << "Specified device name " << dev_name
00114             << " is not a block device" << std::endl;
00115         return false;
00116       }
00117 
00118       // check if a volume using /dev/disk/by-uuid links first
00119       {
00120         Pathname            dpath("/dev/disk/by-uuid");
00121         std::list<Pathname> dlist;
00122         if( zypp::filesystem::readdir(dlist, dpath) == 0)
00123         {
00124           std::list<Pathname>::const_iterator it;
00125           for(it = dlist.begin(); it != dlist.end(); ++it)
00126           {
00127             PathInfo vol_info(*it);
00128             if( vol_info.isBlk() && vol_info.major() == dev_info.major() &&
00129                                     vol_info.minor() == dev_info.minor())
00130             {
00131               DBG << "Specified device name " << dev_name
00132                   << " is a volume (disk/by-uuid link "
00133                   << vol_info.path() << ")"
00134                   << std::endl;
00135               return true;
00136             }
00137           }
00138         }
00139       }
00140 
00141       // check if a volume using /dev/disk/by-label links
00142       // (e.g. vbd mapped volumes in a XEN vm)
00143       {
00144         Pathname            dpath("/dev/disk/by-label");
00145         std::list<Pathname> dlist;
00146         if( zypp::filesystem::readdir(dlist, dpath) == 0)
00147         {
00148           std::list<Pathname>::const_iterator it;
00149           for(it = dlist.begin(); it != dlist.end(); ++it)
00150           {
00151             PathInfo vol_info(*it);
00152             if( vol_info.isBlk() && vol_info.major() == dev_info.major() &&
00153                                     vol_info.minor() == dev_info.minor())
00154             {
00155               DBG << "Specified device name " << dev_name
00156                   << " is a volume (disk/by-label link "
00157                   << vol_info.path() << ")"
00158                   << std::endl;
00159               return true;
00160             }
00161           }
00162         }
00163       }
00164 
00165       // check if a filesystem volume using the /sbin/vol_id tool
00166       // (there is no /dev/disk link for some of them)
00167       for(const char *vol_id_paths[] = VOL_ID_TOOL_PATHS,
00168                      **vol_id_path    = vol_id_paths;
00169           vol_id_path != NULL && *vol_id_path != NULL;
00170           vol_id_path++)
00171       {
00172         PathInfo vol_id_info(*vol_id_path);
00173         if( !vol_id_info.isFile() || !vol_id_info.isXUsr())
00174           continue;
00175 
00176         const char *cmd[3];
00177         cmd[0] = *vol_id_path;
00178         cmd[1] = dev_name.asString().c_str();
00179         cmd[2] = NULL;
00180 
00181         ExternalProgram vol_id(cmd, ExternalProgram::Stderr_To_Stdout);
00182 
00183         std::string vol_fs_usage;
00184         std::string vol_fs_uuid;
00185         std::string vol_fs_type;
00186 
00187         for(std::string out( vol_id.receiveLine());
00188             out.length(); out = vol_id.receiveLine())
00189         {
00190           out = str::rtrim(out);
00191 
00192           if( out.compare(0, sizeof("ID_FS_USAGE=")-1, "ID_FS_USAGE=") == 0)
00193           {
00194             vol_fs_usage = out.substr(sizeof("ID_FS_USAGE=")-1);
00195           }
00196           else
00197           if( out.compare(0, sizeof("ID_FS_TYPE=")-1, "ID_FS_TYPE=")   == 0)
00198           {
00199             vol_fs_type  = out.substr(sizeof("ID_FS_TYPE=")-1);
00200           }
00201           else
00202           if( out.compare(0, sizeof("ID_FS_UUID=")-1, "ID_FS_UUID=")   == 0)
00203           {
00204             vol_fs_uuid  = out.substr(sizeof("ID_FS_UUID=")-1);
00205           }
00206         }
00207 
00208         if( vol_id.close() == 0)
00209         {
00210           if( vol_fs_usage == "filesystem")
00211           {
00212             if(vol_fs_type == "iso9660" || vol_fs_type == "udf")
00213             {
00214               DBG << "Specified device name " << dev_name
00215                   << " is a CD/DVD volume (type " << vol_fs_type << ")"
00216                   << std::endl;
00217               return true;
00218             }
00219             else
00220             if(!vol_fs_type.empty() && !vol_fs_uuid.empty())
00221             {
00222               DBG << "Specified device name " << dev_name
00223                   << " is a volume (type " << vol_fs_type
00224                   << ", uuid " << vol_fs_uuid << ")"
00225                   << std::endl;
00226               return true;
00227             }
00228           }
00229         }
00230       }
00231 
00232       ERR << "Specified device name " << dev_name
00233           << " is not a usable disk volume"
00234           << std::endl;
00235       return false;
00236     }
00237 
00239     //
00240     //
00241     //  METHOD NAME : MediaDISK::attachTo
00242     //  METHOD TYPE : PMError
00243     //
00244     //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
00245     //
00246     void MediaDISK::attachTo(bool next)
00247     {
00248       if(next)
00249         ZYPP_THROW(MediaNotSupportedException(url()));
00250       // FIXME
00251       // do mount --bind <partition>/<dir> to <to>
00252       //   mount /dev/<partition> /tmp_mount
00253       //   mount /tmp_mount/<dir> <to> --bind -o ro
00254       // FIXME: try all filesystems
00255     
00256       if(_device.empty())
00257         ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
00258     
00259       PathInfo dev_info(_device);
00260       if(!dev_info.isBlk())
00261         ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
00262 #if DELAYED_VERIFY
00263       DBG << "Verifying " << _device << " ..." << std::endl;
00264       if( !verifyIfDiskVolume( _device))
00265       {
00266         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00267       }
00268 #endif
00269 
00270       if(_filesystem.empty())
00271         ZYPP_THROW(MediaBadUrlEmptyFilesystemException(url()));
00272 
00273       MediaSourceRef media( new MediaSource(
00274         "disk", _device, dev_info.major(), dev_info.minor()
00275       ));
00276       AttachedMedia  ret( findAttachedMedia( media));
00277 
00278       if( ret.mediaSource &&
00279           ret.attachPoint &&
00280           !ret.attachPoint->empty())
00281       {
00282         DBG << "Using a shared media "
00283             << ret.mediaSource->name
00284             << " attached on "
00285             << ret.attachPoint->path
00286             << endl;
00287 
00288         removeAttachPoint();
00289         setAttachPoint(ret.attachPoint);
00290         setMediaSource(ret.mediaSource);
00291         return;
00292       }
00293 
00294       MediaManager  manager;
00295       MountEntries  entries( manager.getMountEntries());
00296       MountEntries::const_iterator e;
00297       for( e = entries.begin(); e != entries.end(); ++e)
00298       {
00299         bool        is_device = false;
00300         std::string dev_path(Pathname(e->src).asString());
00301         PathInfo    dev_info;
00302 
00303         if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
00304             dev_info(e->src) && dev_info.isBlk())
00305         {
00306           is_device = true;
00307         }
00308 
00309         if( is_device && media->maj_nr == dev_info.major() &&
00310                          media->min_nr == dev_info.minor())
00311         {
00312 #if REUSE_FOREIGN_MOUNTS > 0
00313           AttachPointRef ap( new AttachPoint(e->dir, false));
00314           AttachedMedia  am( media, ap);
00315           //
00316           // 1 = automounted only, 2 == all
00317           //
00318 #if REUSE_FOREIGN_MOUNTS == 1
00319           if( isAutoMountedMedia(am))
00320 #endif
00321           {
00322             DBG << "Using a system mounted media "
00323                 << media->name
00324                 << " attached on "
00325                 << ap->path
00326                 << endl;
00327 
00328             media->iown = false; // mark attachment as foreign
00329 
00330             setMediaSource(media);
00331             setAttachPoint(ap);
00332             return;
00333           }
00334 #else
00335           media->bdir = e->dir;
00336 #endif
00337         }
00338       }
00339 
00340       Mount mount;
00341       std::string mountpoint = attachPoint().asString();
00342       if( !isUseableAttachPoint(attachPoint()))
00343       {
00344         mountpoint = createAttachPoint().asString();
00345         if( mountpoint.empty())
00346           ZYPP_THROW( MediaBadAttachPointException(url()));
00347         setAttachPoint( mountpoint, true);
00348       }
00349 
00350       string options = _url.getQueryParam("mountoptions");
00351       if(options.empty())
00352       {
00353         options = "ro";
00354       }
00355 
00356       if( !media->bdir.empty())
00357       {
00358         options += ",bind";
00359         mount.mount(media->bdir, mountpoint, "none", options);
00360       }
00361       else
00362       {
00363         mount.mount(_device, mountpoint, _filesystem, options);
00364       }
00365 
00366       setMediaSource(media);
00367 
00368       // wait for /etc/mtab update ...
00369       // (shouldn't be needed)
00370       int limit = 3;
00371       bool mountsucceeded;
00372       while( !(mountsucceeded=isAttached()) && --limit)
00373       {
00374         sleep(1);
00375       }
00376 
00377       if( !mountsucceeded)
00378       {
00379         setMediaSource(MediaSourceRef());
00380         try
00381         {
00382           mount.umount(attachPoint().asString());
00383         }
00384         catch (const MediaException & excpt_r)
00385         {
00386           ZYPP_CAUGHT(excpt_r);
00387         }
00388         ZYPP_THROW(MediaMountException(
00389           "Unable to verify that the media was mounted",
00390           _device, mountpoint
00391         ));
00392       }
00393     }
00394 
00396     //
00397     //  METHOD NAME : MediaDISK::isAttached
00398     //  METHOD TYPE : bool
00399     //
00400     //  DESCRIPTION : Override check if media is attached.
00401     //
00402     bool
00403     MediaDISK::isAttached() const
00404     {
00405       return checkAttached(false);
00406     }
00407 
00409     //
00410     //
00411     //  METHOD NAME : MediaDISK::releaseFrom
00412     //  METHOD TYPE : PMError
00413     //
00414     //  DESCRIPTION : Asserted that media is attached.
00415     //
00416     void MediaDISK::releaseFrom( const std::string & ejectDev )
00417     {
00418       AttachedMedia am( attachedMedia());
00419       if(am.mediaSource && am.mediaSource->iown)
00420       {
00421         Mount mount;
00422         mount.umount(attachPoint().asString());
00423       }
00424     }
00425 
00427     //
00428     //  METHOD NAME : MediaDISK::getFile
00429     //  METHOD TYPE : PMError
00430     //
00431     //  DESCRIPTION : Asserted that media is attached.
00432     //
00433     void MediaDISK::getFile (const Pathname & filename) const
00434     {
00435       MediaHandler::getFile( filename );
00436     }
00437     
00439     //
00440     //  METHOD NAME : MediaDISK::getDir
00441     //  METHOD TYPE : PMError
00442     //
00443     //  DESCRIPTION : Asserted that media is attached.
00444     //
00445     void MediaDISK::getDir( const Pathname & dirname, bool recurse_r ) const
00446     {
00447       MediaHandler::getDir( dirname, recurse_r );
00448     }
00449     
00451     //
00452     //
00453     //  METHOD NAME : MediaDISK::getDirInfo
00454     //  METHOD TYPE : PMError
00455     //
00456     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00457     //
00458     void MediaDISK::getDirInfo( std::list<std::string> & retlist,
00459                                 const Pathname & dirname, bool dots ) const
00460     {
00461       MediaHandler::getDirInfo( retlist, dirname, dots );
00462     }
00463     
00465     //
00466     //
00467     //  METHOD NAME : MediaDISK::getDirInfo
00468     //  METHOD TYPE : PMError
00469     //
00470     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00471     //
00472     void MediaDISK::getDirInfo( filesystem::DirContent & retlist,
00473                                 const Pathname & dirname, bool dots ) const
00474     {
00475       MediaHandler::getDirInfo( retlist, dirname, dots );
00476     }
00477 
00478     bool MediaDISK::getDoesFileExist( const Pathname & filename ) const
00479     {
00480       return MediaHandler::getDoesFileExist( filename );
00481     }    
00482     
00483   } // namespace media
00484 } // namespace zypp
00485 // vim: set ts=8 sts=2 sw=2 ai noet:

doxygen