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