libzypp 9.41.1

Mount.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <mntent.h>
00014 
00015 #include <cstdio>
00016 #include <climits>
00017 #include <cerrno>
00018 
00019 #include <iostream>
00020 #include <fstream>
00021 #include <string>
00022 
00023 #include "zypp/base/ExternalDataSource.h"
00024 #include "zypp/base/Logger.h"
00025 #include "zypp/media/Mount.h"
00026 #include "zypp/media/MediaException.h"
00027 
00028 #include "zypp/PathInfo.h"
00029 
00030 #ifndef N_
00031 #define N_(STR) STR
00032 #endif
00033 
00034 using namespace std;
00035 
00036 #define BNC925501 1
00037 
00038 namespace zypp {
00039   namespace media {
00040 #if ( BNC925501 )
00041     namespace
00042     {
00043       bool bnc925501ExtraDebug = false;
00044     }
00045 #endif
00046 
00047     std::ostream & operator<<( std::ostream & str, const MountEntry & obj )
00048     {
00049       str << obj.src << " on " << obj.dir << " type " << obj.type;
00050       if ( ! obj.opts.empty() )
00051         str << " (" << obj.opts << ")";
00052       return str;
00053     }
00054 
00055 
00056 Mount::Mount()
00057 {
00058     process = 0;
00059     exit_code = -1;
00060 }
00061 
00062 Mount::~Mount()
00063 {
00064    MIL <<  "~Mount()" << endl;
00065 
00066    if ( process )
00067       delete process;
00068 
00069    process = NULL;
00070 
00071    MIL << "~Mount() end" << endl;
00072 }
00073 
00074 void Mount::mount( const std::string & source,
00075                    const std::string & target,
00076                    const std::string & filesystem,
00077                    const std::string & options,
00078                    const Environment & environment )
00079 {
00080     const char *const argv[] = {
00081         "/bin/mount",
00082         "-t", filesystem.c_str(),
00083         "-o", options.c_str(),
00084         source.c_str(),
00085         target.c_str(),
00086         NULL
00087      };
00088 
00089     std::string err;
00090 
00091     this->run(argv, environment, ExternalProgram::Stderr_To_Stdout);
00092 
00093     if ( process == NULL )
00094     {
00095       ZYPP_THROW(MediaMountException("Mounting media failed", source, target));
00096     }
00097 
00098     string value;
00099     string output = process->receiveLine();
00100 
00101     // parse error messages
00102     while ( output.length() > 0)
00103     {
00104         string::size_type       ret;
00105 
00106         // extract \n
00107         ret = output.find_first_of ( "\n" );
00108         if ( ret != string::npos )
00109         {
00110             value.assign ( output, 0, ret );
00111         }
00112         else
00113         {
00114             value = output;
00115         }
00116 
00117         DBG << "stdout: " << value << endl;
00118 
00119         if  ( value.find ( "is already mounted on" ) != string::npos )
00120         {
00121             err = "Media already mounted";
00122         }
00123         else if  ( value.find ( "ermission denied" ) != string::npos )
00124         {
00125             err = "Permission denied";
00126         }
00127         else if  ( value.find ( "wrong fs type" ) != string::npos )
00128         {
00129             err = "Invalid filesystem on media";
00130         }
00131         else if  ( value.find ( "No medium found" ) != string::npos )
00132         {
00133             err = "No medium found";
00134         }
00135         else if  ( value.find ( "Not a directory" ) != string::npos )
00136         {
00137             if( filesystem == "nfs" || filesystem == "nfs4" )
00138             {
00139                 err = "Nfs path is not a directory";
00140             }
00141             else
00142             {
00143                err = "Unable to find directory on the media";
00144             }
00145         }
00146 #if ( BNC925501 )
00147         else if ( value.find ( "already mounted or" ) )
00148         {
00149           bnc925501ExtraDebug = true;
00150         }
00151 #endif
00152 
00153         output = process->receiveLine();
00154     }
00155 
00156     int status = Status();
00157 
00158     if ( status == 0 )
00159     {
00160         // return codes overwites parsed error message
00161         err = "";
00162     }
00163     else if ( status != 0 && err == "" )
00164     {
00165         err = "Mounting media failed";
00166     }
00167 
00168     if ( err != "" ) {
00169       WAR << "mount " << source << " " << target << ": " << err << endl;
00170 #if ( BNC925501 )
00171       if ( bnc925501ExtraDebug )
00172       {
00173         Mount::getEntries();
00174       }
00175 #endif
00176       ZYPP_THROW(MediaMountException(err, source, target, value));
00177     } else {
00178       MIL << "mounted " << source << " " << target << endl;
00179     }
00180 }
00181 
00182 void Mount::umount( const std::string & path )
00183 {
00184     const char *const argv[] = {
00185         "/bin/umount",
00186         path.c_str(),
00187         NULL
00188      };
00189 
00190     std::string err;
00191 
00192     this->run(argv, ExternalProgram::Stderr_To_Stdout);
00193 
00194     if ( process == NULL )
00195     {
00196         ZYPP_THROW(MediaUnmountException("E_mount_failed", path));
00197     }
00198 
00199     string value;
00200     string output = process->receiveLine();
00201 
00202     // parse error messages
00203     while ( output.length() > 0)
00204     {
00205         string::size_type       ret;
00206 
00207         // extract \n
00208         ret = output.find_first_of ( "\n" );
00209         if ( ret != string::npos )
00210         {
00211             value.assign ( output, 0, ret );
00212         }
00213         else
00214         {
00215             value = output;
00216         }
00217 
00218         DBG << "stdout: " << value << endl;
00219 
00220         // if  ( value.find ( "not mounted" ) != string::npos )
00221         // {
00222         //    err = Error::E_already_mounted;
00223         // }
00224 
00225         if  ( value.find ( "device is busy" ) != string::npos )
00226         {
00227             err = "Device is busy";
00228         }
00229 
00230         output = process->receiveLine();
00231     }
00232 
00233     int status = Status();
00234 
00235     if ( status == 0 )
00236     {
00237         // return codes overwites parsed error message
00238         err = "";
00239     }
00240     else if ( status != 0 && err == "" )
00241     {
00242         err = "Unmounting media failed";
00243     }
00244 
00245     if ( err != "") {
00246       WAR << "umount " << path << ": " << err << endl;
00247       ZYPP_THROW(MediaUnmountException(err, path));
00248     } else {
00249       MIL << "unmounted " << path << endl;
00250     }
00251 }
00252 
00253 void Mount::run( const char *const *argv, const Environment& environment,
00254                  ExternalProgram::Stderr_Disposition disp )
00255 {
00256   exit_code = -1;
00257 
00258   if ( process != NULL )
00259   {
00260      delete process;
00261      process = NULL;
00262   }
00263   // Launch the program
00264 
00265   process = new ExternalProgram(argv, environment, disp, false, -1, true);
00266 }
00267 
00268 /*--------------------------------------------------------------*/
00269 /* Return the exit status of the Mount process, closing the     */
00270 /* connection if not already done                               */
00271 /*--------------------------------------------------------------*/
00272 int Mount::Status()
00273 {
00274    if ( process == NULL )
00275       return -1;
00276 
00277    exit_code = process->close();
00278    process->kill();
00279    delete process;
00280    process = 0;
00281 
00282    DBG << "exit code: " << exit_code << endl;
00283 
00284    return exit_code;
00285 }
00286 
00287 /* Forcably kill the process */
00288 void Mount::Kill()
00289 {
00290   if (process) process->kill();
00291 }
00292 
00293 // STATIC
00294 MountEntries
00295 Mount::getEntries(const std::string &mtab)
00296 {
00297   MountEntries             entries;
00298   std::vector<std::string> mtabs;
00299   bool                     verbose = false;
00300 #if ( BNC925501 )
00301   verbose = bnc925501ExtraDebug;
00302 #endif
00303 
00304   if( mtab.empty())
00305   {
00306     mtabs.push_back("/proc/mounts");
00307     // Also read /etc/mtab if it is a file (on newer sytems
00308     // mtab is a symlink to /proc/mounts).
00309     // Reason for this is the different representation of
00310     // mounted loop devices:
00311     //   /etc/mtab:    /tmp/SLES-11-SP2-MINI-ISO-x86_64-Beta2-DVD.iso on /mnt type iso9660 (ro,loop=/dev/loop0)
00312     //   /proc/mounts: /dev/loop0 /mnt iso9660 ro,relatime 0 0
00313     if ( PathInfo( "/etc/mtab", PathInfo::LSTAT ).isFile() )
00314       mtabs.push_back("/etc/mtab");
00315   }
00316   else
00317   {
00318     mtabs.push_back(mtab);
00319   }
00320 
00321   std::vector<std::string>::const_iterator t;
00322   for( t=mtabs.begin(); t != mtabs.end(); ++t)
00323   {
00324     if( verbose)
00325     {
00326       DBG << "Reading mount table from '" << *t << "'" << std::endl;
00327     }
00328     FILE *fp = setmntent(t->c_str(), "r");
00329     if( fp)
00330     {
00331       char          buf[PATH_MAX * 4];
00332       struct mntent ent;
00333 
00334       memset(buf,  0, sizeof(buf));
00335       memset(&ent, 0, sizeof(ent));
00336 
00337       while( getmntent_r(fp, &ent, buf, sizeof(buf)) != NULL)
00338       {
00339         if( ent.mnt_fsname && *ent.mnt_fsname &&
00340             ent.mnt_dir    && *ent.mnt_dir    &&
00341             ent.mnt_type   && *ent.mnt_type   &&
00342             ent.mnt_opts   && *ent.mnt_opts)
00343         {
00344           MountEntry entry(
00345             ent.mnt_fsname, ent.mnt_dir,
00346             ent.mnt_type,   ent.mnt_opts,
00347             ent.mnt_freq,   ent.mnt_passno
00348           );
00349 
00350           // Attempt quick fix for bnc#710269:
00351           // MountEntry is "//dist/install/ on /var/adm/mount/AP_0x00000001 type cifs (ro,relatime,unc=\dist\install,username=,domain=suse.de"
00352           // but looking for "Looking for media(cifs<//dist/install>)attached(*/var/adm/mount/AP_0x00000001)"
00353           // Kick the trailing '/' in "//dist/install/"
00354           // TODO: Check and fix comparison in MediaHandler::checkAttached instead.
00355           if ( entry.src.size() > 1     // not for "/"
00356                && entry.src[entry.src.size()-1] == '/' )
00357           {
00358             entry.src.erase( entry.src.size()-1 );
00359           }
00360           entries.push_back(entry);
00361 #if ( BNC925501 )
00362           if ( bnc925501ExtraDebug )
00363             DBG << "Add MountEntry " << entry << endl;
00364 #endif
00365         }
00366         else
00367         {
00368           WAR << "Suspicious MountEntry |"
00369               << ( ent.mnt_fsname ? ent.mnt_fsname : "NULL" ) << "|"
00370               << ( ent.mnt_dir ? ent.mnt_dir : "NULL" ) << "|"
00371               << ( ent.mnt_type ? ent.mnt_type : "NULL" ) << "|"
00372               << ( ent.mnt_opts ? ent.mnt_opts : "NULL" ) << "|"
00373               << endl;
00374         }
00375         memset(buf,  0, sizeof(buf));
00376         memset(&ent, 0, sizeof(ent));
00377       }
00378       endmntent(fp);
00379 
00380       if( entries.empty())
00381       {
00382         WAR << "Unable to read any entry from the mount table '" << *t << "'"
00383             << std::endl;
00384       }
00385       else
00386       {
00387         // OK, have a non-empty mount table.
00388         t = mtabs.end();
00389         break;
00390       }
00391     }
00392     else
00393     {
00394       int err = errno;
00395       verbose = true;
00396       WAR << "Failed to read the mount table '" << *t << "': "
00397           << ::strerror(err)
00398           << std::endl;
00399       errno = err;
00400     }
00401   }
00402   return entries;
00403 }
00404 
00405   } // namespace media
00406 } // namespace zypp