Merge pull request #23 from openSUSE/drop_package_manager
[platform/upstream/libzypp.git] / zypp / MediaSetAccess.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9
10 #include <iostream>
11 #include <fstream>
12
13 #include "zypp/base/LogTools.h"
14 #include "zypp/base/Regex.h"
15 #include "zypp/base/UserRequestException.h"
16 #include "zypp/ZYppCallbacks.h"
17 #include "zypp/MediaSetAccess.h"
18 #include "zypp/PathInfo.h"
19 //#include "zypp/source/MediaSetAccessReportReceivers.h"
20
21 using namespace std;
22
23 ///////////////////////////////////////////////////////////////////
24 namespace zypp
25 { /////////////////////////////////////////////////////////////////
26
27 IMPL_PTR_TYPE(MediaSetAccess);
28
29 ///////////////////////////////////////////////////////////////////
30
31   MediaSetAccess::MediaSetAccess(const Url &url,
32                                  const Pathname & prefered_attach_point)
33       : _url(url)
34       , _prefAttachPoint(prefered_attach_point)
35   {}
36
37   MediaSetAccess::MediaSetAccess(const std::string & label_r,
38                                  const Url &url,
39                                  const Pathname & prefered_attach_point)
40       : _url(url)
41       , _prefAttachPoint(prefered_attach_point)
42       , _label( label_r )
43   {}
44
45   MediaSetAccess::~MediaSetAccess()
46   {
47     try
48     {
49       release();
50     }
51     catch(...) {} // don't let exception escape a dtor.
52   }
53
54
55   void MediaSetAccess::setVerifier( unsigned media_nr, media::MediaVerifierRef verifier )
56   {
57     if (_medias.find(media_nr) != _medias.end())
58     {
59       // the media already exists, set theverifier
60       media::MediaAccessId id = _medias[media_nr];
61       media::MediaManager media_mgr;
62       media_mgr.addVerifier( id, verifier );
63       // remove any saved verifier for this media
64       _verifiers.erase(media_nr);
65     }
66     else
67     {
68       // save the verifier in the map, and set it when
69       // the media number is first attached
70       _verifiers[media_nr] = verifier;
71     }
72   }
73
74   void MediaSetAccess::releaseFile( const OnMediaLocation & on_media_file )
75   {
76     releaseFile( on_media_file.filename(), on_media_file.medianr() );
77   }
78
79   void MediaSetAccess::releaseFile( const Pathname & file, unsigned media_nr)
80   {
81     media::MediaManager media_mgr;
82     media::MediaAccessId media;
83
84     media = getMediaAccessId( media_nr);
85     DBG << "Going to release file " << file
86         << " from media number " << media_nr << endl;
87
88     if ( ! media_mgr.isAttached(media) )
89       return; //disattached media is free
90
91     media_mgr.releaseFile (media, file);
92   }
93
94   void MediaSetAccess::dirInfo( filesystem::DirContent &retlist, const Pathname &dirname,
95                                 bool dots, unsigned media_nr )
96   {
97     media::MediaManager media_mgr;
98     media::MediaAccessId media;
99     media = getMediaAccessId(media_nr);
100
101     // try to attach the media
102     if ( ! media_mgr.isAttached(media) )
103         media_mgr.attach(media);
104
105     media_mgr.dirInfo(media, retlist, dirname, dots);
106   }
107
108   struct ProvideFileOperation
109   {
110     Pathname result;
111     void operator()( media::MediaAccessId media, const Pathname &file )
112     {
113       media::MediaManager media_mgr;
114       media_mgr.provideFile(media, file);
115       result = media_mgr.localPath(media, file);
116     }
117   };
118
119   struct ProvideDirTreeOperation
120   {
121     Pathname result;
122     void operator()( media::MediaAccessId media, const Pathname &file )
123     {
124       media::MediaManager media_mgr;
125       media_mgr.provideDirTree(media, file);
126       result = media_mgr.localPath(media, file);
127     }
128   };
129
130   struct ProvideDirOperation
131   {
132     Pathname result;
133     void operator()( media::MediaAccessId media, const Pathname &file )
134     {
135       media::MediaManager media_mgr;
136       media_mgr.provideDir(media, file);
137       result = media_mgr.localPath(media, file);
138     }
139   };
140
141   struct ProvideFileExistenceOperation
142   {
143     bool result;
144     ProvideFileExistenceOperation()
145         : result(false)
146     {}
147
148     void operator()( media::MediaAccessId media, const Pathname &file )
149     {
150       media::MediaManager media_mgr;
151       result = media_mgr.doesFileExist(media, file);
152     }
153   };
154
155
156
157   Pathname MediaSetAccess::provideFile( const OnMediaLocation & resource, ProvideFileOptions options, const Pathname &deltafile )
158   {
159     ProvideFileOperation op;
160     provide( boost::ref(op), resource, options, deltafile );
161     return op.result;
162   }
163
164   Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, ProvideFileOptions options )
165   {
166     OnMediaLocation resource;
167     ProvideFileOperation op;
168     resource.setLocation(file, media_nr);
169     provide( boost::ref(op), resource, options, Pathname() );
170     return op.result;
171   }
172
173   bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
174   {
175     ProvideFileExistenceOperation op;
176     OnMediaLocation resource;
177     resource.setLocation(file, media_nr);
178     provide( boost::ref(op), resource, PROVIDE_DEFAULT, Pathname());
179     return op.result;
180   }
181
182   void MediaSetAccess::provide( ProvideOperation op,
183                                 const OnMediaLocation &resource,
184                                 ProvideFileOptions options,
185                                 const Pathname &deltafile )
186   {
187     Pathname file(resource.filename());
188     unsigned media_nr(resource.medianr());
189
190     callback::SendReport<media::MediaChangeReport> report;
191     media::MediaManager media_mgr;
192
193     media::MediaAccessId media;
194
195     do
196     {
197       // get the mediaId, but don't try to attach it here
198       media = getMediaAccessId( media_nr);
199       bool deltafileset = false;
200
201       try
202       {
203         DBG << "Going to try to provide " << (resource.optional() ? "optional" : "") << " file " << file
204             << " from media number " << media_nr << endl;
205         // try to attach the media
206         if ( ! media_mgr.isAttached(media) )
207           media_mgr.attach(media);
208         media_mgr.setDeltafile(media, deltafile);
209         deltafileset = true;
210         op(media, file);
211         media_mgr.setDeltafile(media, Pathname());
212         break;
213       }
214       catch ( media::MediaException & excp )
215       {
216         ZYPP_CAUGHT(excp);
217         if (deltafileset)
218           media_mgr.setDeltafile(media, Pathname());
219         media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
220         unsigned int devindex = 0;
221         vector<string> devices;
222         media_mgr.getDetectedDevices(media, devices, devindex);
223
224         do
225         {
226           if (user != media::MediaChangeReport::EJECT) // no use in calling this again
227           {
228             DBG << "Media couldn't provide file " << file << " , releasing." << endl;
229             try
230             {
231               media_mgr.release(media);
232             }
233             catch (const Exception & excpt_r)
234             {
235                 ZYPP_CAUGHT(excpt_r);
236                 MIL << "Failed to release media " << media << endl;
237             }
238           }
239
240           // set up the reason
241           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
242
243           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
244               typeid(excp) == typeid( media::MediaNotAFileException ) )
245           {
246             reason = media::MediaChangeReport::NOT_FOUND;
247           }
248           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
249               typeid(excp) == typeid( media::MediaNotAttachedException) )
250           {
251             reason = media::MediaChangeReport::WRONG;
252           }
253           else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
254                    typeid(excp) == typeid( media::MediaTemporaryProblemException))
255           {
256             reason = media::MediaChangeReport::IO_SOFT;
257           }
258
259           // Propagate the original error if _no_ callback receiver is connected, or
260           // non_interactive mode (for optional files) is used (except for wrong media).
261           if ( ! callback::SendReport<media::MediaChangeReport>::connected()
262              || (( options & PROVIDE_NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG ) )
263           {
264               MIL << "Can't provide file. Non-Interactive mode." << endl;
265               ZYPP_RETHROW(excp);
266           }
267           else
268           {
269             // release all media before requesting another (#336881)
270             media_mgr.releaseAll();
271
272             user = report->requestMedia (
273               _url,
274               media_nr,
275               _label,
276               reason,
277               excp.asUserString(),
278               devices,
279               devindex
280             );
281           }
282
283           MIL << "ProvideFile exception caught, callback answer: " << user << endl;
284
285           if( user == media::MediaChangeReport::ABORT )
286           {
287             DBG << "Aborting" << endl;
288             AbortRequestException aexcp("Aborting requested by user");
289             aexcp.remember(excp);
290             ZYPP_THROW(aexcp);
291           }
292           else if ( user == media::MediaChangeReport::IGNORE )
293           {
294             DBG << "Skipping" << endl;
295             SkipRequestException nexcp("User-requested skipping of a file");
296             nexcp.remember(excp);
297             ZYPP_THROW(nexcp);
298           }
299           else if ( user == media::MediaChangeReport::EJECT )
300           {
301             DBG << "Eject: try to release" << endl;
302             media_mgr.releaseAll();
303             // eject
304             media_mgr.release (media,
305               devindex < devices.size() ? devices[devindex] : "");
306           }
307           else if ( user == media::MediaChangeReport::RETRY  ||
308             user == media::MediaChangeReport::CHANGE_URL )
309           {
310             // retry
311             DBG << "Going to try again" << endl;
312             // invalidate current media access id
313             media_mgr.close(media);
314             _medias.erase(media_nr);
315
316             // not attaching, media set will do that for us
317             // this could generate uncaught exception (#158620)
318             break;
319           }
320           else
321           {
322             DBG << "Don't know, let's ABORT" << endl;
323             ZYPP_RETHROW ( excp );
324           }
325         } while( user == media::MediaChangeReport::EJECT );
326       }
327
328       // retry or change URL
329     } while( true );
330   }
331
332   Pathname MediaSetAccess::provideDir(const Pathname & dir,
333                                       bool recursive,
334                                       unsigned media_nr,
335                                       ProvideFileOptions options )
336   {
337     OnMediaLocation resource;
338     resource.setLocation(dir, media_nr);
339     if ( recursive )
340     {
341         ProvideDirTreeOperation op;
342         provide( boost::ref(op), resource, options, Pathname());
343         return op.result;
344     }
345     ProvideDirOperation op;
346     provide( boost::ref(op), resource, options, Pathname());
347     return op.result;
348   }
349
350   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
351   {
352     media::MediaManager media_mgr;
353
354     if (_medias.find(medianr) != _medias.end())
355     {
356       media::MediaAccessId id = _medias[medianr];
357       return id;
358     }
359     Url url;
360     url = rewriteUrl (_url, medianr);
361     media::MediaAccessId id = media_mgr.open(url, _prefAttachPoint);
362     _medias[medianr] = id;
363
364     try
365     {
366       if (_verifiers.find(medianr) != _verifiers.end())
367       {
368         // a verifier is set for this media
369         // FIXME check the case where the verifier exists
370         // but we have no access id for the media
371         media::MediaAccessId id = _medias[medianr];
372         media::MediaManager media_mgr;
373         media_mgr.delVerifier(id);
374         media_mgr.addVerifier( id, _verifiers[medianr] );
375         // remove any saved verifier for this media
376         _verifiers.erase(medianr);
377       }
378     }
379     catch ( const Exception &e )
380     {
381       ZYPP_CAUGHT(e);
382       WAR << "Verifier not found" << endl;
383     }
384
385     return id;
386   }
387
388
389   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
390   {
391     std::string scheme = url_r.getScheme();
392     if (scheme == "cd" || scheme == "dvd")
393       return url_r;
394
395     DBG << "Rewriting url " << url_r << endl;
396
397     if( scheme == "iso")
398     {
399       // TODO the iso parameter will not be required in the future, this
400       // code has to be adapted together with the MediaISO change.
401       // maybe some MediaISOURL interface should be used.
402       std::string isofile = url_r.getQueryParam("iso");
403       str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
404
405       str::smatch what;
406       if(str::regex_match(isofile, what, e))
407       {
408         Url url( url_r);
409         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
410         url.setQueryParam("iso", isofile);
411         DBG << "Url rewrite result: " << url << endl;
412         return url;
413       }
414     }
415     else
416     {
417       std::string pathname = url_r.getPathName();
418       str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
419       str::smatch what;
420       if(str::regex_match(pathname, what, e))
421       {
422         Url url( url_r);
423         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
424         url.setPathName(pathname);
425         DBG << "Url rewrite result: " << url << endl;
426         return url;
427       }
428     }
429     return url_r;
430   }
431
432   void MediaSetAccess::release()
433   {
434     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
435     media::MediaManager manager;
436     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
437       manager.release(m->second, "");
438   }
439
440   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
441   {
442     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
443     return str;
444   }
445
446 /////////////////////////////////////////////////////////////////
447 } // namespace zypp
448 ///////////////////////////////////////////////////////////////////