Imported Upstream version 16.3.2
[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.asUserHistory(),
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             try
303             {
304               media_mgr.releaseAll();
305               media_mgr.release (media, devindex < devices.size() ? devices[devindex] : "");
306             }
307             catch ( const Exception & e)
308             {
309               ZYPP_CAUGHT(e);
310             }
311           }
312           else if ( user == media::MediaChangeReport::RETRY  ||
313             user == media::MediaChangeReport::CHANGE_URL )
314           {
315             // retry
316             DBG << "Going to try again" << endl;
317             // invalidate current media access id
318             media_mgr.close(media);
319             _medias.erase(media_nr);
320
321             // not attaching, media set will do that for us
322             // this could generate uncaught exception (#158620)
323             break;
324           }
325           else
326           {
327             DBG << "Don't know, let's ABORT" << endl;
328             ZYPP_RETHROW ( excp );
329           }
330         } while( user == media::MediaChangeReport::EJECT );
331       }
332
333       // retry or change URL
334     } while( true );
335   }
336
337   Pathname MediaSetAccess::provideDir(const Pathname & dir,
338                                       bool recursive,
339                                       unsigned media_nr,
340                                       ProvideFileOptions options )
341   {
342     OnMediaLocation resource;
343     resource.setLocation(dir, media_nr);
344     if ( recursive )
345     {
346         ProvideDirTreeOperation op;
347         provide( boost::ref(op), resource, options, Pathname());
348         return op.result;
349     }
350     ProvideDirOperation op;
351     provide( boost::ref(op), resource, options, Pathname());
352     return op.result;
353   }
354
355   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
356   {
357     if ( _medias.find( medianr ) != _medias.end() )
358     {
359       return _medias[medianr];
360     }
361
362     Url url( medianr > 1 ? rewriteUrl( _url, medianr ) : _url );
363     media::MediaManager media_mgr;
364     media::MediaAccessId id = media_mgr.open( url, _prefAttachPoint );
365     _medias[medianr] = id;
366
367     try
368     {
369       if ( _verifiers.find(medianr) != _verifiers.end() )
370       {
371         // a verifier is set for this media
372         // FIXME check the case where the verifier exists
373         // but we have no access id for the media
374         media_mgr.delVerifier( id );
375         media_mgr.addVerifier( id, _verifiers[medianr] );
376         // remove any saved verifier for this media
377         _verifiers.erase( medianr );
378       }
379     }
380     catch ( const Exception &e )
381     {
382       ZYPP_CAUGHT(e);
383       WAR << "Verifier not found" << endl;
384     }
385
386     return id;
387   }
388
389
390   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
391   {
392     std::string scheme = url_r.getScheme();
393     if (scheme == "cd" || scheme == "dvd")
394       return url_r;
395
396     DBG << "Rewriting url " << url_r << endl;
397
398     if( scheme == "iso")
399     {
400       // TODO the iso parameter will not be required in the future, this
401       // code has to be adapted together with the MediaISO change.
402       // maybe some MediaISOURL interface should be used.
403       std::string isofile = url_r.getQueryParam("iso");
404       str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
405
406       str::smatch what;
407       if(str::regex_match(isofile, what, e))
408       {
409         Url url( url_r);
410         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
411         url.setQueryParam("iso", isofile);
412         DBG << "Url rewrite result: " << url << endl;
413         return url;
414       }
415     }
416     else
417     {
418       std::string pathname = url_r.getPathName();
419       str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
420       str::smatch what;
421       if(str::regex_match(pathname, what, e))
422       {
423         Url url( url_r);
424         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
425         url.setPathName(pathname);
426         DBG << "Url rewrite result: " << url << endl;
427         return url;
428       }
429     }
430     return url_r;
431   }
432
433   void MediaSetAccess::release()
434   {
435     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
436     media::MediaManager manager;
437     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
438       manager.release(m->second, "");
439   }
440
441   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
442   {
443     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
444     return str;
445   }
446
447 /////////////////////////////////////////////////////////////////
448 } // namespace zypp
449 ///////////////////////////////////////////////////////////////////