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