- special cdrom detection code for SCSI / Virtual CDROMs on iSeries
[platform/upstream/libzypp.git] / zypp / media / MediaCD.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaCD.cc
10  *
11 */
12
13 #include <iostream>
14
15 #include "zypp/base/Logger.h"
16 #include "zypp/ExternalProgram.h"
17 #include "zypp/media/Mount.h"
18 #include "zypp/media/MediaCD.h"
19 #include "zypp/media/MediaManager.h"
20 #include "zypp/Url.h"
21 #include "zypp/target/hal/HalContext.h"
22
23 #include <cstring> // strerror
24 #include <cstdlib> // getenv
25
26 #include <errno.h>
27 #include <dirent.h>
28
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h> // geteuid, ...
33
34 #include <linux/cdrom.h>
35
36 /*
37 ** verify devices names as late as possible (while attach)
38 */
39 #define  DELAYED_VERIFY          1
40
41 /*
42 ** try umount of foreign (user/automounter) media on eject
43 **   0 = don't force, 1 = automounted only, 2 == all
44 */
45 #define  FORCE_RELEASE_FOREIGN   2
46
47 /*
48 ** Reuse foreign (user/automounter) mount points.
49 ** 0 = don't use, 1 = automounted only, 2 = all
50 */
51 #define  REUSE_FOREIGN_MOUNTS    2
52
53 /*
54 ** if to throw exception on eject errors or ignore them
55 */
56 #define  REPORT_EJECT_ERRORS     1
57
58 /*
59 ** If defined to the full path of the eject utility,
60 ** it will be used additionally to the eject-ioctl.
61 */
62 #define EJECT_TOOL_PATH "/bin/eject"
63
64
65 using namespace std;
66
67 namespace zypp {
68   namespace media {
69
70 ///////////////////////////////////////////////////////////////////
71 //
72 //      CLASS NAME : MediaCD
73 //
74 ///////////////////////////////////////////////////////////////////
75
76     ///////////////////////////////////////////////////////////////////
77     //
78     //
79     //  METHOD NAME : MediaCD::MediaCD
80     //  METHOD TYPE : Constructor
81     //
82     //  DESCRIPTION :
83     //
84     MediaCD::MediaCD( const Url &      url_r,
85                   const Pathname & attach_point_hint_r )
86       : MediaHandler( url_r, attach_point_hint_r,
87                     url_r.getPathName(), // urlpath below attachpoint
88                     false )
89       //, does_download
90       , _lastdev(-1), _lastdev_tried(-1)
91     {
92       MIL << "MediaCD::MediaCD(" << url_r << ", "
93           << attach_point_hint_r << ")" << endl;
94
95       if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
96       {
97         ERR << "Unsupported schema in the Url: " << url_r.asString()
98                                                  << std::endl;
99         ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
100       }
101
102 #if !DELAYED_VERIFY
103       DeviceList detected( detectDevices(
104         url_r.getScheme() == "dvd" ? true : false
105       ));
106 #endif
107
108       string devices = _url.getQueryParam("devices");
109       if (!devices.empty())
110       {
111         string::size_type pos;
112         DBG << "parse " << devices << endl;
113         while(!devices.empty())
114         {
115             pos = devices.find(',');
116             string device = devices.substr(0,pos);
117             if (!device.empty())
118             {
119 #if DELAYED_VERIFY
120               MediaSource media("cdrom", device, 0, 0);
121               _devices.push_back( media);
122                DBG << "use device (delayed verify)" << device << endl;
123 #else
124               bool is_ok = false;
125               PathInfo dinfo(device);
126               if( dinfo.isBlk())
127               {
128                 MediaSource media("cdrom", device, dinfo.major(),
129                                                    dinfo.minor());
130                 DeviceList::const_iterator d( detected.begin());
131                 for( ; d != detected.end(); ++d)
132                 {
133                   if( media.equals( *d))
134                   {
135                     is_ok = true;
136                     _devices.push_back( *d);
137                     DBG << "use device " << device << endl;
138                   }
139                 }
140               }
141
142               if( !is_ok)
143               {
144                 ERR << "Device " << device << " is not acceptable "
145                     << "for " << _url.getScheme() << std::endl;
146                 ZYPP_THROW(MediaBadUrlException(_url,
147                   "Invalid device name in URL devices argument"
148                 ));
149               }
150 #endif
151             }
152             if (pos!=string::npos)
153                 devices=devices.substr(pos+1);
154             else
155                 devices.erase();
156         }
157       }
158       else
159       {
160 #if DELAYED_VERIFY
161         DBG << "going to use on-demand device list" << endl;
162         return;
163 #else
164         DBG << "going to use default device list" << endl;
165         //default is /dev/cdrom; for dvd: /dev/dvd if it exists
166         string device( "/dev/cdrom" );
167         if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
168           device = "/dev/dvd";
169         }
170
171         PathInfo dinfo(device);
172         if( dinfo.isBlk())
173         {
174           MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
175
176           DeviceList::const_iterator d( detected.begin());
177           for( ; d != detected.end(); ++d)
178           {
179             // /dev/cdrom or /dev/dvd to the front
180             if( media.equals( *d))
181               _devices.push_front( *d);
182             else
183               _devices.push_back( *d);
184           }
185         }
186         else
187         {
188           // no /dev/cdrom or /dev/dvd link
189           _devices = detected;
190         }
191 #endif
192       }
193
194       if( _devices.empty())
195       {
196         ERR << "Unable to find any cdrom drive for " << _url.asString()
197                                                      << std::endl;
198         ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
199       }
200     }
201
202     ///////////////////////////////////////////////////////////////////
203     //
204     //
205     //  METHOD NAME : MediaCD::openTray
206     //  METHOD TYPE : bool
207     //
208     bool MediaCD::openTray( const std::string & device_r )
209     {
210       int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
211       int res = -1;
212
213       if ( fd != -1)
214       {
215         res = ::ioctl( fd, CDROMEJECT );
216         ::close( fd );
217       }
218
219       if ( res )
220       {
221         if( fd == -1)
222         {
223           WAR << "Unable to open '" << device_r
224               << "' (" << ::strerror( errno ) << ")" << endl;
225         }
226         else
227         {
228           WAR << "Eject " << device_r
229               << " failed (" << ::strerror( errno ) << ")" << endl;
230         }
231
232 #if defined(EJECT_TOOL_PATH)
233         DBG << "Try to eject " << device_r << " using "
234             << EJECT_TOOL_PATH << " utility" << std::endl;
235
236         const char *cmd[3];
237         cmd[0] = EJECT_TOOL_PATH;
238         cmd[1] = device_r.c_str();
239         cmd[2] = NULL;
240         ExternalProgram eject(cmd, ExternalProgram::Stderr_To_Stdout);
241
242         for(std::string out( eject.receiveLine());
243             out.length(); out = eject.receiveLine())
244         {
245           DBG << " " << out;
246         }
247
248         if(eject.close() != 0)
249         {
250           WAR << "Eject of " << device_r << " failed." << std::endl;
251           return false;
252         }
253 #else
254         return false;
255 #endif
256       }
257       MIL << "Eject of " << device_r << " successful." << endl;
258       return true;
259     }
260
261     ///////////////////////////////////////////////////////////////////
262     //
263     //
264     //  METHOD NAME : MediaCD::closeTray
265     //  METHOD TYPE : bool
266     //
267     bool MediaCD::closeTray( const std::string & device_r )
268     {
269       int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
270       if ( fd == -1 ) {
271         WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
272         return false;
273       }
274       int res = ::ioctl( fd, CDROMCLOSETRAY );
275       ::close( fd );
276       if ( res ) {
277         WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
278         return false;
279       }
280       DBG << "Close tray " << device_r << endl;
281       return true;
282     }
283
284     ///////////////////////////////////////////////////////////////////
285     //
286     //
287     //  METHOD NAME : MediaCD::detectDevices
288     //  METHOD TYPE : MediaCD::DeviceList
289     //
290     MediaCD::DeviceList
291     MediaCD::detectDevices(bool supportingDVD)
292     {
293       using namespace zypp::target::hal;
294
295       DeviceList detected;
296       try
297       {
298         HalContext hal(true);
299
300         std::vector<std::string> drv_udis;
301         drv_udis = hal.findDevicesByCapability("storage.cdrom");
302
303         DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
304         for(size_t d = 0; d < drv_udis.size(); d++)
305         {
306           HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
307
308           if( drv)
309           {
310             bool supportsDVD=false;
311             if( supportingDVD)
312             {
313               std::vector<std::string> caps;
314               try {
315                 caps = drv.getCdromCapabilityNames();
316               }
317               catch(const HalException &e)
318               {
319                 ZYPP_CAUGHT(e);
320               }
321
322               std::vector<std::string>::const_iterator ci;
323               for( ci=caps.begin(); ci != caps.end(); ++ci)
324               {
325                 if( *ci == "dvd")
326                   supportsDVD = true;
327               }
328             }
329
330             MediaSource media("cdrom", drv.getDeviceFile(),
331                                        drv.getDeviceMajor(),
332                                        drv.getDeviceMinor());
333             DBG << "Found " << drv_udis[d] << ": "
334                             << media.asString() << std::endl;
335             if( supportingDVD && supportsDVD)
336             {
337               detected.push_front(media);
338             }
339             else
340             {
341               detected.push_back(media);
342             }
343           }
344         }
345       }
346       catch(const zypp::target::hal::HalException &e)
347       {
348         ZYPP_CAUGHT(e);
349       }
350
351       return detected;
352     }
353
354
355     ///////////////////////////////////////////////////////////////////
356     //
357     //
358     //  METHOD NAME : MediaCD::attachTo
359     //  METHOD TYPE : PMError
360     //
361     //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
362     //
363     void MediaCD::attachTo(bool next)
364     {
365       DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
366       if (next && _lastdev == -1)
367         ZYPP_THROW(MediaNotSupportedException(url()));
368
369 #if DELAYED_VERIFY
370       DeviceList detected( detectDevices(
371         _url.getScheme() == "dvd" ? true : false
372       ));
373
374       if(_devices.empty())
375       {
376         DBG << "creating on-demand device list" << endl;
377         //default is /dev/cdrom; for dvd: /dev/dvd if it exists
378         string device( "/dev/cdrom" );
379         if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
380           device = "/dev/dvd";
381         }
382
383         PathInfo dinfo(device);
384         if( dinfo.isBlk())
385         {
386           MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
387
388           DeviceList::const_iterator d( detected.begin());
389           for( ; d != detected.end(); ++d)
390           {
391             // /dev/cdrom or /dev/dvd to the front
392             if( media.equals( *d))
393               _devices.push_front( *d);
394             else
395               _devices.push_back( *d);
396           }
397         }
398         else
399         {
400           // no /dev/cdrom or /dev/dvd link
401           _devices = detected;
402         }
403       }
404 #endif
405
406       Mount mount;
407       string mountpoint = attachPoint().asString();
408       bool mountsucceeded = false;
409       int count = 0;
410       MediaMountException merr;
411
412       string options = _url.getQueryParam("mountoptions");
413       if (options.empty())
414       {
415         options="ro";
416       }
417
418       //TODO: make configurable
419       list<string> filesystems;
420
421       // if DVD, try UDF filesystem before iso9660
422       if ( _url.getScheme() == "dvd" )
423         filesystems.push_back("udf");
424
425       filesystems.push_back("iso9660");
426
427       // try all devices in sequence
428       for (DeviceList::iterator it = _devices.begin()
429         ; !mountsucceeded && it != _devices.end()
430         ; ++it, count++ )
431       {
432         DBG << "count " << count << endl;
433         if (next && count <=_lastdev_tried )
434         {
435                 DBG << "skipping device " << it->name << endl;
436                 continue;
437         }
438
439         _lastdev_tried = count;
440
441 #if DELAYED_VERIFY
442         MediaSource temp( *it);
443         bool        valid=false;
444         PathInfo    dinfo(temp.name);
445         if( dinfo.isBlk())
446         {
447           temp.maj_nr = dinfo.major();
448           temp.min_nr = dinfo.minor();
449
450           DeviceList::const_iterator d( detected.begin());
451           for( ; d != detected.end(); ++d)
452           {
453             if( temp.equals( *d))
454             {
455               valid = true;
456               break;
457             }
458           }
459         }
460         if( !valid)
461         {
462                 DBG << "skipping invalid device: " << it->name << endl;
463                 continue;
464         }
465         MediaSourceRef media( new MediaSource(temp));
466 #else
467         MediaSourceRef media( new MediaSource( *it));
468 #endif
469
470         AttachedMedia ret( findAttachedMedia( media));
471
472         if( ret.mediaSource && ret.attachPoint &&
473            !ret.attachPoint->empty())
474         {
475           DBG << "Using a shared media "
476               << ret.mediaSource->name
477               << " attached on "
478               << ret.attachPoint->path
479               << endl;
480           removeAttachPoint();
481           setAttachPoint(ret.attachPoint);
482           setMediaSource(ret.mediaSource);
483           _lastdev = count;
484           mountsucceeded = true;
485           break;
486         }
487
488 #if REUSE_FOREIGN_MOUNTS > 0
489         {
490           MediaManager  manager;
491           MountEntries  entries( manager.getMountEntries());
492           MountEntries::const_iterator e;
493           for( e = entries.begin(); e != entries.end(); ++e)
494           {
495             bool        is_device = false;
496             std::string dev_path(Pathname(e->src).asString());
497             PathInfo    dev_info;
498
499             if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
500                 dev_info(e->src) && dev_info.isBlk())
501             {
502               is_device = true;
503             }
504
505             if( is_device && media->maj_nr == dev_info.major() &&
506                              media->min_nr == dev_info.minor())
507             {
508               AttachPointRef ap( new AttachPoint(e->dir, false));
509               AttachedMedia  am( media, ap);
510               //
511               // 1 = automounted only, 2 == all
512               //
513 #if REUSE_FOREIGN_MOUNTS == 1
514               if( isAutoMountedMedia(am))
515 #endif
516               {
517                 DBG << "Using a system mounted media "
518                     << media->name
519                     << " attached on "
520                     << ap->path
521                     << endl;
522
523                 media->iown = false; // mark attachment as foreign
524
525                 setMediaSource(media);
526                 setAttachPoint(ap);
527                 _lastdev = count;
528                 mountsucceeded = true;
529                 break;
530               }
531             }
532           }
533           if( mountsucceeded)
534             break;
535         }
536 #endif  // REUSE_FOREIGN_MOUNTS
537
538         // close tray
539         closeTray( it->name );
540
541         // try all filesystems in sequence
542         for(list<string>::iterator fsit = filesystems.begin()
543             ; !mountsucceeded && fsit != filesystems.end()
544             ; ++fsit)
545         {
546           try {
547             if( !isUseableAttachPoint(Pathname(mountpoint)))
548             {
549               mountpoint = createAttachPoint().asString();
550               setAttachPoint( mountpoint, true);
551               if( mountpoint.empty())
552               {
553                 ZYPP_THROW( MediaBadAttachPointException(url()));
554               }
555             }
556
557             mount.mount(it->name, mountpoint, *fsit, options);
558
559             setMediaSource(media);
560
561             // wait for /etc/mtab update ...
562             // (shouldn't be needed)
563             int limit = 5;
564             while( !(mountsucceeded=isAttached()) && --limit)
565             {
566               sleep(1);
567             }
568
569             if( mountsucceeded)
570             {
571               _lastdev = count;
572             }
573             else
574             {
575               setMediaSource(MediaSourceRef());
576               try
577               {
578                 mount.umount(attachPoint().asString());
579               }
580               catch (const MediaException & excpt_r)
581               {
582                 ZYPP_CAUGHT(excpt_r);
583               }
584               ZYPP_THROW(MediaMountException(
585                 "Unable to verify that the media was mounted",
586                 it->name, mountpoint
587               ));
588             }
589           }
590           catch (const MediaMountException &e)
591           {
592             merr = e;
593             removeAttachPoint();
594             ZYPP_CAUGHT(e);
595           }
596           catch (const MediaException & excpt_r)
597           {
598             removeAttachPoint();
599             ZYPP_CAUGHT(excpt_r);
600           }
601         }
602       }
603
604       if (!mountsucceeded)
605       {
606         _lastdev = -1;
607
608         if( !merr.mountOutput().empty())
609         {
610           ZYPP_THROW(MediaMountException(merr.mountError(),
611                                          _url.asString(),
612                                          mountpoint,
613                                          merr.mountOutput()));
614         }
615         else
616         {
617           ZYPP_THROW(MediaMountException("Mounting media failed",
618                                          _url.asString(), mountpoint));
619         }
620       }
621       DBG << _lastdev << " " << count << endl;
622     }
623
624
625     ///////////////////////////////////////////////////////////////////
626     //
627     //
628     //  METHOD NAME : MediaCD::releaseFrom
629     //  METHOD TYPE : PMError
630     //
631     //  DESCRIPTION : Asserted that media is attached.
632     //
633     void MediaCD::releaseFrom( bool eject )
634     {
635       Mount mount;
636       try {
637         AttachedMedia am( attachedMedia());
638         if(am.mediaSource && am.mediaSource->iown)
639           mount.umount(am.attachPoint->path.asString());
640       }
641       catch (const Exception & excpt_r)
642       {
643         ZYPP_CAUGHT(excpt_r);
644         if (eject)
645         {
646 #if FORCE_RELEASE_FOREIGN > 0
647           /* 1 = automounted only, 2 = all */
648           forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN == 1);
649 #endif
650           if(openTray( mediaSourceName()))
651             return;
652         }
653         ZYPP_RETHROW(excpt_r);
654       }
655
656       // eject device
657       if (eject)
658       {
659 #if FORCE_RELEASE_FOREIGN > 0
660         /* 1 = automounted only, 2 = all */
661         forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN == 1);
662 #endif
663         if( !openTray( mediaSourceName() ))
664         {
665 #if REPORT_EJECT_ERRORS
666           ZYPP_THROW(MediaNotEjectedException(mediaSourceName()));
667 #endif
668         }
669       }
670     }
671
672     ///////////////////////////////////////////////////////////////////
673     //
674     //
675     //  METHOD NAME : MediaCD::forceEject
676     //  METHOD TYPE : void
677     //
678     // Asserted that media is not attached.
679     //
680     void MediaCD::forceEject()
681     {
682       bool ejected=false;
683       if ( !isAttached()) {     // no device mounted in this instance
684 #if DELAYED_VERIFY
685         DeviceList detected( detectDevices(
686           _url.getScheme() == "dvd" ? true : false
687         ));
688
689         if(_devices.empty())
690         {
691           DBG << "creating on-demand device list" << endl;
692           //default is /dev/cdrom; for dvd: /dev/dvd if it exists
693           string device( "/dev/cdrom" );
694           if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
695            device = "/dev/dvd";
696           }
697
698           PathInfo dinfo(device);
699           if( dinfo.isBlk())
700           {
701             MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
702
703             DeviceList::const_iterator d( detected.begin());
704             for( ; d != detected.end(); ++d)
705             {
706               // /dev/cdrom or /dev/dvd to the front
707               if( media.equals( *d))
708                 _devices.push_front( *d);
709               else
710                 _devices.push_back( *d);
711             }
712           }
713           else
714           {
715             // no /dev/cdrom or /dev/dvd link
716             _devices = detected;
717           }
718         }
719 #endif
720
721         DeviceList::iterator it;
722         for( it = _devices.begin(); it != _devices.end(); ++it ) {
723           MediaSourceRef media( new MediaSource( *it));
724 #if DELAYED_VERIFY
725           bool        valid=false;
726           PathInfo    dinfo(media->name);
727           if( dinfo.isBlk())
728           {
729             media->maj_nr = dinfo.major();
730             media->min_nr = dinfo.minor();
731
732             DeviceList::const_iterator d( detected.begin());
733             for( ; d != detected.end(); ++d)
734             {
735               if( media->equals( *d))
736               {
737                 valid = true;
738                 break;
739               }
740             }
741           }
742           if( !valid)
743           {
744             DBG << "skipping invalid device: " << it->name << endl;
745             continue;
746           }
747 #endif
748
749           // FIXME: we have also to check if it is mounted in the system
750           AttachedMedia ret( findAttachedMedia( media));
751           if( !ret.mediaSource)
752           {
753 #if FORCE_RELEASE_FOREIGN > 0
754             /* 1 = automounted only, 2 = all */
755             forceRelaseAllMedia(media, false, FORCE_RELEASE_FOREIGN == 1);
756 #endif
757             if ( openTray( it->name ) )
758             {
759               ejected = true;
760               break; // on 1st success
761             }
762           }
763         }
764       }
765       if( !ejected)
766       {
767 #if REPORT_EJECT_ERRORS
768         ZYPP_THROW(MediaNotEjectedException());
769 #endif
770       }
771     }
772
773     bool MediaCD::isAutoMountedMedia(const AttachedMedia &media)
774     {
775       bool is_automounted = false;
776       if( media.mediaSource && !media.mediaSource->name.empty())
777       {
778         using namespace zypp::target::hal;
779
780         try
781         {
782           HalContext hal(true);
783
784           HalVolume vol = hal.getVolumeFromDeviceFile(media.mediaSource->name);
785           if( vol)
786           {
787             std::string udi = vol.getUDI();
788             std::string key;
789             std::string mnt;
790
791             try
792             {
793               key = "info.hal_mount.created_mount_point";
794               mnt = hal.getDevicePropertyString(udi, key);
795
796               if(media.attachPoint->path == mnt)
797                 is_automounted = true;
798             }
799             catch(const HalException &e1)
800             {
801               ZYPP_CAUGHT(e1);
802
803               try
804               {
805                 key = "volume.mount_point";
806                 mnt = hal.getDevicePropertyString(udi, key);
807
808                 if(media.attachPoint->path == mnt)
809                   is_automounted = true;
810               }
811               catch(const HalException &e2)
812               {
813                 ZYPP_CAUGHT(e2);
814               }
815             }
816           }
817         }
818         catch(const HalException &e)
819         {
820           ZYPP_CAUGHT(e);
821         }
822       }
823       DBG << "Media "       << media.mediaSource->asString()
824           << " attached on " << media.attachPoint->path
825           << " is"           << (is_automounted ? "" : " not")
826           << " automounted"  << std::endl;
827       return is_automounted;
828     }
829
830     ///////////////////////////////////////////////////////////////////
831     //
832     //  METHOD NAME : MediaCD::isAttached
833     //  METHOD TYPE : bool
834     //
835     //  DESCRIPTION : Override check if media is attached.
836     //
837     bool
838     MediaCD::isAttached() const
839     {
840       return checkAttached(false);
841     }
842
843     ///////////////////////////////////////////////////////////////////
844     //
845     //  METHOD NAME : MediaCD::getFile
846     //  METHOD TYPE : PMError
847     //
848     //  DESCRIPTION : Asserted that media is attached.
849     //
850     void MediaCD::getFile( const Pathname & filename ) const
851     {
852       MediaHandler::getFile( filename );
853     }
854
855     ///////////////////////////////////////////////////////////////////
856     //
857     //  METHOD NAME : MediaCD::getDir
858     //  METHOD TYPE : PMError
859     //
860     //  DESCRIPTION : Asserted that media is attached.
861     //
862     void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
863     {
864       MediaHandler::getDir( dirname, recurse_r );
865     }
866
867     ///////////////////////////////////////////////////////////////////
868     //
869     //
870     //  METHOD NAME : MediaCD::getDirInfo
871     //  METHOD TYPE : PMError
872     //
873     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
874     //
875     void MediaCD::getDirInfo( std::list<std::string> & retlist,
876                               const Pathname & dirname, bool dots ) const
877     {
878       MediaHandler::getDirInfo( retlist, dirname, dots );
879     }
880
881     ///////////////////////////////////////////////////////////////////
882     //
883     //
884     //  METHOD NAME : MediaCD::getDirInfo
885     //  METHOD TYPE : PMError
886     //
887     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
888     //
889     void MediaCD::getDirInfo( filesystem::DirContent & retlist,
890                               const Pathname & dirname, bool dots ) const
891     {
892       MediaHandler::getDirInfo( retlist, dirname, dots );
893     }
894
895     bool MediaCD::getDoesFileExist( const Pathname & filename ) const
896     {
897       return MediaHandler::getDoesFileExist( filename );
898     }
899
900     bool MediaCD::hasMoreDevices()
901     {
902       if (_devices.size() == 0)
903         return false;
904       else if (_lastdev_tried < 0)
905         return true;
906
907       return (unsigned) _lastdev_tried < _devices.size() - 1;
908     }
909   } // namespace media
910 } // namespace zypp
911 // vim: set ts=8 sts=2 sw=2 ai noet: