dc7ea9937d9b57d72ddb1eb42ef253764aaaead5
[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 #if 1
106       else
107       if( !(src.getScheme() == "hd"   ||
108             src.getScheme() == "dir"  ||
109             src.getScheme() == "file" ||
110             src.getScheme() == "nfs"  ||
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 #endif
119
120       MediaManager manager;
121
122       _parentId = manager.open(src, _url.getQueryParam("mnt"));
123     }
124
125     // ---------------------------------------------------------------
126     MediaISO::~MediaISO()
127     {
128       try
129       {
130         release();
131
132         if( _parentId)
133         {
134           DBG << "Closing parent handler..." << std::endl;
135           MediaManager manager;
136           if(manager.isOpen(_parentId))
137             manager.close(_parentId);
138           _parentId = 0;
139         }
140       }
141       catch( ... )
142       {}
143     }
144
145     // ---------------------------------------------------------------
146     bool
147     MediaISO::isAttached() const
148     {
149       return checkAttached(false);
150     }
151
152     // ---------------------------------------------------------------
153     string MediaISO::findUnusedLoopDevice()
154     {
155       const char* argv[] =
156       {
157         LOSETUP_TOOL_PATH,
158         "-f",
159         NULL
160       };
161       ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
162
163       string out = losetup.receiveLine();
164       string device = out.substr(0, out.size() - 1); // remove the trailing endl
165       for(; out.length(); out = losetup.receiveLine())
166         DBG << "losetup: " << out;
167
168       if (losetup.close() != 0)
169       {
170         ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
171         ZYPP_THROW(MediaNoLoopDeviceException(_url));
172       }
173
174       DBG << "found " << device << endl;
175       return device;
176     }
177
178     // ---------------------------------------------------------------
179     void MediaISO::attachTo(bool next)
180     {
181       if(next)
182         ZYPP_THROW(MediaNotSupportedException(_url));
183
184       MediaManager manager;
185       manager.attach(_parentId, false);
186
187       try
188       {
189         manager.provideFile(_parentId, _isofile);
190       }
191       catch(const MediaException &e1)
192       {
193         ZYPP_CAUGHT(e1);
194         try
195         {
196           manager.release(_parentId);
197         }
198         catch(const MediaException &e2)
199         {
200           ZYPP_CAUGHT(e2);
201         }
202
203         MediaMountException e3(
204           "Unable to find iso filename on source media",
205           _url.asString(), attachPoint().asString()
206         );
207         e3.remember(e1);
208         ZYPP_THROW(e3);
209       }
210
211       // if the provided file is a symlink, expand it (#274651)
212       // (this will probably work only for file/dir and cd/dvd schemes)
213       Pathname isofile = expandlink(manager.localPath(_parentId, _isofile));
214       if( isofile.empty() || !PathInfo(isofile).isFile())
215       {
216         ZYPP_THROW(MediaNotSupportedException(_url));
217       }
218
219       //! \todo make this thread-safe - another thread might pick up the same device
220       string loopdev = findUnusedLoopDevice(); // (bnc #428009)
221
222       MediaSourceRef media( new MediaSource("iso",  loopdev));
223       PathInfo dinfo(loopdev);
224       if( dinfo.isBlk())
225       {
226         media->maj_nr = dinfo.major();
227         media->min_nr = dinfo.minor();
228       }
229       else
230         ERR << loopdev << " is not a block device" << endl;
231
232       AttachedMedia  ret( findAttachedMedia( media));
233       if( ret.mediaSource &&
234           ret.attachPoint &&
235           !ret.attachPoint->empty())
236       {
237         DBG << "Using a shared media "
238             << ret.mediaSource->name
239             << " attached on "
240             << ret.attachPoint->path
241             << std::endl;
242         removeAttachPoint();
243         setAttachPoint(ret.attachPoint);
244         setMediaSource(ret.mediaSource);
245         return;
246       }
247
248       std::string mountpoint = attachPoint().asString();
249       if( !isUseableAttachPoint(attachPoint()))
250       {
251         mountpoint = createAttachPoint().asString();
252         if( mountpoint.empty())
253           ZYPP_THROW( MediaBadAttachPointException(url()));
254         setAttachPoint( mountpoint, true);
255       }
256
257       std::string mountopts("ro,loop=" + loopdev);
258
259       Mount mount;
260       mount.mount(isofile.asString(), mountpoint,
261                   _filesystem, mountopts);
262
263       setMediaSource(media);
264
265       // wait for /etc/mtab update ...
266       // (shouldn't be needed)
267       int limit = 3;
268       bool mountsucceeded;
269       while( !(mountsucceeded=isAttached()) && --limit)
270       {
271         sleep(1);
272       }
273
274       if( !mountsucceeded)
275       {
276         setMediaSource(MediaSourceRef());
277         try
278         {
279           mount.umount(attachPoint().asString());
280           manager.release(_parentId);
281         }
282         catch (const MediaException & excpt_r)
283         {
284           ZYPP_CAUGHT(excpt_r);
285         }
286         ZYPP_THROW(MediaMountException(
287           "Unable to verify that the media was mounted",
288           isofile.asString(), mountpoint
289         ));
290       }
291     }
292
293     // ---------------------------------------------------------------
294
295     void MediaISO::releaseFrom(const std::string & ejectDev)
296     {
297       Mount mount;
298       mount.umount(attachPoint().asString());
299
300       if( _parentId)
301       {
302         MediaManager manager;
303         manager.release(_parentId);
304       }
305       // else:
306       // the media manager has reset the _parentId
307       // and will destroy the parent handler itself.
308     }
309
310     // ---------------------------------------------------------------
311     void MediaISO::getFile(const Pathname &filename) const
312     {
313       MediaHandler::getFile(filename);
314     }
315
316     // ---------------------------------------------------------------
317     void MediaISO::getDir(const Pathname &dirname,
318                            bool            recurse_r) const
319     {
320       MediaHandler::getDir(dirname, recurse_r);
321     }
322
323     // ---------------------------------------------------------------
324     void MediaISO::getDirInfo(std::list<std::string> &retlist,
325                                const Pathname         &dirname,
326                                bool                    dots) const
327     {
328       MediaHandler::getDirInfo( retlist, dirname, dots );
329     }
330
331     // ---------------------------------------------------------------
332     void MediaISO::getDirInfo(filesystem::DirContent &retlist,
333                                const Pathname         &dirname,
334                                bool                    dots) const
335     {
336       MediaHandler::getDirInfo(retlist, dirname, dots);
337     }
338
339     bool MediaISO::getDoesFileExist( const Pathname & filename ) const
340     {
341       return MediaHandler::getDoesFileExist( filename );
342     }
343
344     //////////////////////////////////////////////////////////////////
345   } // namespace media
346   ////////////////////////////////////////////////////////////////////
347
348   ////////////////////////////////////////////////////////////////////
349 } // namespace zypp
350 //////////////////////////////////////////////////////////////////////
351
352 // vim: set ts=2 sts=2 sw=2 ai et:
353