Imported Upstream version 16.5.0
[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   Pathname MediaSetAccess::provideOptionalFile( const Pathname & file, unsigned media_nr )
174   {
175     try
176     {
177       if ( doesFileExist( file, media_nr ) )
178         return provideFile( file, media_nr, PROVIDE_NON_INTERACTIVE );
179     }
180     catch ( const media::MediaFileNotFoundException & excpt_r )
181     { ZYPP_CAUGHT( excpt_r ); }
182     catch ( const media::MediaNotAFileException & excpt_r )
183     { ZYPP_CAUGHT( excpt_r ); }
184    return Pathname();
185   }
186
187   bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
188   {
189     ProvideFileExistenceOperation op;
190     OnMediaLocation resource;
191     resource.setLocation(file, media_nr);
192     provide( boost::ref(op), resource, PROVIDE_DEFAULT, Pathname());
193     return op.result;
194   }
195
196   void MediaSetAccess::provide( ProvideOperation op,
197                                 const OnMediaLocation &resource,
198                                 ProvideFileOptions options,
199                                 const Pathname &deltafile )
200   {
201     Pathname file(resource.filename());
202     unsigned media_nr(resource.medianr());
203
204     callback::SendReport<media::MediaChangeReport> report;
205     media::MediaManager media_mgr;
206
207     media::MediaAccessId media;
208
209     do
210     {
211       // get the mediaId, but don't try to attach it here
212       media = getMediaAccessId( media_nr);
213       bool deltafileset = false;
214
215       try
216       {
217         DBG << "Going to try to provide " << (resource.optional() ? "optional" : "") << " file " << file
218             << " from media number " << media_nr << endl;
219         // try to attach the media
220         if ( ! media_mgr.isAttached(media) )
221           media_mgr.attach(media);
222         media_mgr.setDeltafile(media, deltafile);
223         deltafileset = true;
224         op(media, file);
225         media_mgr.setDeltafile(media, Pathname());
226         break;
227       }
228       catch ( media::MediaException & excp )
229       {
230         ZYPP_CAUGHT(excp);
231         if (deltafileset)
232           media_mgr.setDeltafile(media, Pathname());
233         media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
234         unsigned int devindex = 0;
235         vector<string> devices;
236         media_mgr.getDetectedDevices(media, devices, devindex);
237
238         do
239         {
240           if (user != media::MediaChangeReport::EJECT) // no use in calling this again
241           {
242             DBG << "Media couldn't provide file " << file << " , releasing." << endl;
243             try
244             {
245               media_mgr.release(media);
246             }
247             catch (const Exception & excpt_r)
248             {
249                 ZYPP_CAUGHT(excpt_r);
250                 MIL << "Failed to release media " << media << endl;
251             }
252           }
253
254           // set up the reason
255           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
256
257           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
258               typeid(excp) == typeid( media::MediaNotAFileException ) )
259           {
260             reason = media::MediaChangeReport::NOT_FOUND;
261           }
262           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
263               typeid(excp) == typeid( media::MediaNotAttachedException) )
264           {
265             reason = media::MediaChangeReport::WRONG;
266           }
267           else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
268                    typeid(excp) == typeid( media::MediaTemporaryProblemException))
269           {
270             reason = media::MediaChangeReport::IO_SOFT;
271           }
272
273           // Propagate the original error if _no_ callback receiver is connected, or
274           // non_interactive mode (for optional files) is used (except for wrong media).
275           if ( ! callback::SendReport<media::MediaChangeReport>::connected()
276              || (( options & PROVIDE_NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG ) )
277           {
278               MIL << "Can't provide file. Non-Interactive mode." << endl;
279               ZYPP_RETHROW(excp);
280           }
281           else
282           {
283             // release all media before requesting another (#336881)
284             media_mgr.releaseAll();
285
286             user = report->requestMedia (
287               _url,
288               media_nr,
289               _label,
290               reason,
291               excp.asUserHistory(),
292               devices,
293               devindex
294             );
295           }
296
297           MIL << "ProvideFile exception caught, callback answer: " << user << endl;
298
299           if( user == media::MediaChangeReport::ABORT )
300           {
301             DBG << "Aborting" << endl;
302             AbortRequestException aexcp("Aborting requested by user");
303             aexcp.remember(excp);
304             ZYPP_THROW(aexcp);
305           }
306           else if ( user == media::MediaChangeReport::IGNORE )
307           {
308             DBG << "Skipping" << endl;
309             SkipRequestException nexcp("User-requested skipping of a file");
310             nexcp.remember(excp);
311             ZYPP_THROW(nexcp);
312           }
313           else if ( user == media::MediaChangeReport::EJECT )
314           {
315             DBG << "Eject: try to release" << endl;
316             try
317             {
318               media_mgr.releaseAll();
319               media_mgr.release (media, devindex < devices.size() ? devices[devindex] : "");
320             }
321             catch ( const Exception & e)
322             {
323               ZYPP_CAUGHT(e);
324             }
325           }
326           else if ( user == media::MediaChangeReport::RETRY  ||
327             user == media::MediaChangeReport::CHANGE_URL )
328           {
329             // retry
330             DBG << "Going to try again" << endl;
331             // invalidate current media access id
332             media_mgr.close(media);
333             _medias.erase(media_nr);
334
335             // not attaching, media set will do that for us
336             // this could generate uncaught exception (#158620)
337             break;
338           }
339           else
340           {
341             DBG << "Don't know, let's ABORT" << endl;
342             ZYPP_RETHROW ( excp );
343           }
344         } while( user == media::MediaChangeReport::EJECT );
345       }
346
347       // retry or change URL
348     } while( true );
349   }
350
351   Pathname MediaSetAccess::provideDir(const Pathname & dir,
352                                       bool recursive,
353                                       unsigned media_nr,
354                                       ProvideFileOptions options )
355   {
356     OnMediaLocation resource;
357     resource.setLocation(dir, media_nr);
358     if ( recursive )
359     {
360         ProvideDirTreeOperation op;
361         provide( boost::ref(op), resource, options, Pathname());
362         return op.result;
363     }
364     ProvideDirOperation op;
365     provide( boost::ref(op), resource, options, Pathname());
366     return op.result;
367   }
368
369   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
370   {
371     if ( _medias.find( medianr ) != _medias.end() )
372     {
373       return _medias[medianr];
374     }
375
376     Url url( medianr > 1 ? rewriteUrl( _url, medianr ) : _url );
377     media::MediaManager media_mgr;
378     media::MediaAccessId id = media_mgr.open( url, _prefAttachPoint );
379     _medias[medianr] = id;
380
381     try
382     {
383       if ( _verifiers.find(medianr) != _verifiers.end() )
384       {
385         // a verifier is set for this media
386         // FIXME check the case where the verifier exists
387         // but we have no access id for the media
388         media_mgr.delVerifier( id );
389         media_mgr.addVerifier( id, _verifiers[medianr] );
390         // remove any saved verifier for this media
391         _verifiers.erase( medianr );
392       }
393     }
394     catch ( const Exception &e )
395     {
396       ZYPP_CAUGHT(e);
397       WAR << "Verifier not found" << endl;
398     }
399
400     return id;
401   }
402
403
404   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
405   {
406     std::string scheme = url_r.getScheme();
407     if (scheme == "cd" || scheme == "dvd")
408       return url_r;
409
410     DBG << "Rewriting url " << url_r << endl;
411
412     if( scheme == "iso")
413     {
414       // TODO the iso parameter will not be required in the future, this
415       // code has to be adapted together with the MediaISO change.
416       // maybe some MediaISOURL interface should be used.
417       std::string isofile = url_r.getQueryParam("iso");
418       str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
419
420       str::smatch what;
421       if(str::regex_match(isofile, what, e))
422       {
423         Url url( url_r);
424         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
425         url.setQueryParam("iso", isofile);
426         DBG << "Url rewrite result: " << url << endl;
427         return url;
428       }
429     }
430     else
431     {
432       std::string pathname = url_r.getPathName();
433       str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
434       str::smatch what;
435       if(str::regex_match(pathname, what, e))
436       {
437         Url url( url_r);
438         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
439         url.setPathName(pathname);
440         DBG << "Url rewrite result: " << url << endl;
441         return url;
442       }
443     }
444     return url_r;
445   }
446
447   void MediaSetAccess::release()
448   {
449     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
450     media::MediaManager manager;
451     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
452       manager.release(m->second, "");
453   }
454
455   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
456   {
457     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
458     return str;
459   }
460
461 /////////////////////////////////////////////////////////////////
462 } // namespace zypp
463 ///////////////////////////////////////////////////////////////////