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