00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <iostream>
00014
00015 #include "zypp/base/Logger.h"
00016 #include "zypp/ExternalProgram.h"
00017 #include "zypp/media/Mount.h"
00018 #include "zypp/media/MediaCD.h"
00019 #include "zypp/media/MediaManager.h"
00020 #include "zypp/Url.h"
00021 #include "zypp/target/hal/HalContext.h"
00022
00023 #include <cstring>
00024 #include <cstdlib>
00025
00026 #include <errno.h>
00027 #include <dirent.h>
00028
00029 #include <sys/ioctl.h>
00030 #include <sys/stat.h>
00031 #include <fcntl.h>
00032 #include <unistd.h>
00033
00034 #include <linux/cdrom.h>
00035
00036
00037
00038
00039 #define DELAYED_VERIFY 1
00040
00041
00042
00043
00044
00045 #define FORCE_RELEASE_FOREIGN 1
00046
00047
00048
00049
00050 #define REPORT_EJECT_ERRORS 1
00051
00052 using namespace std;
00053
00054 namespace zypp {
00055 namespace media {
00056
00057 namespace {
00058
00059 bool isNewDevice(const std::list<MediaSource> &devices,
00060 const MediaSource &media)
00061 {
00062 std::list<MediaSource>::const_iterator d( devices.begin());
00063 for( ; d != devices.end(); ++d)
00064 {
00065 if( media.equals( *d))
00066 return false;
00067 }
00068 return true;
00069 }
00070
00071 inline Pathname get_sysfs_path()
00072 {
00073 Pathname sysfs_path;
00074 if(::getuid() == ::geteuid() && ::getgid() == ::getegid())
00075 {
00076 const char *env = ::getenv("SYSFS_PATH");
00077 if( env && *env)
00078 {
00079 sysfs_path = env;
00080 if( PathInfo(sysfs_path, PathInfo::LSTAT).isDir())
00081 return sysfs_path;
00082 }
00083 }
00084 sysfs_path = "/sys";
00085 if( PathInfo(sysfs_path, PathInfo::LSTAT).isDir())
00086 return sysfs_path;
00087 else
00088 return Pathname();
00089 }
00090
00091 }
00092
00093
00095
00096
00097
00099
00101
00102
00103
00104
00105
00106
00107
00108 MediaCD::MediaCD( const Url & url_r,
00109 const Pathname & attach_point_hint_r )
00110 : MediaHandler( url_r, attach_point_hint_r,
00111 url_r.getPathName(),
00112 false )
00113
00114 , _lastdev(-1)
00115 {
00116 MIL << "MediaCD::MediaCD(" << url_r << ", "
00117 << attach_point_hint_r << ")" << endl;
00118
00119 if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
00120 {
00121 ERR << "Unsupported schema in the Url: " << url_r.asString()
00122 << std::endl;
00123 ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
00124 }
00125
00126 #if !DELAYED_VERIFY
00127 DeviceList detected( detectDevices(
00128 url_r.getScheme() == "dvd" ? true : false
00129 ));
00130 #endif
00131
00132 string devices = _url.getQueryParam("devices");
00133 if (!devices.empty())
00134 {
00135 string::size_type pos;
00136 DBG << "parse " << devices << endl;
00137 while(!devices.empty())
00138 {
00139 pos = devices.find(',');
00140 string device = devices.substr(0,pos);
00141 if (!device.empty())
00142 {
00143 #if DELAYED_VERIFY
00144 MediaSource media("cdrom", device, 0, 0);
00145 _devices.push_back( media);
00146 DBG << "use device (delayed verify)" << device << endl;
00147 #else
00148 bool is_ok = false;
00149 PathInfo dinfo(device);
00150 if( dinfo.isBlk())
00151 {
00152 MediaSource media("cdrom", device, dinfo.major(),
00153 dinfo.minor());
00154 DeviceList::const_iterator d( detected.begin());
00155 for( ; d != detected.end(); ++d)
00156 {
00157 if( media.equals( *d))
00158 {
00159 is_ok = true;
00160 _devices.push_back( *d);
00161 DBG << "use device " << device << endl;
00162 }
00163 }
00164 }
00165
00166 if( !is_ok)
00167 {
00168 ERR << "Device " << device << " is not acceptable "
00169 << "for " << _url.getScheme() << std::endl;
00170 ZYPP_THROW(MediaBadUrlException(_url,
00171 "Invalid device name in URL devices argument"
00172 ));
00173 }
00174 #endif
00175 }
00176 if (pos!=string::npos)
00177 devices=devices.substr(pos+1);
00178 else
00179 devices.erase();
00180 }
00181 }
00182 else
00183 {
00184 #if DELAYED_VERIFY
00185 DBG << "going to use on-demand device list" << endl;
00186 return;
00187 #else
00188 DBG << "going to use default device list" << endl;
00189
00190 string device( "/dev/cdrom" );
00191 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
00192 device = "/dev/dvd";
00193 }
00194
00195 PathInfo dinfo(device);
00196 if( dinfo.isBlk())
00197 {
00198 MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
00199
00200 DeviceList::const_iterator d( detected.begin());
00201 for( ; d != detected.end(); ++d)
00202 {
00203
00204 if( media.equals( *d))
00205 _devices.push_front( *d);
00206 else
00207 _devices.push_back( *d);
00208 }
00209 }
00210 else
00211 {
00212
00213 _devices = detected;
00214 }
00215 #endif
00216 }
00217
00218 if( _devices.empty())
00219 {
00220 ERR << "Unable to find any cdrom drive for " << _url.asString()
00221 << std::endl;
00222 ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00223 }
00224 }
00225
00227
00228
00229
00230
00231
00232 bool MediaCD::openTray( const string & device_r )
00233 {
00234 int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
00235 if ( fd == -1 ) {
00236 WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
00237 return false;
00238 }
00239 int res = ::ioctl( fd, CDROMEJECT );
00240 ::close( fd );
00241 if ( res ) {
00242 WAR << "Eject " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
00243 return false;
00244 }
00245 MIL << "Eject " << device_r << endl;
00246 return true;
00247 }
00248
00250
00251
00252
00253
00254
00255 bool MediaCD::closeTray( const string & device_r )
00256 {
00257 int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
00258 if ( fd == -1 ) {
00259 WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
00260 return false;
00261 }
00262 int res = ::ioctl( fd, CDROMCLOSETRAY );
00263 ::close( fd );
00264 if ( res ) {
00265 WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
00266 return false;
00267 }
00268 DBG << "Close tray " << device_r << endl;
00269 return true;
00270 }
00271
00273
00274
00275
00276
00277
00278 MediaCD::DeviceList
00279 MediaCD::detectDevices(bool supportingDVD)
00280 {
00281 using namespace zypp::target::hal;
00282
00283 DeviceList detected;
00284 try
00285 {
00286 HalContext hal(true);
00287
00288 std::vector<std::string> drv_udis;
00289 drv_udis = hal.findDevicesByCapability("storage.cdrom");
00290
00291 DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
00292 for(size_t d = 0; d < drv_udis.size(); d++)
00293 {
00294 HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
00295
00296 if( drv)
00297 {
00298 if( supportingDVD)
00299 {
00300 std::vector<std::string> caps;
00301 try {
00302 caps = drv.getCdromCapabilityNames();
00303 }
00304 catch(const HalException &e)
00305 {
00306 ZYPP_CAUGHT(e);
00307 }
00308
00309 bool found=false;
00310 std::vector<std::string>::const_iterator ci;
00311 for( ci=caps.begin(); ci != caps.end(); ++ci)
00312 {
00313 if( *ci == "dvd")
00314 found = true;
00315 }
00316 if( !found)
00317 continue;
00318 }
00319
00320 MediaSource media("cdrom", drv.getDeviceFile(),
00321 drv.getDeviceMajor(),
00322 drv.getDeviceMinor());
00323 DBG << "Found " << drv_udis[d] << ": "
00324 << media.asString() << std::endl;
00325 detected.push_back(media);
00326 }
00327 }
00328 }
00329 catch(const zypp::target::hal::HalException &e)
00330 {
00331 ZYPP_CAUGHT(e);
00332 }
00333
00334
00335
00336
00337
00338
00339 if( detected.empty())
00340 {
00341 Pathname sysfs_path( get_sysfs_path());
00342 if(sysfs_path.empty())
00343 return detected;
00344
00345 std::string sys_name;
00346 std::string dev_name;
00347
00348
00349 sys_name = sysfs_path.cat("block/sr").asString();
00350 dev_name = "/dev/sr";
00351 DBG << "Collecting SCSI CD-ROM devices ("
00352 << dev_name << "X)" << std::endl;
00353 for(size_t i=0; i < 16; i++)
00354 {
00355 PathInfo sys_info(sys_name + str::numstring(i));
00356 PathInfo dev_info(dev_name + str::numstring(i));
00357 if( sys_info.isDir() && dev_info.isBlk())
00358 {
00359
00360 MediaSource media("cdrom", dev_info.asString(),
00361 dev_info.major(),
00362 dev_info.minor());
00363 if( isNewDevice(detected, media))
00364 {
00365 DBG << "Found SCSI CDROM "
00366 << media.asString()
00367 << std::endl;
00368 detected.push_back(media);
00369 }
00370 }
00371 }
00372
00373
00374 #if powerpc
00375 sys_name = sysfs_path.cat("block/iseries!vcd").asString();
00376 dev_name = "/dev/iseries/vcd";
00377 DBG << "Collecting iSeries virtual CD-ROM devices ("
00378 << dev_name << "X)" << std::endl;
00379 for(size_t i=0; i < 8; i++)
00380 {
00381 char drive_letter = 'a' + i;
00382 PathInfo sys_info(sys_name + drive_letter);
00383 PathInfo dev_info(dev_name + drive_letter);
00384 if( sys_info.isDir() && dev_info.isBlk())
00385 {
00386
00387 MediaSource media("cdrom", dev_info.asString(),
00388 dev_info.major(),
00389 dev_info.minor());
00390 if( isNewDevice(detected, media))
00391 {
00392 DBG << "Found iSeries virtual CDROM "
00393 << media.asString()
00394 << std::endl;
00395 detected.push_back(media);
00396 }
00397 }
00398 }
00399 #endif // powerpc
00400
00401
00402 }
00403 return detected;
00404 }
00405
00406
00408
00409
00410
00411
00412
00413
00414
00415 void MediaCD::attachTo(bool next)
00416 {
00417 DBG << "next " << next << " last " << _lastdev << endl;
00418 if (next && _lastdev == -1)
00419 ZYPP_THROW(MediaNotSupportedException(url()));
00420
00421 #if DELAYED_VERIFY
00422 DeviceList detected( detectDevices(
00423 _url.getScheme() == "dvd" ? true : false
00424 ));
00425
00426 if(_devices.empty())
00427 {
00428 DBG << "creating on-demand device list" << endl;
00429
00430 string device( "/dev/cdrom" );
00431 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
00432 device = "/dev/dvd";
00433 }
00434
00435 PathInfo dinfo(device);
00436 if( dinfo.isBlk())
00437 {
00438 MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
00439
00440 DeviceList::const_iterator d( detected.begin());
00441 for( ; d != detected.end(); ++d)
00442 {
00443
00444 if( media.equals( *d))
00445 _devices.push_front( *d);
00446 else
00447 _devices.push_back( *d);
00448 }
00449 }
00450 else
00451 {
00452
00453 _devices = detected;
00454 }
00455 }
00456 #endif
00457
00458 Mount mount;
00459 string mountpoint = attachPoint().asString();
00460 bool mountsucceeded = false;
00461 int count = 0;
00462
00463 string options = _url.getQueryParam("mountoptions");
00464 if (options.empty())
00465 {
00466 options="ro";
00467 }
00468
00469
00470 list<string> filesystems;
00471
00472
00473 if ( _url.getScheme() == "dvd" )
00474 filesystems.push_back("udf");
00475
00476 filesystems.push_back("iso9660");
00477
00478
00479 for (DeviceList::iterator it = _devices.begin()
00480 ; !mountsucceeded && it != _devices.end()
00481 ; ++it, count++ )
00482 {
00483 DBG << "count " << count << endl;
00484 if (next && count<=_lastdev )
00485 {
00486 DBG << "skipping device " << it->name << endl;
00487 continue;
00488 }
00489 #if DELAYED_VERIFY
00490 MediaSource temp( *it);
00491 bool valid=false;
00492 PathInfo dinfo(temp.name);
00493 if( dinfo.isBlk())
00494 {
00495 temp.maj_nr = dinfo.major();
00496 temp.min_nr = dinfo.minor();
00497
00498 DeviceList::const_iterator d( detected.begin());
00499 for( ; d != detected.end(); ++d)
00500 {
00501 if( temp.equals( *d))
00502 {
00503 valid = true;
00504 break;
00505 }
00506 }
00507 }
00508 if( !valid)
00509 {
00510 DBG << "skipping invalid device: " << it->name << endl;
00511 continue;
00512 }
00513 MediaSourceRef media( new MediaSource(temp));
00514 #else
00515 MediaSourceRef media( new MediaSource( *it));
00516 #endif
00517
00518 AttachedMedia ret( findAttachedMedia( media));
00519
00520 if( ret.mediaSource && ret.attachPoint &&
00521 !ret.attachPoint->empty())
00522 {
00523 DBG << "Using a shared media "
00524 << ret.mediaSource->name
00525 << " attached on "
00526 << ret.attachPoint->path
00527 << endl;
00528 removeAttachPoint();
00529 setAttachPoint(ret.attachPoint);
00530 setMediaSource(ret.mediaSource);
00531 _lastdev = count;
00532 mountsucceeded = true;
00533 break;
00534 }
00535
00536
00537
00538
00539
00540
00541 closeTray( it->name );
00542
00543
00544 for(list<string>::iterator fsit = filesystems.begin()
00545 ; !mountsucceeded && fsit != filesystems.end()
00546 ; ++fsit)
00547 {
00548 try {
00549 if( !isUseableAttachPoint(Pathname(mountpoint)))
00550 {
00551 mountpoint = createAttachPoint().asString();
00552 setAttachPoint( mountpoint, true);
00553 if( mountpoint.empty())
00554 {
00555 ZYPP_THROW( MediaBadAttachPointException(url()));
00556 }
00557 }
00558
00559 mount.mount(it->name, mountpoint, *fsit, options);
00560
00561 setMediaSource(media);
00562
00563
00564
00565 int limit = 3;
00566 while( !(mountsucceeded=isAttached()) && --limit)
00567 {
00568 sleep(1);
00569 }
00570
00571 if( mountsucceeded)
00572 {
00573 _lastdev = count;
00574 }
00575 else
00576 {
00577 setMediaSource(MediaSourceRef());
00578 try
00579 {
00580 mount.umount(attachPoint().asString());
00581 }
00582 catch (const MediaException & excpt_r)
00583 {
00584 ZYPP_CAUGHT(excpt_r);
00585 }
00586 ZYPP_THROW(MediaMountException(
00587 "Unable to verify that the media was mounted",
00588 it->name, mountpoint
00589 ));
00590 }
00591 }
00592 catch (const MediaException & excpt_r)
00593 {
00594 removeAttachPoint();
00595 ZYPP_CAUGHT(excpt_r);
00596 }
00597 }
00598 }
00599
00600 if (!mountsucceeded)
00601 {
00602 _lastdev = -1;
00603 ZYPP_THROW(MediaMountException(_url.asString(), mountpoint, "Mounting media failed"));
00604 }
00605 DBG << _lastdev << " " << count << endl;
00606 }
00607
00608
00610
00611
00612
00613
00614
00615
00616
00617 void MediaCD::releaseFrom( bool eject )
00618 {
00619 Mount mount;
00620 try {
00621 mount.umount(attachPoint().asString());
00622 }
00623 catch (const Exception & excpt_r)
00624 {
00625 ZYPP_CAUGHT(excpt_r);
00626 if (eject)
00627 {
00628 #if FORCE_RELEASE_FOREIGN
00629 forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
00630 #endif
00631 if(openTray( mediaSourceName()))
00632 return;
00633 }
00634 ZYPP_RETHROW(excpt_r);
00635 }
00636
00637
00638 if (eject)
00639 {
00640 #if FORCE_RELEASE_FOREIGN
00641 forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
00642 #endif
00643 if( !openTray( mediaSourceName() ))
00644 {
00645 #if REPORT_EJECT_ERRORS
00646 ZYPP_THROW(MediaNotEjectedException(mediaSourceName()));
00647 #endif
00648 }
00649 }
00650 }
00651
00653
00654
00655
00656
00657
00658
00659
00660 void MediaCD::forceEject()
00661 {
00662 bool ejected=false;
00663 if ( !isAttached()) {
00664 #if DELAYED_VERIFY
00665 DeviceList detected( detectDevices(
00666 _url.getScheme() == "dvd" ? true : false
00667 ));
00668
00669 if(_devices.empty())
00670 {
00671 DBG << "creating on-demand device list" << endl;
00672
00673 string device( "/dev/cdrom" );
00674 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
00675 device = "/dev/dvd";
00676 }
00677
00678 PathInfo dinfo(device);
00679 if( dinfo.isBlk())
00680 {
00681 MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
00682
00683 DeviceList::const_iterator d( detected.begin());
00684 for( ; d != detected.end(); ++d)
00685 {
00686
00687 if( media.equals( *d))
00688 _devices.push_front( *d);
00689 else
00690 _devices.push_back( *d);
00691 }
00692 }
00693 else
00694 {
00695
00696 _devices = detected;
00697 }
00698 }
00699 #endif
00700
00701 DeviceList::iterator it;
00702 for( it = _devices.begin(); it != _devices.end(); ++it ) {
00703 MediaSourceRef media( new MediaSource( *it));
00704 #if DELAYED_VERIFY
00705 bool valid=false;
00706 PathInfo dinfo(media->name);
00707 if( dinfo.isBlk())
00708 {
00709 media->maj_nr = dinfo.major();
00710 media->min_nr = dinfo.minor();
00711
00712 DeviceList::const_iterator d( detected.begin());
00713 for( ; d != detected.end(); ++d)
00714 {
00715 if( media->equals( *d))
00716 {
00717 valid = true;
00718 break;
00719 }
00720 }
00721 }
00722 if( !valid)
00723 {
00724 DBG << "skipping invalid device: " << it->name << endl;
00725 continue;
00726 }
00727 #endif
00728
00729
00730 AttachedMedia ret( findAttachedMedia( media));
00731 if( !ret.mediaSource)
00732 {
00733 #if FORCE_RELEASE_FOREIGN
00734 forceRelaseAllMedia(media, false, FORCE_RELEASE_FOREIGN != 2);
00735 #endif
00736 if ( openTray( it->name ) )
00737 {
00738 ejected = true;
00739 break;
00740 }
00741 }
00742 }
00743 }
00744 if( !ejected)
00745 {
00746 #if REPORT_EJECT_ERRORS
00747 ZYPP_THROW(MediaNotEjectedException());
00748 #endif
00749 }
00750 }
00751
00752 bool MediaCD::isAutoMountedMedia(const AttachedMedia &media)
00753 {
00754 bool is_automounted = false;
00755 if( media.mediaSource && !media.mediaSource->name.empty())
00756 {
00757 using namespace zypp::target::hal;
00758
00759 try
00760 {
00761 HalContext hal(true);
00762
00763 HalVolume vol = hal.getVolumeFromDeviceFile(media.mediaSource->name);
00764 if( vol)
00765 {
00766 std::string udi = vol.getUDI();
00767 std::string key = "info.hal_mount.created_mount_point";
00768 std::string mnt = hal.getDevicePropertyString(udi, key);
00769
00770 if(media.attachPoint->path == mnt)
00771 is_automounted = true;
00772 }
00773 }
00774 catch(const HalException &e)
00775 {
00776 ZYPP_CAUGHT(e);
00777 }
00778 }
00779 DBG << "Media " << media.mediaSource->asString()
00780 << " attached on " << media.attachPoint->path
00781 << " is" << (is_automounted ? "" : " not")
00782 << " automounted" << std::endl;
00783 return is_automounted;
00784 }
00785
00787
00788
00789
00790
00791
00792
00793 bool
00794 MediaCD::isAttached() const
00795 {
00796 return checkAttached(false);
00797 }
00798
00800
00801
00802
00803
00804
00805
00806 void MediaCD::getFile( const Pathname & filename ) const
00807 {
00808 MediaHandler::getFile( filename );
00809 }
00810
00812
00813
00814
00815
00816
00817
00818 void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
00819 {
00820 MediaHandler::getDir( dirname, recurse_r );
00821 }
00822
00824
00825
00826
00827
00828
00829
00830
00831 void MediaCD::getDirInfo( std::list<std::string> & retlist,
00832 const Pathname & dirname, bool dots ) const
00833 {
00834 MediaHandler::getDirInfo( retlist, dirname, dots );
00835 }
00836
00838
00839
00840
00841
00842
00843
00844
00845 void MediaCD::getDirInfo( filesystem::DirContent & retlist,
00846 const Pathname & dirname, bool dots ) const
00847 {
00848 MediaHandler::getDirInfo( retlist, dirname, dots );
00849 }
00850
00851 }
00852 }
00853