Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / media / MediaISO.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaISO.cc
10  *
11  */
12 #include <iostream>
13
14 #include "zypp/base/Logger.h"
15 #include "zypp/media/Mount.h"
16
17 #include "zypp/media/MediaISO.h"
18
19
20 #define LOSETUP_TOOL_PATH "/sbin/losetup"
21
22 using std::string;
23 using std::endl;
24
25 //////////////////////////////////////////////////////////////////////
26 namespace zypp
27 { ////////////////////////////////////////////////////////////////////
28
29   ////////////////////////////////////////////////////////////////////
30   namespace media
31   { //////////////////////////////////////////////////////////////////
32
33     ///////////////////////////////////////////////////////////////////
34     //
35     // MediaISO Url:
36     //
37     //   Schema: iso
38     //   Path name: subdir to the location of desired files inside
39     //              of the ISO.
40     //   Query parameters:
41     //     url:        The iso filename source media url pointing
42     //                 to a directory containing the ISO file.
43     //     mnt:        Prefered attach point for source media url.
44     //     iso:        The name of the iso file.
45     //     filesystem: Optional, defaults to "auto".
46     //
47     ///////////////////////////////////////////////////////////////////
48     MediaISO::MediaISO(const Url      &url_r,
49                        const Pathname &attach_point_hint_r)
50       : MediaHandler(url_r, attach_point_hint_r,
51                      url_r.getPathName(), // urlpath below attachpoint
52                      false)               // does_download
53     {
54       MIL << "MediaISO::MediaISO(" << url_r << ", "
55           << attach_point_hint_r << ")" << std::endl;
56
57       _isofile    = _url.getQueryParam("iso");
58       if( _isofile.empty())
59       {
60         ERR << "Media url does not contain iso filename" << std::endl;
61         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
62       }
63
64       _filesystem = _url.getQueryParam("filesystem");
65       if( _filesystem.empty())
66         _filesystem = "auto";
67
68       std::string arg;
69       zypp::Url   src;
70       try
71       {
72         // this percent-decodes the query parameter, it must be later encoded
73         // again before used in a Url object
74         arg = _url.getQueryParam("url");
75         if( arg.empty() && _isofile.dirname().absolute())
76         {
77           src = std::string("dir:///");
78           src.setPathName(url::encode(_isofile.dirname().asString(), URL_SAFE_CHARS));
79           _isofile = _isofile.basename();
80         }
81         else
82         {
83           src = url::encode(arg, URL_SAFE_CHARS);
84         }
85       }
86       catch(const zypp::url::UrlException &e)
87       {
88         ZYPP_CAUGHT(e);
89         ERR << "Unable to parse iso filename source media url" << std::endl;
90         MediaBadUrlException ne(_url);
91         ne.remember(e);
92         ZYPP_THROW(ne);
93       }
94       if( !src.isValid())
95       {
96         ERR << "Invalid iso filename source media url" << std::endl;
97         ZYPP_THROW(MediaBadUrlException(src));
98       }
99       if( src.getScheme() == "iso")
100       {
101         ERR << "ISO filename source media url with iso scheme (nested iso): "
102             << src.asString() << std::endl;
103         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
104       }
105       else
106       if( !(src.getScheme() == "hd"   ||
107             src.getScheme() == "dir"  ||
108             src.getScheme() == "file" ||
109             src.getScheme() == "nfs"  ||
110             src.getScheme() == "nfs4" ||
111             src.getScheme() == "smb"  ||
112             src.getScheme() == "cifs"))
113       {
114         ERR << "ISO filename source media url scheme is not supported: "
115             << src.asString() << std::endl;
116         ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
117       }
118
119       MediaManager manager;
120
121       _parentId = manager.open(src, _url.getQueryParam("mnt"));
122     }
123
124     // ---------------------------------------------------------------
125     MediaISO::~MediaISO()
126     {
127       try
128       {
129         release();
130
131         if( _parentId)
132         {
133           DBG << "Closing parent handler..." << std::endl;
134           MediaManager manager;
135           if(manager.isOpen(_parentId))
136             manager.close(_parentId);
137           _parentId = 0;
138         }
139       }
140       catch( ... )
141       {}
142     }
143
144     // ---------------------------------------------------------------
145     bool
146     MediaISO::isAttached() const
147     {
148       return checkAttached(false);
149     }
150
151     // ---------------------------------------------------------------
152     string MediaISO::findUnusedLoopDevice()
153     {
154       const char* argv[] =
155       {
156         LOSETUP_TOOL_PATH,
157         "-f",
158         NULL
159       };
160       ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
161
162       string out = losetup.receiveLine();
163       string device = out.substr(0, out.size() - 1); // remove the trailing endl
164       for(; out.length(); out = losetup.receiveLine())
165         DBG << "losetup: " << out;
166
167       if (losetup.close() != 0)
168       {
169         ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
170         ZYPP_THROW(MediaNoLoopDeviceException(_url));
171       }
172
173       DBG << "found " << device << endl;
174       return device;
175     }
176
177     // ---------------------------------------------------------------
178     void MediaISO::attachTo(bool next)
179     {
180       if(next)
181         ZYPP_THROW(MediaNotSupportedException(_url));
182
183       MediaManager manager;
184       manager.attach(_parentId);
185
186       try
187       {
188         manager.provideFile(_parentId, _isofile);
189       }
190       catch(const MediaException &e1)
191       {
192         ZYPP_CAUGHT(e1);
193         try
194         {
195           manager.release(_parentId);
196         }
197         catch(const MediaException &e2)
198         {
199           ZYPP_CAUGHT(e2);
200         }
201
202         MediaMountException e3(
203           "Unable to find iso filename on source media",
204           _url.asString(), attachPoint().asString()
205         );
206         e3.remember(e1);
207         ZYPP_THROW(e3);
208       }
209
210       // if the provided file is a symlink, expand it (#274651)
211       // (this will probably work only for file/dir and cd/dvd schemes)
212       Pathname isofile = expandlink(manager.localPath(_parentId, _isofile));
213       if( isofile.empty() || !PathInfo(isofile).isFile())
214       {
215         ZYPP_THROW(MediaNotSupportedException(_url));
216       }
217
218       //! \todo make this thread-safe - another thread might pick up the same device
219       string loopdev = findUnusedLoopDevice(); // (bnc #428009)
220
221       MediaSourceRef media( new MediaSource("iso",  loopdev));
222       PathInfo dinfo(loopdev);
223       if( dinfo.isBlk())
224       {
225         media->maj_nr = dinfo.devMajor();
226         media->min_nr = dinfo.devMinor();
227       }
228       else
229         ERR << loopdev << " is not a block device" << endl;
230
231       AttachedMedia  ret( findAttachedMedia( media));
232       if( ret.mediaSource &&
233           ret.attachPoint &&
234           !ret.attachPoint->empty())
235       {
236         DBG << "Using a shared media "
237             << ret.mediaSource->name
238             << " attached on "
239             << ret.attachPoint->path
240             << std::endl;
241         removeAttachPoint();
242         setAttachPoint(ret.attachPoint);
243         setMediaSource(ret.mediaSource);
244         return;
245       }
246
247       if( !isUseableAttachPoint( attachPoint() ) )
248       {
249         setAttachPoint( createAttachPoint(), true );
250       }
251       std::string mountpoint( attachPoint().asString() );
252       std::string mountopts("ro,loop=" + loopdev);
253
254       Mount mount;
255       mount.mount(isofile.asString(), mountpoint,
256                   _filesystem, mountopts);
257
258       setMediaSource(media);
259
260       // wait for /etc/mtab update ...
261       // (shouldn't be needed)
262       int limit = 3;
263       bool mountsucceeded;
264       while( !(mountsucceeded=isAttached()) && --limit)
265       {
266         sleep(1);
267       }
268
269       if( !mountsucceeded)
270       {
271         setMediaSource(MediaSourceRef());
272         try
273         {
274           mount.umount(attachPoint().asString());
275           manager.release(_parentId);
276         }
277         catch (const MediaException & excpt_r)
278         {
279           ZYPP_CAUGHT(excpt_r);
280         }
281         ZYPP_THROW(MediaMountException(
282           "Unable to verify that the media was mounted",
283           isofile.asString(), mountpoint
284         ));
285       }
286     }
287
288     // ---------------------------------------------------------------
289
290     void MediaISO::releaseFrom(const std::string & ejectDev)
291     {
292       Mount mount;
293       mount.umount(attachPoint().asString());
294
295       if( _parentId)
296       {
297         // Unmounting the iso already succeeded,
298         // so don't let exceptions escape.
299         MediaManager manager;
300         try
301         {
302           manager.release(_parentId);
303         }
304         catch ( const Exception & excpt_r )
305         {
306           ZYPP_CAUGHT( excpt_r );
307           WAR << "Not been able to cleanup the parent mount." << endl;
308         }
309       }
310       // else:
311       // the media manager has reset the _parentId
312       // and will destroy the parent handler itself.
313     }
314
315     // ---------------------------------------------------------------
316     void MediaISO::getFile(const Pathname &filename) const
317     {
318       MediaHandler::getFile(filename);
319     }
320
321     // ---------------------------------------------------------------
322     void MediaISO::getDir(const Pathname &dirname,
323                            bool            recurse_r) const
324     {
325       MediaHandler::getDir(dirname, recurse_r);
326     }
327
328     // ---------------------------------------------------------------
329     void MediaISO::getDirInfo(std::list<std::string> &retlist,
330                                const Pathname         &dirname,
331                                bool                    dots) const
332     {
333       MediaHandler::getDirInfo( retlist, dirname, dots );
334     }
335
336     // ---------------------------------------------------------------
337     void MediaISO::getDirInfo(filesystem::DirContent &retlist,
338                                const Pathname         &dirname,
339                                bool                    dots) const
340     {
341       MediaHandler::getDirInfo(retlist, dirname, dots);
342     }
343
344     bool MediaISO::getDoesFileExist( const Pathname & filename ) const
345     {
346       return MediaHandler::getDoesFileExist( filename );
347     }
348
349     //////////////////////////////////////////////////////////////////
350   } // namespace media
351   ////////////////////////////////////////////////////////////////////
352
353   ////////////////////////////////////////////////////////////////////
354 } // namespace zypp
355 //////////////////////////////////////////////////////////////////////
356
357 // vim: set ts=2 sts=2 sw=2 ai et:
358