- support optional deltafile for provideFile and enqueueDigested
[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           // non interactive only bother the user if wrong medium is in the drive
260           // otherwise propagate the error
261           if ( ( options & PROVIDE_NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG)
262           {
263               MIL << "Can't provide file. Non-Interactive mode." << endl;
264               ZYPP_RETHROW(excp);
265           }
266           else
267           {
268             // release all media before requesting another (#336881)
269             media_mgr.releaseAll();
270
271             user = report->requestMedia (
272               _url,
273               media_nr,
274               _label,
275               reason,
276               excp.asUserString(),
277               devices,
278               devindex
279             );
280           }
281
282           MIL << "ProvideFile exception caught, callback answer: " << user << endl;
283
284           if( user == media::MediaChangeReport::ABORT )
285           {
286             DBG << "Aborting" << endl;
287             ZYPP_RETHROW ( excp );
288           }
289           else if ( user == media::MediaChangeReport::IGNORE )
290           {
291             DBG << "Skipping" << endl;
292             SkipRequestException nexcp("User-requested skipping of a file");
293             nexcp.remember(excp);
294             ZYPP_THROW(nexcp);
295           }
296           else if ( user == media::MediaChangeReport::EJECT )
297           {
298             DBG << "Eject: try to release" << endl;
299             media_mgr.releaseAll();
300             // eject
301             media_mgr.release (media,
302               devindex < devices.size() ? devices[devindex] : "");
303           }
304           else if ( user == media::MediaChangeReport::RETRY  ||
305             user == media::MediaChangeReport::CHANGE_URL )
306           {
307             // retry
308             DBG << "Going to try again" << endl;
309             // invalidate current media access id
310             media_mgr.close(media);
311             _medias.erase(media_nr);
312
313             // not attaching, media set will do that for us
314             // this could generate uncaught exception (#158620)
315             break;
316           }
317           else
318           {
319             DBG << "Don't know, let's ABORT" << endl;
320             ZYPP_RETHROW ( excp );
321           }
322         } while( user == media::MediaChangeReport::EJECT );
323       }
324
325       // retry or change URL
326     } while( true );
327   }
328
329   Pathname MediaSetAccess::provideDir(const Pathname & dir,
330                                       bool recursive,
331                                       unsigned media_nr,
332                                       ProvideFileOptions options )
333   {
334     OnMediaLocation resource;
335     resource.setLocation(dir, media_nr);
336     if ( recursive )
337     {
338         ProvideDirTreeOperation op;
339         provide( boost::ref(op), resource, options, Pathname());
340         return op.result;
341     }
342     ProvideDirOperation op;
343     provide( boost::ref(op), resource, options, Pathname());
344     return op.result;
345   }
346
347   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
348   {
349     media::MediaManager media_mgr;
350
351     if (_medias.find(medianr) != _medias.end())
352     {
353       media::MediaAccessId id = _medias[medianr];
354       return id;
355     }
356     Url url;
357     url = rewriteUrl (_url, medianr);
358     media::MediaAccessId id = media_mgr.open(url, _prefAttachPoint);
359     _medias[medianr] = id;
360
361     try
362     {
363       if (_verifiers.find(medianr) != _verifiers.end())
364       {
365         // a verifier is set for this media
366         // FIXME check the case where the verifier exists
367         // but we have no access id for the media
368         media::MediaAccessId id = _medias[medianr];
369         media::MediaManager media_mgr;
370         media_mgr.delVerifier(id);
371         media_mgr.addVerifier( id, _verifiers[medianr] );
372         // remove any saved verifier for this media
373         _verifiers.erase(medianr);
374       }
375     }
376     catch ( const Exception &e )
377     {
378       ZYPP_CAUGHT(e);
379       WAR << "Verifier not found" << endl;
380     }
381
382     return id;
383   }
384
385
386   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
387   {
388     std::string scheme = url_r.getScheme();
389     if (scheme == "cd" || scheme == "dvd")
390       return url_r;
391
392     DBG << "Rewriting url " << url_r << endl;
393
394     if( scheme == "iso")
395     {
396       // TODO the iso parameter will not be required in the future, this
397       // code has to be adapted together with the MediaISO change.
398       // maybe some MediaISOURL interface should be used.
399       std::string isofile = url_r.getQueryParam("iso");
400       str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
401
402       str::smatch what;
403       if(str::regex_match(isofile, what, e))
404       {
405         Url url( url_r);
406         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
407         url.setQueryParam("iso", isofile);
408         DBG << "Url rewrite result: " << url << endl;
409         return url;
410       }
411     }
412     else
413     {
414       std::string pathname = url_r.getPathName();
415       str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
416       str::smatch what;
417       if(str::regex_match(pathname, what, e))
418       {
419         Url url( url_r);
420         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
421         url.setPathName(pathname);
422         DBG << "Url rewrite result: " << url << endl;
423         return url;
424       }
425     }
426     return url_r;
427   }
428
429   void MediaSetAccess::release()
430   {
431     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
432     media::MediaManager manager;
433     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
434       manager.release(m->second, "");
435   }
436
437   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
438   {
439     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
440     return str;
441   }
442
443 /////////////////////////////////////////////////////////////////
444 } // namespace zypp
445 ///////////////////////////////////////////////////////////////////