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 bool supportsDVD=false;
00299 if( supportingDVD)
00300 {
00301 std::vector<std::string> caps;
00302 try {
00303 caps = drv.getCdromCapabilityNames();
00304 }
00305 catch(const HalException &e)
00306 {
00307 ZYPP_CAUGHT(e);
00308 }
00309
00310 std::vector<std::string>::const_iterator ci;
00311 for( ci=caps.begin(); ci != caps.end(); ++ci)
00312 {
00313 if( *ci == "dvd")
00314 supportsDVD = true;
00315 }
00316 }
00317
00318 MediaSource media("cdrom", drv.getDeviceFile(),
00319 drv.getDeviceMajor(),
00320 drv.getDeviceMinor());
00321 DBG << "Found " << drv_udis[d] << ": "
00322 << media.asString() << std::endl;
00323 if( supportingDVD && supportsDVD)
00324 {
00325 detected.push_front(media);
00326 }
00327 else
00328 {
00329 detected.push_back(media);
00330 }
00331 }
00332 }
00333 }
00334 catch(const zypp::target::hal::HalException &e)
00335 {
00336 ZYPP_CAUGHT(e);
00337 }
00338
00339
00340
00341
00342
00343
00344 if( detected.empty())
00345 {
00346 Pathname sysfs_path( get_sysfs_path());
00347 if(sysfs_path.empty())
00348 return detected;
00349
00350 std::string sys_name;
00351 std::string dev_name;
00352
00353
00354 sys_name = sysfs_path.cat("block/sr").asString();
00355 dev_name = "/dev/sr";
00356 DBG << "Collecting SCSI CD-ROM devices ("
00357 << dev_name << "X)" << std::endl;
00358 for(size_t i=0; i < 16; i++)
00359 {
00360 PathInfo sys_info(sys_name + str::numstring(i));
00361 PathInfo dev_info(dev_name + str::numstring(i));
00362 if( sys_info.isDir() && dev_info.isBlk())
00363 {
00364
00365 MediaSource media("cdrom", dev_info.asString(),
00366 dev_info.major(),
00367 dev_info.minor());
00368 if( isNewDevice(detected, media))
00369 {
00370 DBG << "Found SCSI CDROM "
00371 << media.asString()
00372 << std::endl;
00373 detected.push_back(media);
00374 }
00375 }
00376 }
00377
00378
00379 #if powerpc
00380 sys_name = sysfs_path.cat("block/iseries!vcd").asString();
00381 dev_name = "/dev/iseries/vcd";
00382 DBG << "Collecting iSeries virtual CD-ROM devices ("
00383 << dev_name << "X)" << std::endl;
00384 for(size_t i=0; i < 8; i++)
00385 {
00386 char drive_letter = 'a' + i;
00387 PathInfo sys_info(sys_name + drive_letter);
00388 PathInfo dev_info(dev_name + drive_letter);
00389 if( sys_info.isDir() && dev_info.isBlk())
00390 {
00391
00392 MediaSource media("cdrom", dev_info.asString(),
00393 dev_info.major(),
00394 dev_info.minor());
00395 if( isNewDevice(detected, media))
00396 {
00397 DBG << "Found iSeries virtual CDROM "
00398 << media.asString()
00399 << std::endl;
00400 detected.push_back(media);
00401 }
00402 }
00403 }
00404 #endif // powerpc
00405
00406
00407 }
00408 return detected;
00409 }
00410
00411
00413
00414
00415
00416
00417
00418
00419
00420 void MediaCD::attachTo(bool next)
00421 {
00422 DBG << "next " << next << " last " << _lastdev << endl;
00423 if (next && _lastdev == -1)
00424 ZYPP_THROW(MediaNotSupportedException(url()));
00425
00426 #if DELAYED_VERIFY
00427 DeviceList detected( detectDevices(
00428 _url.getScheme() == "dvd" ? true : false
00429 ));
00430
00431 if(_devices.empty())
00432 {
00433 DBG << "creating on-demand device list" << endl;
00434
00435 string device( "/dev/cdrom" );
00436 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
00437 device = "/dev/dvd";
00438 }
00439
00440 PathInfo dinfo(device);
00441 if( dinfo.isBlk())
00442 {
00443 MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
00444
00445 DeviceList::const_iterator d( detected.begin());
00446 for( ; d != detected.end(); ++d)
00447 {
00448
00449 if( media.equals( *d))
00450 _devices.push_front( *d);
00451 else
00452 _devices.push_back( *d);
00453 }
00454 }
00455 else
00456 {
00457
00458 _devices = detected;
00459 }
00460 }
00461 #endif
00462
00463 Mount mount;
00464 string mountpoint = attachPoint().asString();
00465 bool mountsucceeded = false;
00466 int count = 0;
00467
00468 string options = _url.getQueryParam("mountoptions");
00469 if (options.empty())
00470 {
00471 options="ro";
00472 }
00473
00474
00475 list<string> filesystems;
00476
00477
00478 if ( _url.getScheme() == "dvd" )
00479 filesystems.push_back("udf");
00480
00481 filesystems.push_back("iso9660");
00482
00483
00484 for (DeviceList::iterator it = _devices.begin()
00485 ; !mountsucceeded && it != _devices.end()
00486 ; ++it, count++ )
00487 {
00488 DBG << "count " << count << endl;
00489 if (next && count<=_lastdev )
00490 {
00491 DBG << "skipping device " << it->name << endl;
00492 continue;
00493 }
00494 #if DELAYED_VERIFY
00495 MediaSource temp( *it);
00496 bool valid=false;
00497 PathInfo dinfo(temp.name);
00498 if( dinfo.isBlk())
00499 {
00500 temp.maj_nr = dinfo.major();
00501 temp.min_nr = dinfo.minor();
00502
00503 DeviceList::const_iterator d( detected.begin());
00504 for( ; d != detected.end(); ++d)
00505 {
00506 if( temp.equals( *d))
00507 {
00508 valid = true;
00509 break;
00510 }
00511 }
00512 }
00513 if( !valid)
00514 {
00515 DBG << "skipping invalid device: " << it->name << endl;
00516 continue;
00517 }
00518 MediaSourceRef media( new MediaSource(temp));
00519 #else
00520 MediaSourceRef media( new MediaSource( *it));
00521 #endif
00522
00523 AttachedMedia ret( findAttachedMedia( media));
00524
00525 if( ret.mediaSource && ret.attachPoint &&
00526 !ret.attachPoint->empty())
00527 {
00528 DBG << "Using a shared media "
00529 << ret.mediaSource->name
00530 << " attached on "
00531 << ret.attachPoint->path
00532 << endl;
00533 removeAttachPoint();
00534 setAttachPoint(ret.attachPoint);
00535 setMediaSource(ret.mediaSource);
00536 _lastdev = count;
00537 mountsucceeded = true;
00538 break;
00539 }
00540
00541
00542
00543
00544
00545
00546 closeTray( it->name );
00547
00548
00549 for(list<string>::iterator fsit = filesystems.begin()
00550 ; !mountsucceeded && fsit != filesystems.end()
00551 ; ++fsit)
00552 {
00553 try {
00554 if( !isUseableAttachPoint(Pathname(mountpoint)))
00555 {
00556 mountpoint = createAttachPoint().asString();
00557 setAttachPoint( mountpoint, true);
00558 if( mountpoint.empty())
00559 {
00560 ZYPP_THROW( MediaBadAttachPointException(url()));
00561 }
00562 }
00563
00564 mount.mount(it->name, mountpoint, *fsit, options);
00565
00566 setMediaSource(media);
00567
00568
00569
00570 int limit = 5;
00571 while( !(mountsucceeded=isAttached()) && --limit)
00572 {
00573 sleep(1);
00574 }
00575
00576 if( mountsucceeded)
00577 {
00578 _lastdev = count;
00579 }
00580 else
00581 {
00582 setMediaSource(MediaSourceRef());
00583 try
00584 {
00585 mount.umount(attachPoint().asString());
00586 }
00587 catch (const MediaException & excpt_r)
00588 {
00589 ZYPP_CAUGHT(excpt_r);
00590 }
00591 ZYPP_THROW(MediaMountException(
00592 "Unable to verify that the media was mounted",
00593 it->name, mountpoint
00594 ));
00595 }
00596 }
00597 catch (const MediaException & excpt_r)
00598 {
00599 removeAttachPoint();
00600 ZYPP_CAUGHT(excpt_r);
00601 }
00602 }
00603 }
00604
00605 if (!mountsucceeded)
00606 {
00607 _lastdev = -1;
00608 ZYPP_THROW(MediaMountException(_url.asString(), mountpoint, "Mounting media failed"));
00609 }
00610 DBG << _lastdev << " " << count << endl;
00611 }
00612
00613
00615
00616
00617
00618
00619
00620
00621
00622 void MediaCD::releaseFrom( bool eject )
00623 {
00624 Mount mount;
00625 try {
00626 mount.umount(attachPoint().asString());
00627 }
00628 catch (const Exception & excpt_r)
00629 {
00630 ZYPP_CAUGHT(excpt_r);
00631 if (eject)
00632 {
00633 #if FORCE_RELEASE_FOREIGN
00634 forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
00635 #endif
00636 if(openTray( mediaSourceName()))
00637 return;
00638 }
00639 ZYPP_RETHROW(excpt_r);
00640 }
00641
00642
00643 if (eject)
00644 {
00645 #if FORCE_RELEASE_FOREIGN
00646 forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
00647 #endif
00648 if( !openTray( mediaSourceName() ))
00649 {
00650 #if REPORT_EJECT_ERRORS
00651 ZYPP_THROW(MediaNotEjectedException(mediaSourceName()));
00652 #endif
00653 }
00654 }
00655 }
00656
00658
00659
00660
00661
00662
00663
00664
00665 void MediaCD::forceEject()
00666 {
00667 bool ejected=false;
00668 if ( !isAttached()) {
00669 #if DELAYED_VERIFY
00670 DeviceList detected( detectDevices(
00671 _url.getScheme() == "dvd" ? true : false
00672 ));
00673
00674 if(_devices.empty())
00675 {
00676 DBG << "creating on-demand device list" << endl;
00677
00678 string device( "/dev/cdrom" );
00679 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
00680 device = "/dev/dvd";
00681 }
00682
00683 PathInfo dinfo(device);
00684 if( dinfo.isBlk())
00685 {
00686 MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
00687
00688 DeviceList::const_iterator d( detected.begin());
00689 for( ; d != detected.end(); ++d)
00690 {
00691
00692 if( media.equals( *d))
00693 _devices.push_front( *d);
00694 else
00695 _devices.push_back( *d);
00696 }
00697 }
00698 else
00699 {
00700
00701 _devices = detected;
00702 }
00703 }
00704 #endif
00705
00706 DeviceList::iterator it;
00707 for( it = _devices.begin(); it != _devices.end(); ++it ) {
00708 MediaSourceRef media( new MediaSource( *it));
00709 #if DELAYED_VERIFY
00710 bool valid=false;
00711 PathInfo dinfo(media->name);
00712 if( dinfo.isBlk())
00713 {
00714 media->maj_nr = dinfo.major();
00715 media->min_nr = dinfo.minor();
00716
00717 DeviceList::const_iterator d( detected.begin());
00718 for( ; d != detected.end(); ++d)
00719 {
00720 if( media->equals( *d))
00721 {
00722 valid = true;
00723 break;
00724 }
00725 }
00726 }
00727 if( !valid)
00728 {
00729 DBG << "skipping invalid device: " << it->name << endl;
00730 continue;
00731 }
00732 #endif
00733
00734
00735 AttachedMedia ret( findAttachedMedia( media));
00736 if( !ret.mediaSource)
00737 {
00738 #if FORCE_RELEASE_FOREIGN
00739 forceRelaseAllMedia(media, false, FORCE_RELEASE_FOREIGN != 2);
00740 #endif
00741 if ( openTray( it->name ) )
00742 {
00743 ejected = true;
00744 break;
00745 }
00746 }
00747 }
00748 }
00749 if( !ejected)
00750 {
00751 #if REPORT_EJECT_ERRORS
00752 ZYPP_THROW(MediaNotEjectedException());
00753 #endif
00754 }
00755 }
00756
00757 bool MediaCD::isAutoMountedMedia(const AttachedMedia &media)
00758 {
00759 bool is_automounted = false;
00760 if( media.mediaSource && !media.mediaSource->name.empty())
00761 {
00762 using namespace zypp::target::hal;
00763
00764 try
00765 {
00766 HalContext hal(true);
00767
00768 HalVolume vol = hal.getVolumeFromDeviceFile(media.mediaSource->name);
00769 if( vol)
00770 {
00771 std::string udi = vol.getUDI();
00772 std::string key = "info.hal_mount.created_mount_point";
00773 std::string mnt = hal.getDevicePropertyString(udi, key);
00774
00775 if(media.attachPoint->path == mnt)
00776 is_automounted = true;
00777 }
00778 }
00779 catch(const HalException &e)
00780 {
00781 ZYPP_CAUGHT(e);
00782 }
00783 }
00784 DBG << "Media " << media.mediaSource->asString()
00785 << " attached on " << media.attachPoint->path
00786 << " is" << (is_automounted ? "" : " not")
00787 << " automounted" << std::endl;
00788 return is_automounted;
00789 }
00790
00792
00793
00794
00795
00796
00797
00798 bool
00799 MediaCD::isAttached() const
00800 {
00801 return checkAttached(false);
00802 }
00803
00805
00806
00807
00808
00809
00810
00811 void MediaCD::getFile( const Pathname & filename ) const
00812 {
00813 MediaHandler::getFile( filename );
00814 }
00815
00817
00818
00819
00820
00821
00822
00823 void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
00824 {
00825 MediaHandler::getDir( dirname, recurse_r );
00826 }
00827
00829
00830
00831
00832
00833
00834
00835
00836 void MediaCD::getDirInfo( std::list<std::string> & retlist,
00837 const Pathname & dirname, bool dots ) const
00838 {
00839 MediaHandler::getDirInfo( retlist, dirname, dots );
00840 }
00841
00843
00844
00845
00846
00847
00848
00849
00850 void MediaCD::getDirInfo( filesystem::DirContent & retlist,
00851 const Pathname & dirname, bool dots ) const
00852 {
00853 MediaHandler::getDirInfo( retlist, dirname, dots );
00854 }
00855
00856 }
00857 }
00858