libzypp  10.5.0
MediaCIFS.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <iostream>
00014 #include <fstream>
00015 
00016 #include "zypp/base/Logger.h"
00017 #include "zypp/base/Gettext.h"
00018 #include "zypp/TmpPath.h"
00019 #include "zypp/KVMap.h"
00020 #include "zypp/media/Mount.h"
00021 #include "zypp/media/MediaUserAuth.h"
00022 #include "zypp/media/CredentialManager.h"
00023 #include "zypp/ZYppCallbacks.h"
00024 #include "zypp/ZConfig.h"
00025 
00026 #include "zypp/media/MediaCIFS.h"
00027 
00028 #include <sys/types.h>
00029 #include <sys/mount.h>
00030 #include <errno.h>
00031 #include <dirent.h>
00032 
00033 using namespace std;
00034 
00035 namespace zypp {
00036   namespace media {
00037 
00038     /******************************************************************
00039     **
00040     **
00041     **  FUNCTION NAME : getShare
00042     **  FUNCTION TYPE : inline Pathname
00043     **
00044     ** Get the 1st path component (CIFS share name).
00045     */
00046     inline string getShare( Pathname spath_r )
00047     {
00048       if ( spath_r.empty() )
00049         return string();
00050 
00051       string share( spath_r.absolutename().asString() );
00052       string::size_type sep = share.find( "/", 1 );
00053       if ( sep == string::npos )
00054         share = share.erase( 0, 1 ); // nothing but the share name in spath_r
00055       else
00056         share = share.substr( 1, sep-1 );
00057 
00058       // deescape %2f in sharename
00059       while ( (sep = share.find( "%2f" )) != string::npos ) {
00060         share.replace( sep, 3, "/" );
00061       }
00062 
00063       return share;
00064     }
00065 
00066     /******************************************************************
00067     **
00068     **
00069     **  FUNCTION NAME : stripShare
00070     **  FUNCTION TYPE : inline Pathname
00071     **
00072     ** Strip off the 1st path component (CIFS share name).
00073     */
00074     inline Pathname stripShare( Pathname spath_r )
00075     {
00076       if ( spath_r.empty() )
00077         return Pathname();
00078 
00079       string striped( spath_r.absolutename().asString() );
00080       string::size_type sep = striped.find( "/", 1 );
00081       if ( sep == string::npos )
00082         return "/"; // nothing but the share name in spath_r
00083 
00084       return striped.substr( sep );
00085     }
00086 
00088     //
00089     //  CLASS NAME : MediaCIFS
00090     //
00092 
00094     //
00095     //
00096     //  METHOD NAME : MediaCIFS::MediaCIFS
00097     //  METHOD TYPE : Constructor
00098     //
00099     //  DESCRIPTION :
00100     //
00101     MediaCIFS::MediaCIFS( const Url &      url_r,
00102                         const Pathname & attach_point_hint_r )
00103         : MediaHandler( url_r, attach_point_hint_r,
00104                     stripShare( url_r.getPathName() ), // urlpath WITHOUT share name at attachpoint
00105                     false )       // does_download
00106     {
00107         MIL << "MediaCIFS::MediaCIFS(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00108     }
00109 
00111     //
00112     //
00113     //  METHOD NAME : MediaCIFS::attachTo
00114     //  METHOD TYPE : PMError
00129     void MediaCIFS::attachTo(bool next)
00130     {
00131       if(_url.getHost().empty())
00132         ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00133       if(next)
00134         ZYPP_THROW(MediaNotSupportedException(_url));
00135 
00136       string path = "//";
00137       path += _url.getHost() + "/" + getShare( _url.getPathName() );
00138 
00139       MediaSourceRef media( new MediaSource( "cifs", path));
00140       AttachedMedia  ret( findAttachedMedia( media));
00141 
00142       if( ret.mediaSource &&
00143           ret.attachPoint &&
00144           !ret.attachPoint->empty())
00145       {
00146         DBG << "Using a shared media "
00147             << ret.mediaSource->name
00148             << " attached on "
00149             << ret.attachPoint->path
00150             << endl;
00151 
00152         removeAttachPoint();
00153         setAttachPoint(ret.attachPoint);
00154         setMediaSource(ret.mediaSource);
00155         return;
00156       }
00157 
00158       std::string mountpoint = attachPoint().asString();
00159       if( !isUseableAttachPoint(attachPoint()))
00160       {
00161         mountpoint = createAttachPoint().asString();
00162         if( mountpoint.empty())
00163           ZYPP_THROW( MediaBadAttachPointException(url()));
00164         setAttachPoint( mountpoint, true);
00165       }
00166 
00167       Mount mount;
00168       CredentialManager cm;
00169 
00170       Mount::Options options( _url.getQueryParam("mountoptions") );
00171       string username = _url.getUsername();
00172       string password = _url.getPassword();
00173 
00174       if ( ! options.has( "rw" ) ) {
00175         options["ro"];
00176       }
00177 
00178       // look for a workgroup
00179       string workgroup = _url.getQueryParam("workgroup");
00180       if ( workgroup.empty() )
00181         workgroup = _url.getQueryParam("domain");
00182       if ( !workgroup.empty() )
00183         options["domain"] = workgroup;
00184 
00185       // extract 'username', do not overwrite any _url.username
00186 
00187       Mount::Options::iterator toEnv;
00188       toEnv = options.find("username");
00189       if ( toEnv != options.end() ) {
00190         if ( username.empty() )
00191           username = toEnv->second;
00192         options.erase( toEnv );
00193       }
00194 
00195       toEnv = options.find("user"); // actually cifs specific
00196       if ( toEnv != options.end() ) {
00197         if ( username.empty() )
00198           username = toEnv->second;
00199         options.erase( toEnv );
00200       }
00201 
00202       // extract 'password', do not overwrite any _url.password
00203 
00204       toEnv = options.find("password");
00205       if ( toEnv != options.end() ) {
00206         if ( password.empty() )
00207           password = toEnv->second;
00208         options.erase( toEnv );
00209       }
00210 
00211       toEnv = options.find("pass"); // actually cifs specific
00212       if ( toEnv != options.end() ) {
00213         if ( password.empty() )
00214           password = toEnv->second;
00215         options.erase( toEnv );
00216       }
00217 
00218       if ( username.empty() || password.empty() )
00219       {
00220         AuthData_Ptr c = cm.getCred(_url);
00221         if (c)
00222         {
00223           username = c->username();
00224           password = c->password();
00225         }
00226       }
00227 
00228       bool firstTry = true;
00229       bool authRequired = false;
00230       AuthData authdata;
00231       do // repeat this while the mount returns "Permission denied" error
00232       {
00233         // get credentials from authenicate()
00234         if ( !firstTry )
00235         {
00236           username = authdata.username();
00237           password = authdata.password();
00238         }
00239 
00240         // pass 'username' and 'password' via environment
00241         Mount::Environment environment;
00242         if ( !username.empty() )
00243           environment["USER"] = username;
00244         if ( !password.empty() )
00245           environment["PASSWD"] = password;
00246 
00248         // In case we need a tmpfile, credentials will remove
00249         // it in it's destructor after the mout call below.
00250         filesystem::TmpPath credentials;
00251         if ( !username.empty() || !password.empty() )
00252         {
00253           filesystem::TmpFile tmp;
00254           ofstream outs( tmp.path().asString().c_str() );
00255           outs << "username=" <<  username << endl;
00256           outs << "password=" <<  password << endl;
00257           outs.close();
00258 
00259           credentials = tmp;
00260           options["credentials"] = credentials.path().asString();
00261         }
00262         else
00263         {
00264           // Use 'guest' option unless explicitly disabled (bnc #547354)
00265           if ( options.has( "noguest" ) )
00266             options.erase( "noguest" );
00267           else
00268             // prevent smbmount from asking for password
00269             // only add this option if 'credentials' is not used (bnc #560496)
00270             options["guest"];
00271         }
00272 
00273         //
00275 
00276         try
00277         {
00278           mount.mount( path, mountpoint, "cifs",
00279                        options.asString(), environment );
00280           setMediaSource(media);
00281           break;
00282         }
00283         catch (const MediaMountException & e)
00284         {
00285           ZYPP_CAUGHT( e );
00286 
00287           if ( e.mountError() == "Permission denied" )
00288             authRequired = authenticate( authdata, firstTry );
00289           else
00290             ZYPP_RETHROW( e );
00291         }
00292 
00293         firstTry = false;
00294       }
00295       while ( authRequired );
00296 
00297       // wait for /etc/mtab update ...
00298       // (shouldn't be needed)
00299       int limit = 3;
00300       bool mountsucceeded;
00301       while( !(mountsucceeded=isAttached()) && --limit)
00302         sleep(1);
00303 
00304       if ( !mountsucceeded )
00305       {
00306         setMediaSource(MediaSourceRef());
00307         try
00308         {
00309           mount.umount(attachPoint().asString());
00310         }
00311         catch (const MediaException & excpt_r)
00312         {
00313           ZYPP_CAUGHT(excpt_r);
00314         }
00315         ZYPP_THROW(MediaMountException(
00316           "Unable to verify that the media was mounted",
00317           path, mountpoint
00318         ));
00319       }
00320     }
00321 
00323     //
00324     //  METHOD NAME : MediaCIFS::isAttached
00325     //  METHOD TYPE : bool
00326     //
00327     //  DESCRIPTION : Override check if media is attached.
00328     //
00329     bool
00330     MediaCIFS::isAttached() const
00331     {
00332       return checkAttached(true);
00333     }
00334 
00336     //
00337     //
00338     //  METHOD NAME : MediaCIFS::releaseFrom
00339     //  METHOD TYPE : PMError
00340     //
00341     //  DESCRIPTION : Asserted that media is attached.
00342     //
00343     void MediaCIFS::releaseFrom( const std::string & ejectDev )
00344     {
00345       Mount mount;
00346       mount.umount(attachPoint().asString());
00347     }
00348 
00350     //
00351     //  METHOD NAME : MediaCIFS::getFile
00352     //  METHOD TYPE : PMError
00353     //
00354     //  DESCRIPTION : Asserted that media is attached.
00355     //
00356     void MediaCIFS::getFile (const Pathname & filename) const
00357     {
00358       MediaHandler::getFile( filename );
00359     }
00360 
00362     //
00363     //  METHOD NAME : MediaCIFS::getDir
00364     //  METHOD TYPE : PMError
00365     //
00366     //  DESCRIPTION : Asserted that media is attached.
00367     //
00368     void MediaCIFS::getDir( const Pathname & dirname, bool recurse_r ) const
00369     {
00370       MediaHandler::getDir( dirname, recurse_r );
00371     }
00372 
00374     //
00375     //
00376     //  METHOD NAME : MediaCIFS::getDirInfo
00377     //  METHOD TYPE : PMError
00378     //
00379     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00380     //
00381     void MediaCIFS::getDirInfo( std::list<std::string> & retlist,
00382                                const Pathname & dirname, bool dots ) const
00383     {
00384       MediaHandler::getDirInfo( retlist, dirname, dots );
00385     }
00386 
00388     //
00389     //
00390     //  METHOD NAME : MediaCIFS::getDirInfo
00391     //  METHOD TYPE : PMError
00392     //
00393     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00394     //
00395     void MediaCIFS::getDirInfo( filesystem::DirContent & retlist,
00396                                const Pathname & dirname, bool dots ) const
00397     {
00398       MediaHandler::getDirInfo( retlist, dirname, dots );
00399     }
00400 
00401     bool MediaCIFS::getDoesFileExist( const Pathname & filename ) const
00402     {
00403       return MediaHandler::getDoesFileExist( filename );
00404     }
00405 
00406     bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const
00407     {
00409       CredentialManager cm(CredManagerOptions(ZConfig::instance().systemRoot()));
00410 
00411       // get stored credentials
00412       AuthData_Ptr cmcred = cm.getCred(_url);
00413 
00414       AuthData_Ptr smbcred;
00415       smbcred.reset(new AuthData());
00416       callback::SendReport<AuthenticationReport> auth_report;
00417 
00418       // preset the username if present in current url
00419       if (!_url.getUsername().empty() && firstTry)
00420         smbcred->setUsername(_url.getUsername());
00421       // if CM has found some credentials, preset the username from there
00422       else if (cmcred)
00423         smbcred->setUsername(cmcred->username());
00424 
00425       // indicate we have no good credentials from CM
00426       cmcred.reset();
00427 
00428       string prompt_msg = str::form(
00430         _("Authentication required for '%s'"), _url.asString().c_str());
00431 
00432       // ask user
00433       if (auth_report->prompt(_url, prompt_msg, *smbcred))
00434       {
00435         DBG << "callback answer: retry" << endl
00436             << "AuthData: " << *smbcred << endl;
00437 
00438         if (smbcred->valid())
00439         {
00440           cmcred = smbcred;
00441             // if (credentials->username() != _url.getUsername())
00442             //   _url.setUsername(credentials->username());
00450         }
00451       }
00452       else
00453         DBG << "callback answer: cancel" << endl;
00454 
00455       // set username and password
00456       if (cmcred)
00457       {
00458         authdata.setUsername(cmcred->username());
00459         authdata.setPassword(cmcred->password());
00460 
00461         // save the credentials
00462         cmcred->setUrl(_url);
00463         cm.addCred(*cmcred);
00464         cm.save();
00465 
00466         return true;
00467       }
00468 
00469       return false;
00470     }
00471 
00472 
00473   } // namespace media
00474 } // namespace zypp