Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / media / MediaDISK.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaDISK.cc
10  *
11 */
12
13 #include "zypp/base/Logger.h"
14 #include "zypp/base/String.h"
15 #include "zypp/media/Mount.h"
16 #include "zypp/media/MediaDISK.h"
17 #include "zypp/media/MediaManager.h"
18
19 #include <iostream>
20 #include <fstream>
21 #include <sstream>
22
23 #include <sys/types.h>
24 #include <sys/mount.h>
25 #include <errno.h>
26 #include <dirent.h>
27
28 /*
29 ** verify devices names as late as possible (while attach)
30 */
31 #define DELAYED_VERIFY           1
32
33 using namespace std;
34
35 namespace zypp {
36   namespace media {
37
38     ///////////////////////////////////////////////////////////////////
39     //
40     //  CLASS NAME : MediaDISK
41     //
42     ///////////////////////////////////////////////////////////////////
43
44     ///////////////////////////////////////////////////////////////////
45     //
46     //
47     //  METHOD NAME : MediaDISK::MediaDISK
48     //  METHOD TYPE : Constructor
49     //
50     //  DESCRIPTION :
51     //
52     MediaDISK::MediaDISK( const Url &      url_r,
53                           const Pathname & attach_point_hint_r )
54         : MediaHandler( url_r, attach_point_hint_r,
55                     url_r.getPathName(), // urlpath below attachpoint
56                     false ) // does_download
57     {
58       MIL << "MediaDISK::MediaDISK(" << url_r << ", " << attach_point_hint_r << ")" << endl;
59
60       _device = Pathname(_url.getQueryParam("device")).asString();
61       if( _device.empty())
62       {
63         ERR << "Media url does not contain a device specification" << std::endl;
64         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
65       }
66 #if DELAYED_VERIFY
67       DBG << "Verify of " << _device << " delayed" << std::endl;
68 #else
69       if( !verifyIfDiskVolume( _device))
70       {
71         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
72       }
73 #endif
74
75       _filesystem = _url.getQueryParam("filesystem");
76       if(_filesystem.empty())
77         _filesystem="auto";
78
79     }
80
81     ///////////////////////////////////////////////////////////////////
82     //
83     //  METHOD NAME : MediaDISK::verifyIfDiskVolume
84     //  METHOD TYPE : void
85     //
86     //  DESCRIPTION : Check if specified device file name is
87     //                a disk volume device or throw an error.
88     //
89     bool MediaDISK::verifyIfDiskVolume(const Pathname &dev_name)
90     {
91       if( dev_name.empty() ||
92           dev_name.asString().compare(0, sizeof("/dev/")-1, "/dev/"))
93       {
94         ERR << "Specified device name " << dev_name
95             << " is not allowed" << std::endl;
96         return false;
97       }
98
99       PathInfo dev_info(dev_name);
100       if( !dev_info.isBlk())
101       {
102         ERR << "Specified device name " << dev_name
103             << " is not a block device" << std::endl;
104         return false;
105       }
106
107       // check if a volume using /dev/disk/by-uuid links first
108       {
109         Pathname            dpath("/dev/disk/by-uuid");
110         std::list<Pathname> dlist;
111         if( zypp::filesystem::readdir(dlist, dpath) == 0)
112         {
113           std::list<Pathname>::const_iterator it;
114           for(it = dlist.begin(); it != dlist.end(); ++it)
115           {
116             PathInfo vol_info(*it);
117             if( vol_info.isBlk() && vol_info.devMajor() == dev_info.devMajor() &&
118                                     vol_info.devMinor() == dev_info.devMinor())
119             {
120               DBG << "Specified device name " << dev_name
121                   << " is a volume (disk/by-uuid link "
122                   << vol_info.path() << ")"
123                   << std::endl;
124               return true;
125             }
126           }
127         }
128       }
129
130       // check if a volume using /dev/disk/by-label links
131       // (e.g. vbd mapped volumes in a XEN vm)
132       {
133         Pathname            dpath("/dev/disk/by-label");
134         std::list<Pathname> dlist;
135         if( zypp::filesystem::readdir(dlist, dpath) == 0)
136         {
137           std::list<Pathname>::const_iterator it;
138           for(it = dlist.begin(); it != dlist.end(); ++it)
139           {
140             PathInfo vol_info(*it);
141             if( vol_info.isBlk() && vol_info.devMajor() == dev_info.devMajor() &&
142                                     vol_info.devMinor() == dev_info.devMinor())
143             {
144               DBG << "Specified device name " << dev_name
145                   << " is a volume (disk/by-label link "
146                   << vol_info.path() << ")"
147                   << std::endl;
148               return true;
149             }
150           }
151         }
152       }
153
154       // check if a filesystem volume using the 'blkid' tool
155       // (there is no /dev/disk link for some of them)
156       ExternalProgram::Arguments args;
157       args.push_back( "blkid" );
158       args.push_back( "-p" );
159       args.push_back( dev_name.asString() );
160
161       ExternalProgram cmd( args, ExternalProgram::Stderr_To_Stdout );
162       cmd >> DBG;
163       if ( cmd.close() != 0 )
164       {
165         ERR << cmd.execError() << endl
166             << "Specified device name " << dev_name
167             << " is not a usable disk volume"
168             << std::endl;
169         return false;
170       }
171       return true;
172     }
173
174     ///////////////////////////////////////////////////////////////////
175     //
176     //
177     //  METHOD NAME : MediaDISK::attachTo
178     //  METHOD TYPE : PMError
179     //
180     //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
181     //
182     void MediaDISK::attachTo(bool next)
183     {
184       if(next)
185         ZYPP_THROW(MediaNotSupportedException(url()));
186       // FIXME
187       // do mount --bind <partition>/<dir> to <to>
188       //   mount /dev/<partition> /tmp_mount
189       //   mount /tmp_mount/<dir> <to> --bind -o ro
190       // FIXME: try all filesystems
191
192       if(_device.empty())
193         ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
194
195       PathInfo dev_info(_device);
196       if(!dev_info.isBlk())
197         ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
198 #if DELAYED_VERIFY
199       DBG << "Verifying " << _device << " ..." << std::endl;
200       if( !verifyIfDiskVolume( _device))
201       {
202         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
203       }
204 #endif
205
206       if(_filesystem.empty())
207         ZYPP_THROW(MediaBadUrlEmptyFilesystemException(url()));
208
209       MediaSourceRef media( new MediaSource(
210         "disk", _device, dev_info.devMajor(), dev_info.devMinor()
211       ));
212       AttachedMedia  ret( findAttachedMedia( media));
213
214       if( ret.mediaSource &&
215           ret.attachPoint &&
216           !ret.attachPoint->empty())
217       {
218         DBG << "Using a shared media "
219             << ret.mediaSource->name
220             << " attached on "
221             << ret.attachPoint->path
222             << endl;
223
224         removeAttachPoint();
225         setAttachPoint(ret.attachPoint);
226         setMediaSource(ret.mediaSource);
227         return;
228       }
229
230       MediaManager  manager;
231       MountEntries  entries( manager.getMountEntries());
232       MountEntries::const_iterator e;
233       for( e = entries.begin(); e != entries.end(); ++e)
234       {
235         bool        is_device = false;
236         std::string dev_path(Pathname(e->src).asString());
237         PathInfo    dev_info;
238
239         if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
240             dev_info(e->src) && dev_info.isBlk())
241         {
242           is_device = true;
243         }
244
245         if( is_device && media->maj_nr == dev_info.devMajor() &&
246                          media->min_nr == dev_info.devMinor())
247         {
248           AttachPointRef ap( new AttachPoint(e->dir, false));
249           AttachedMedia  am( media, ap);
250           {
251             DBG << "Using a system mounted media "
252                 << media->name
253                 << " attached on "
254                 << ap->path
255                 << endl;
256
257             media->iown = false; // mark attachment as foreign
258
259             setMediaSource(media);
260             setAttachPoint(ap);
261             return;
262           }
263         }
264       }
265
266       if( !isUseableAttachPoint( attachPoint() ) )
267       {
268         setAttachPoint( createAttachPoint(), true );
269       }
270       std::string mountpoint( attachPoint().asString() );
271
272       Mount mount;
273       string options = _url.getQueryParam("mountoptions");
274       if(options.empty())
275       {
276         options = "ro";
277       }
278
279       if( !media->bdir.empty())
280       {
281         options += ",bind";
282         mount.mount(media->bdir, mountpoint, "none", options);
283       }
284       else
285       {
286         mount.mount(_device, mountpoint, _filesystem, options);
287       }
288
289       setMediaSource(media);
290
291       // wait for /etc/mtab update ...
292       // (shouldn't be needed)
293       int limit = 3;
294       bool mountsucceeded;
295       while( !(mountsucceeded=isAttached()) && --limit)
296       {
297         sleep(1);
298       }
299
300       if( !mountsucceeded)
301       {
302         setMediaSource(MediaSourceRef());
303         try
304         {
305           mount.umount(attachPoint().asString());
306         }
307         catch (const MediaException & excpt_r)
308         {
309           ZYPP_CAUGHT(excpt_r);
310         }
311         ZYPP_THROW(MediaMountException(
312           "Unable to verify that the media was mounted",
313           _device, mountpoint
314         ));
315       }
316     }
317
318     ///////////////////////////////////////////////////////////////////
319     //
320     //  METHOD NAME : MediaDISK::isAttached
321     //  METHOD TYPE : bool
322     //
323     //  DESCRIPTION : Override check if media is attached.
324     //
325     bool
326     MediaDISK::isAttached() const
327     {
328       return checkAttached(false);
329     }
330
331     ///////////////////////////////////////////////////////////////////
332     //
333     //
334     //  METHOD NAME : MediaDISK::releaseFrom
335     //  METHOD TYPE : PMError
336     //
337     //  DESCRIPTION : Asserted that media is attached.
338     //
339     void MediaDISK::releaseFrom( const std::string & ejectDev )
340     {
341       AttachedMedia am( attachedMedia());
342       if(am.mediaSource && am.mediaSource->iown)
343       {
344         Mount mount;
345         mount.umount(attachPoint().asString());
346       }
347     }
348
349     ///////////////////////////////////////////////////////////////////
350     //
351     //  METHOD NAME : MediaDISK::getFile
352     //  METHOD TYPE : PMError
353     //
354     //  DESCRIPTION : Asserted that media is attached.
355     //
356     void MediaDISK::getFile (const Pathname & filename) const
357     {
358       MediaHandler::getFile( filename );
359     }
360
361     ///////////////////////////////////////////////////////////////////
362     //
363     //  METHOD NAME : MediaDISK::getDir
364     //  METHOD TYPE : PMError
365     //
366     //  DESCRIPTION : Asserted that media is attached.
367     //
368     void MediaDISK::getDir( const Pathname & dirname, bool recurse_r ) const
369     {
370       MediaHandler::getDir( dirname, recurse_r );
371     }
372
373     ///////////////////////////////////////////////////////////////////
374     //
375     //
376     //  METHOD NAME : MediaDISK::getDirInfo
377     //  METHOD TYPE : PMError
378     //
379     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
380     //
381     void MediaDISK::getDirInfo( std::list<std::string> & retlist,
382                                 const Pathname & dirname, bool dots ) const
383     {
384       MediaHandler::getDirInfo( retlist, dirname, dots );
385     }
386
387     ///////////////////////////////////////////////////////////////////
388     //
389     //
390     //  METHOD NAME : MediaDISK::getDirInfo
391     //  METHOD TYPE : PMError
392     //
393     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
394     //
395     void MediaDISK::getDirInfo( filesystem::DirContent & retlist,
396                                 const Pathname & dirname, bool dots ) const
397     {
398       MediaHandler::getDirInfo( retlist, dirname, dots );
399     }
400
401     bool MediaDISK::getDoesFileExist( const Pathname & filename ) const
402     {
403       return MediaHandler::getDoesFileExist( filename );
404     }
405
406   } // namespace media
407 } // namespace zypp
408 // vim: set ts=8 sts=2 sw=2 ai noet: