libzypp  17.14.0
MediaCIFS.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <iostream>
14 #include <fstream>
15 
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/TmpPath.h"
19 #include "zypp/KVMap.h"
20 #include "zypp/media/Mount.h"
23 #include "zypp/ZYppCallbacks.h"
24 #include "zypp/ZConfig.h"
25 
26 #include "zypp/media/MediaCIFS.h"
27 
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <errno.h>
31 #include <dirent.h>
32 
33 using namespace std;
34 
35 namespace zypp {
36  namespace media {
37 
38  /******************************************************************
39  **
40  **
41  ** FUNCTION NAME : getShare
42  ** FUNCTION TYPE : inline Pathname
43  **
44  ** Get the 1st path component (CIFS share name).
45  */
46  inline string getShare( Pathname spath_r )
47  {
48  if ( spath_r.empty() )
49  return string();
50 
51  string share( spath_r.absolutename().asString() );
52  string::size_type sep = share.find( "/", 1 );
53  if ( sep == string::npos )
54  share = share.erase( 0, 1 ); // nothing but the share name in spath_r
55  else
56  share = share.substr( 1, sep-1 );
57 
58  // deescape %2f in sharename
59  while ( (sep = share.find( "%2f" )) != string::npos ) {
60  share.replace( sep, 3, "/" );
61  }
62 
63  return share;
64  }
65 
66  /******************************************************************
67  **
68  **
69  ** FUNCTION NAME : stripShare
70  ** FUNCTION TYPE : inline Pathname
71  **
72  ** Strip off the 1st path component (CIFS share name).
73  */
74  inline Pathname stripShare( Pathname spath_r )
75  {
76  if ( spath_r.empty() )
77  return Pathname();
78 
79  string striped( spath_r.absolutename().asString() );
80  string::size_type sep = striped.find( "/", 1 );
81  if ( sep == string::npos )
82  return "/"; // nothing but the share name in spath_r
83 
84  return striped.substr( sep );
85  }
86 
88  //
89  // CLASS NAME : MediaCIFS
90  //
92 
94  //
95  //
96  // METHOD NAME : MediaCIFS::MediaCIFS
97  // METHOD TYPE : Constructor
98  //
99  // DESCRIPTION :
100  //
101  MediaCIFS::MediaCIFS( const Url & url_r,
102  const Pathname & attach_point_hint_r )
103  : MediaHandler( url_r, attach_point_hint_r,
104  stripShare( url_r.getPathName() ), // urlpath WITHOUT share name at attachpoint
105  false ) // does_download
106  {
107  MIL << "MediaCIFS::MediaCIFS(" << url_r << ", " << attach_point_hint_r << ")" << endl;
108  }
109 
111  //
112  //
113  // METHOD NAME : MediaCIFS::attachTo
114  // METHOD TYPE : PMError
129  void MediaCIFS::attachTo(bool next)
130  {
131  if(_url.getHost().empty())
133  if(next)
135 
136  string path = "//";
137  path += _url.getHost() + "/" + getShare( _url.getPathName() );
138 
139  MediaSourceRef media( new MediaSource( "cifs", path));
140  AttachedMedia ret( findAttachedMedia( media));
141 
142  if( ret.mediaSource &&
143  ret.attachPoint &&
144  !ret.attachPoint->empty())
145  {
146  DBG << "Using a shared media "
147  << ret.mediaSource->name
148  << " attached on "
149  << ret.attachPoint->path
150  << endl;
151 
155  return;
156  }
157 
159  {
161  }
162  std::string mountpoint( attachPoint().asString() );
163 
164  Mount mount;
165  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
166 
167  Mount::Options options( _url.getQueryParam("mountoptions") );
168  string username = _url.getUsername();
169  string password = _url.getPassword();
170 
171  if ( ! options.has( "rw" ) ) {
172  options["ro"];
173  }
174 
175  // look for a workgroup
176  string workgroup = _url.getQueryParam("workgroup");
177  if ( workgroup.empty() )
178  workgroup = _url.getQueryParam("domain");
179  if ( !workgroup.empty() )
180  options["domain"] = workgroup;
181 
182  // extract 'username', do not overwrite any _url.username
183 
184  Mount::Options::iterator toEnv;
185  toEnv = options.find("username");
186  if ( toEnv != options.end() ) {
187  if ( username.empty() )
188  username = toEnv->second;
189  options.erase( toEnv );
190  }
191 
192  toEnv = options.find("user"); // actually cifs specific
193  if ( toEnv != options.end() ) {
194  if ( username.empty() )
195  username = toEnv->second;
196  options.erase( toEnv );
197  }
198 
199  // extract 'password', do not overwrite any _url.password
200 
201  toEnv = options.find("password");
202  if ( toEnv != options.end() ) {
203  if ( password.empty() )
204  password = toEnv->second;
205  options.erase( toEnv );
206  }
207 
208  toEnv = options.find("pass"); // actually cifs specific
209  if ( toEnv != options.end() ) {
210  if ( password.empty() )
211  password = toEnv->second;
212  options.erase( toEnv );
213  }
214 
215  if ( username.empty() || password.empty() )
216  {
217  AuthData_Ptr c = cm.getCred(_url);
218  if (c)
219  {
220  username = c->username();
221  password = c->password();
222  }
223  }
224 
225  bool firstTry = true;
226  bool authRequired = false;
227  AuthData authdata;
228  do // repeat this while the mount returns "Permission denied" error
229  {
230  // get credentials from authenicate()
231  if ( !firstTry )
232  {
233  username = authdata.username();
234  password = authdata.password();
235  }
236 
237  // pass 'username' and 'password' via environment
238  Mount::Environment environment;
239  if ( !username.empty() )
240  environment["USER"] = username;
241  if ( !password.empty() )
242  environment["PASSWD"] = password;
243 
245  // In case we need a tmpfile, credentials will remove
246  // it in it's destructor after the mout call below.
247  filesystem::TmpPath credentials;
248  if ( !username.empty() || !password.empty() )
249  {
251  ofstream outs( tmp.path().asString().c_str() );
252  outs << "username=" << username << endl;
253  outs << "password=" << password << endl;
254  outs.close();
255 
256  credentials = tmp;
257  options["credentials"] = credentials.path().asString();
258  }
259  else
260  {
261  // Use 'guest' option unless explicitly disabled (bnc #547354)
262  if ( options.has( "noguest" ) )
263  options.erase( "noguest" );
264  else
265  // prevent smbmount from asking for password
266  // only add this option if 'credentials' is not used (bnc #560496)
267  options["guest"];
268  }
269 
270  //
272 
273  try
274  {
275  mount.mount( path, mountpoint, "cifs",
276  options.asString(), environment );
277  setMediaSource(media);
278  break;
279  }
280  catch (const MediaMountException & e)
281  {
282  ZYPP_CAUGHT( e );
283 
284  if ( e.mountError() == "Permission denied" )
285  authRequired = authenticate( authdata, firstTry );
286  else
287  ZYPP_RETHROW( e );
288  }
289 
290  firstTry = false;
291  }
292  while ( authRequired );
293 
294  // wait for /etc/mtab update ...
295  // (shouldn't be needed)
296  int limit = 3;
297  bool mountsucceeded;
298  while( !(mountsucceeded=isAttached()) && --limit)
299  sleep(1);
300 
301  if ( !mountsucceeded )
302  {
304  try
305  {
306  mount.umount(attachPoint().asString());
307  }
308  catch (const MediaException & excpt_r)
309  {
310  ZYPP_CAUGHT(excpt_r);
311  }
313  "Unable to verify that the media was mounted",
314  path, mountpoint
315  ));
316  }
317  }
318 
320  //
321  // METHOD NAME : MediaCIFS::isAttached
322  // METHOD TYPE : bool
323  //
324  // DESCRIPTION : Override check if media is attached.
325  //
326  bool
328  {
329  return checkAttached(true);
330  }
331 
333  //
334  //
335  // METHOD NAME : MediaCIFS::releaseFrom
336  // METHOD TYPE : PMError
337  //
338  // DESCRIPTION : Asserted that media is attached.
339  //
340  void MediaCIFS::releaseFrom( const std::string & ejectDev )
341  {
342  Mount mount;
343  mount.umount(attachPoint().asString());
344  }
345 
347  //
348  // METHOD NAME : MediaCIFS::getFile
349  // METHOD TYPE : PMError
350  //
351  // DESCRIPTION : Asserted that media is attached.
352  //
353  void MediaCIFS::getFile (const Pathname & filename, const ByteCount &expectedFileSize_r) const
354  {
355  MediaHandler::getFile( filename, expectedFileSize_r );
356  }
357 
359  //
360  // METHOD NAME : MediaCIFS::getDir
361  // METHOD TYPE : PMError
362  //
363  // DESCRIPTION : Asserted that media is attached.
364  //
365  void MediaCIFS::getDir( const Pathname & dirname, bool recurse_r ) const
366  {
367  MediaHandler::getDir( dirname, recurse_r );
368  }
369 
371  //
372  //
373  // METHOD NAME : MediaCIFS::getDirInfo
374  // METHOD TYPE : PMError
375  //
376  // DESCRIPTION : Asserted that media is attached and retlist is empty.
377  //
378  void MediaCIFS::getDirInfo( std::list<std::string> & retlist,
379  const Pathname & dirname, bool dots ) const
380  {
381  MediaHandler::getDirInfo( retlist, dirname, dots );
382  }
383 
385  //
386  //
387  // METHOD NAME : MediaCIFS::getDirInfo
388  // METHOD TYPE : PMError
389  //
390  // DESCRIPTION : Asserted that media is attached and retlist is empty.
391  //
393  const Pathname & dirname, bool dots ) const
394  {
395  MediaHandler::getDirInfo( retlist, dirname, dots );
396  }
397 
398  bool MediaCIFS::getDoesFileExist( const Pathname & filename ) const
399  {
400  return MediaHandler::getDoesFileExist( filename );
401  }
402 
403  bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const
404  {
406  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
407 
408  // get stored credentials
409  AuthData_Ptr cmcred = cm.getCred(_url);
410 
411  AuthData_Ptr smbcred;
412  smbcred.reset(new AuthData());
414 
415  // preset the username if present in current url
416  if (!_url.getUsername().empty() && firstTry)
417  smbcred->setUsername(_url.getUsername());
418  // if CM has found some credentials, preset the username from there
419  else if (cmcred)
420  smbcred->setUsername(cmcred->username());
421 
422  // indicate we have no good credentials from CM
423  cmcred.reset();
424 
425  string prompt_msg = str::form(
427  _("Authentication required for '%s'"), _url.asString().c_str());
428 
429  // ask user
430  if (auth_report->prompt(_url, prompt_msg, *smbcred))
431  {
432  DBG << "callback answer: retry" << endl
433  << "AuthData: " << *smbcred << endl;
434 
435  if (smbcred->valid())
436  {
437  cmcred = smbcred;
438  // if (credentials->username() != _url.getUsername())
439  // _url.setUsername(credentials->username());
447  }
448  }
449  else
450  DBG << "callback answer: cancel" << endl;
451 
452  // set username and password
453  if (cmcred)
454  {
455  authdata.setUsername(cmcred->username());
456  authdata.setPassword(cmcred->password());
457 
458  // save the credentials
459  cmcred->setUrl(_url);
460  cm.addCred(*cmcred);
461  cm.save();
462 
463  return true;
464  }
465 
466  return false;
467  }
468 
469 
470  } // namespace media
471 } // namespace zypp
Interface to gettext.
#define MIL
Definition: Logger.h:79
const std::string & mountError() const
std::string password() const
Definition: MediaUserAuth.h:57
Interface to the mount program.
Definition: Mount.h:69
std::string asString(const DefaultIntegral< Tp, TInitial > &obj)
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:127
void setPassword(const std::string &password)
Definition: MediaUserAuth.h:53
virtual void getDir(const Pathname &dirname, bool recurse_r) const =0
Call concrete handler to provide directory content (not recursive!) below attach point.
virtual void attachTo(bool next=false) override
Asserted that not already attached, and attachPoint is a directory.
Definition: MediaCIFS.cc:129
Store and operate with byte count.
Definition: ByteCount.h:30
zypp::RW_pointer< MediaSource > MediaSourceRef
Definition: MediaSource.h:124
void save()
Saves any unsaved credentials added via addUserCred() or addGlobalCred() methods.
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
virtual void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const override
Call concrete handler to provide a content list of directory on media via retlist.
Definition: MediaCIFS.cc:378
ExternalProgram::Environment Environment
For passing additional environment variables to mount.
Definition: Mount.h:77
bool isUseableAttachPoint(const Pathname &path, bool mtab=true) const
Ask media manager, if the specified path is already used as attach point or if there are another atta...
Pathname stripShare(Pathname spath_r)
Definition: MediaCIFS.cc:74
Definition: Arch.h:344
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
Pathname path() const
Definition: TmpPath.cc:146
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:127
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
virtual bool isAttached() const override
True if media is attached.
Definition: MediaCIFS.cc:327
bool checkAttached(bool matchMountFs) const
Check actual mediaSource attachment against the current mount table of the system.
virtual void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
Definition: MediaCIFS.cc:365
void mount(const std::string &source, const std::string &target, const std::string &filesystem, const std::string &options, const Environment &environment=Environment())
mount device
Definition: Mount.cc:66
virtual void getFile(const Pathname &filename, const ByteCount &expectedFileSize_r) const override
Call concrete handler to provide file below attach point.
Definition: MediaCIFS.cc:353
virtual bool getDoesFileExist(const Pathname &filename) const override
check if a file exists
Definition: MediaCIFS.cc:398
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition: MediaCIFS.cc:340
bool empty() const
Test for an empty path.
Definition: Pathname.h:113
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:400
AttachPointRef attachPoint
Definition: MediaSource.h:145
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:492
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:655
MediaSourceRef mediaSource
Definition: MediaSource.h:144
Abstract base class for 'physical' MediaHandler like MediaCD, etc.
Definition: MediaHandler.h:45
A simple structure containing references to a media source and its attach point.
Definition: MediaSource.h:133
const Url _url
Url to handle.
Definition: MediaHandler.h:110
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
const std::string & asString() const
String representation.
Definition: Pathname.h:90
Just inherits Exception to separate media exceptions.
std::string username() const
Definition: MediaUserAuth.h:56
std::list< DirEntry > DirContent
Returned by readdir.
Definition: PathInfo.h:547
#define _(MSG)
Definition: Gettext.h:37
Class for handling media authentication data.
Definition: MediaUserAuth.h:30
void removeAttachPoint()
Remove unused attach point.
SolvableIdType size_type
Definition: PoolMember.h:126
Media source internally used by MediaManager and MediaHandler.
Definition: MediaSource.h:36
string getShare(Pathname spath_r)
Definition: MediaCIFS.cc:46
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:396
shared_ptr< AuthData > AuthData_Ptr
Definition: MediaUserAuth.h:69
virtual void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const =0
Call concrete handler to provide a content list of directory on media via retlist.
virtual bool getDoesFileExist(const Pathname &filename) const =0
check if a file exists
AttachedMedia findAttachedMedia(const MediaSourceRef &media) const
Ask the media manager if specified media source is already attached.
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
Definition: Pathname.h:138
Pathname attachPoint() const
Return the currently used attach point.
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition: Url.cc:599
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:583
bool authenticate(AuthData &authdata, bool firstTry) const
Definition: MediaCIFS.cc:403
virtual void getFile(const Pathname &filename, const ByteCount &expectedFileSize_r) const
Call concrete handler to provide file below attach point.
Automaticaly deletes files or directories when no longer needed.
Definition: TmpPath.h:40
A map of (key,value) strings.
Definition: KVMap.h:176
void setUsername(const std::string &username)
Definition: MediaUserAuth.h:52
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
void addCred(const AuthData &cred)
Add new credentials with user callbacks.
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:575
Convenience interface for handling authentication data of media user.
Url manipulation class.
Definition: Url.h:87
void umount(const std::string &path)
umount device
Definition: Mount.cc:162
#define DBG
Definition: Logger.h:78
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:567