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