999828bd17ac8b3a10c8fc7039866e8b5721d9f3
[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       Mount mount;
267       std::string mountpoint = attachPoint().asString();
268       if( !isUseableAttachPoint(attachPoint()))
269       {
270         mountpoint = createAttachPoint().asString();
271         if( mountpoint.empty())
272           ZYPP_THROW( MediaBadAttachPointException(url()));
273         setAttachPoint( mountpoint, true);
274       }
275
276       string options = _url.getQueryParam("mountoptions");
277       if(options.empty())
278       {
279         options = "ro";
280       }
281
282       if( !media->bdir.empty())
283       {
284         options += ",bind";
285         mount.mount(media->bdir, mountpoint, "none", options);
286       }
287       else
288       {
289         mount.mount(_device, mountpoint, _filesystem, options);
290       }
291
292       setMediaSource(media);
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       {
300         sleep(1);
301       }
302
303       if( !mountsucceeded)
304       {
305         setMediaSource(MediaSourceRef());
306         try
307         {
308           mount.umount(attachPoint().asString());
309         }
310         catch (const MediaException & excpt_r)
311         {
312           ZYPP_CAUGHT(excpt_r);
313         }
314         ZYPP_THROW(MediaMountException(
315           "Unable to verify that the media was mounted",
316           _device, mountpoint
317         ));
318       }
319     }
320
321     ///////////////////////////////////////////////////////////////////
322     //
323     //  METHOD NAME : MediaDISK::isAttached
324     //  METHOD TYPE : bool
325     //
326     //  DESCRIPTION : Override check if media is attached.
327     //
328     bool
329     MediaDISK::isAttached() const
330     {
331       return checkAttached(false);
332     }
333
334     ///////////////////////////////////////////////////////////////////
335     //
336     //
337     //  METHOD NAME : MediaDISK::releaseFrom
338     //  METHOD TYPE : PMError
339     //
340     //  DESCRIPTION : Asserted that media is attached.
341     //
342     void MediaDISK::releaseFrom( const std::string & ejectDev )
343     {
344       AttachedMedia am( attachedMedia());
345       if(am.mediaSource && am.mediaSource->iown)
346       {
347         Mount mount;
348         mount.umount(attachPoint().asString());
349       }
350     }
351
352     ///////////////////////////////////////////////////////////////////
353     //
354     //  METHOD NAME : MediaDISK::getFile
355     //  METHOD TYPE : PMError
356     //
357     //  DESCRIPTION : Asserted that media is attached.
358     //
359     void MediaDISK::getFile (const Pathname & filename, const ByteCount &expectedFileSize_r) const
360     {
361       MediaHandler::getFile( filename, expectedFileSize_r );
362     }
363
364     ///////////////////////////////////////////////////////////////////
365     //
366     //  METHOD NAME : MediaDISK::getDir
367     //  METHOD TYPE : PMError
368     //
369     //  DESCRIPTION : Asserted that media is attached.
370     //
371     void MediaDISK::getDir( const Pathname & dirname, bool recurse_r ) const
372     {
373       MediaHandler::getDir( dirname, recurse_r );
374     }
375
376     ///////////////////////////////////////////////////////////////////
377     //
378     //
379     //  METHOD NAME : MediaDISK::getDirInfo
380     //  METHOD TYPE : PMError
381     //
382     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
383     //
384     void MediaDISK::getDirInfo( std::list<std::string> & retlist,
385                                 const Pathname & dirname, bool dots ) const
386     {
387       MediaHandler::getDirInfo( retlist, dirname, dots );
388     }
389
390     ///////////////////////////////////////////////////////////////////
391     //
392     //
393     //  METHOD NAME : MediaDISK::getDirInfo
394     //  METHOD TYPE : PMError
395     //
396     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
397     //
398     void MediaDISK::getDirInfo( filesystem::DirContent & retlist,
399                                 const Pathname & dirname, bool dots ) const
400     {
401       MediaHandler::getDirInfo( retlist, dirname, dots );
402     }
403
404     bool MediaDISK::getDoesFileExist( const Pathname & filename ) const
405     {
406       return MediaHandler::getDoesFileExist( filename );
407     }
408
409   } // namespace media
410 } // namespace zypp
411 // vim: set ts=8 sts=2 sw=2 ai noet: