File indexing completed on 2025-04-20 04:26:55
0001 /* 0002 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <config-k3b.h> 0008 0009 #include "k3bglobals.h" 0010 #include "k3bglobalsettings.h" 0011 #include "k3bversion.h" 0012 #include "k3bdevice.h" 0013 #include "k3bdevicemanager.h" 0014 #include "k3bdeviceglobals.h" 0015 #include "k3bexternalbinmanager.h" 0016 #include "k3bcore.h" 0017 #include "k3bmediacache.h" 0018 #include "k3bmsf.h" 0019 #include "k3b_i18n.h" 0020 0021 #include <KProcess> 0022 #include <kio_version.h> 0023 #include <KIO/Job> 0024 #include <KIO/StatJob> 0025 #include <KMountPoint> 0026 #include <Solid/Device> 0027 #include <Solid/StorageAccess> 0028 #include <Solid/OpticalDrive> 0029 0030 #include <QDataStream> 0031 #include <QDebug> 0032 #include <QDir> 0033 #include <QFile> 0034 #include <QStandardPaths> 0035 #include <QUrl> 0036 #include <QStorageInfo> 0037 0038 #include <cmath> 0039 #include <sys/utsname.h> 0040 0041 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) 0042 # include <sys/param.h> 0043 # include <sys/mount.h> 0044 # include <sys/endian.h> 0045 # define bswap_16(x) bswap16(x) 0046 # define bswap_32(x) bswap32(x) 0047 # define bswap_64(x) bswap64(x) 0048 #else 0049 # include <byteswap.h> 0050 #endif 0051 0052 #ifdef Q_OS_WIN32 0053 #include <windows.h> 0054 #endif 0055 0056 0057 qint16 K3b::swapByteOrder( const qint16& i ) 0058 { 0059 return bswap_16( i ); 0060 //((i << 8) & 0xff00) | ((i >> 8 ) & 0xff); 0061 } 0062 0063 0064 qint32 K3b::swapByteOrder( const qint32& i ) 0065 { 0066 //return ((i << 24) & 0xff000000) | ((i << 8) & 0xff0000) | ((i >> 8) & 0xff00) | ((i >> 24) & 0xff ); 0067 return bswap_32( i ); 0068 } 0069 0070 0071 qint64 K3b::swapByteOrder( const qint64& i ) 0072 { 0073 return bswap_64( i ); 0074 } 0075 0076 0077 QString K3b::findUniqueFilePrefix( const QString& _prefix, const QString& path ) 0078 { 0079 QString url; 0080 if( path.isEmpty() || !QFile::exists(path) ) 0081 url = defaultTempPath(); 0082 else 0083 url = prepareDir( path ); 0084 0085 QString prefix = _prefix; 0086 if( prefix.isEmpty() ) 0087 prefix = "k3b_"; 0088 0089 // now create the unique prefix 0090 QDir dir( url ); 0091 QStringList entries = dir.entryList( QDir::NoFilter, QDir::Name ); 0092 int i = 0; 0093 for( QStringList::iterator it = entries.begin(); 0094 it != entries.end(); ++it ) { 0095 if( (*it).startsWith( prefix + QString::number(i) ) ) { 0096 i++; 0097 it = entries.begin(); 0098 } 0099 } 0100 0101 return url + prefix + QString::number(i); 0102 } 0103 0104 0105 QString K3b::findTempFile( const QString& ending, const QString& d ) 0106 { 0107 return findUniqueFilePrefix( "k3b_", d ) + ( ending.isEmpty() ? QString() : (QString::fromLatin1(".") + ending) ); 0108 } 0109 0110 0111 QString K3b::defaultTempPath() 0112 { 0113 return prepareDir( k3bcore->globalSettings()->defaultTempPath() ); 0114 } 0115 0116 0117 QString K3b::prepareDir( const QString& dir ) 0118 { 0119 if(dir.isEmpty()) 0120 return QString(); 0121 else if ( !dir.endsWith( '/' ) ) 0122 return dir + '/'; 0123 else 0124 return dir; 0125 } 0126 0127 0128 QString K3b::parentDir( const QString& path ) 0129 { 0130 QString parent = path; 0131 if( path.isEmpty()) 0132 return QString(); 0133 if( path[path.length()-1] == '/' ) 0134 parent.truncate( parent.length()-1 ); 0135 0136 int pos = parent.lastIndexOf( '/' ); 0137 if( pos >= 0 ) 0138 parent.truncate( pos+1 ); 0139 else // relative path, do anything... 0140 parent = '/'; 0141 0142 return parent; 0143 } 0144 0145 0146 QString K3b::fixupPath( const QString& path ) 0147 { 0148 QString s; 0149 bool lastWasSlash = false; 0150 for( int i = 0; i < path.length(); ++i ) { 0151 if( path[i] == '/' ) { 0152 if( !lastWasSlash ) { 0153 lastWasSlash = true; 0154 s.append( "/" ); 0155 } 0156 } 0157 else { 0158 lastWasSlash = false; 0159 s.append( path[i] ); 0160 } 0161 } 0162 0163 return s; 0164 } 0165 0166 0167 K3b::Version K3b::kernelVersion() 0168 { 0169 // initialize kernel version 0170 K3b::Version v; 0171 utsname unameinfo; 0172 if( ::uname(&unameinfo) == 0 ) { 0173 v = QString::fromLocal8Bit( unameinfo.release ); 0174 qDebug() << "kernel version: " << v; 0175 } 0176 else 0177 qCritical() << "could not determine kernel version." ; 0178 return v; 0179 } 0180 0181 0182 K3b::Version K3b::simpleKernelVersion() 0183 { 0184 return kernelVersion().simplify(); 0185 } 0186 0187 0188 QString K3b::systemName() 0189 { 0190 QString v; 0191 utsname unameinfo; 0192 if( ::uname(&unameinfo) == 0 ) { 0193 v = QString::fromLocal8Bit( unameinfo.sysname ); 0194 } 0195 else 0196 qCritical() << "could not determine system name." ; 0197 return v; 0198 } 0199 0200 0201 bool K3b::kbFreeOnFs( const QString& path, unsigned long& size, unsigned long& avail ) 0202 { 0203 const QStorageInfo fs(path); 0204 if ( fs.isValid() ) { 0205 size = fs.bytesTotal()/1024; 0206 avail = fs.bytesFree()/1024; 0207 return true; 0208 } 0209 else { 0210 return false; 0211 } 0212 } 0213 0214 0215 KIO::filesize_t K3b::filesize( const QUrl& url ) 0216 { 0217 KIO::filesize_t fSize = 0; 0218 if( url.isLocalFile() ) { 0219 QFileInfo fi( url.toLocalFile() ); 0220 fSize = fi.size(); 0221 } 0222 else { 0223 KIO::UDSEntry uds; 0224 KIO::StatJob* statJob = KIO::stat( url, KIO::HideProgressInfo ); 0225 if (statJob->exec()) 0226 uds = statJob->statResult(); 0227 fSize = uds.numberValue( KIO::UDSEntry::UDS_SIZE ); 0228 } 0229 0230 return fSize; 0231 } 0232 0233 0234 KIO::filesize_t K3b::imageFilesize( const QUrl& url ) 0235 { 0236 KIO::filesize_t size = K3b::filesize( url ); 0237 bool exists = true; 0238 for(int cnt = 0; exists; ++cnt) 0239 { 0240 QUrl nextUrl( url ); 0241 nextUrl.setPath(nextUrl.path() + '.' + QString::number(cnt).rightJustified( 3, '0' )); 0242 #if KIO_VERSION >= QT_VERSION_CHECK(5, 240, 0) 0243 KIO::StatJob* statJob = KIO::stat(nextUrl, KIO::StatJob::SourceSide, KIO::StatDefaultDetails, KIO::HideProgressInfo); 0244 #else 0245 KIO::StatJob* statJob = KIO::statDetails(nextUrl, KIO::StatJob::SourceSide, KIO::StatDefaultDetails, KIO::HideProgressInfo); 0246 #endif 0247 if (statJob->exec()) 0248 size += K3b::filesize(nextUrl); 0249 else 0250 exists = false; 0251 } 0252 return size; 0253 } 0254 0255 0256 QString K3b::cutFilename( const QString& name, int len ) 0257 { 0258 if( name.length() > len ) { 0259 QString ret = name; 0260 0261 // determine extension (we think of an extension to be at most 5 chars in length) 0262 int pos = name.indexOf( '.', -6 ); 0263 if( pos > 0 ) 0264 len -= (name.length() - pos); 0265 0266 ret.truncate( len ); 0267 0268 if( pos > 0 ) 0269 ret.append( name.mid( pos ) ); 0270 0271 return ret; 0272 } 0273 else 0274 return name; 0275 } 0276 0277 0278 QString K3b::removeFilenameExtension( const QString& name ) 0279 { 0280 QString v = name; 0281 int dotpos = v.lastIndexOf( '.' ); 0282 if( dotpos > 0 ) 0283 v.truncate( dotpos ); 0284 return v; 0285 } 0286 0287 0288 QString K3b::appendNumberToFilename( const QString& name, int num, unsigned int maxlen ) 0289 { 0290 // determine extension (we think of an extension to be at most 5 chars in length) 0291 QString result = name; 0292 QString ext; 0293 int pos = name.indexOf( '.', -6 ); 0294 if( pos > 0 ) { 0295 ext = name.mid(pos); 0296 result.truncate( pos ); 0297 } 0298 0299 ext.prepend( QString::number(num) ); 0300 result.truncate( maxlen - ext.length() ); 0301 0302 return result + ext; 0303 } 0304 0305 0306 bool K3b::plainAtapiSupport() 0307 { 0308 // FIXME: what about BSD? 0309 return ( K3b::simpleKernelVersion() >= K3b::Version( 2, 5, 40 ) ); 0310 } 0311 0312 0313 bool K3b::hackedAtapiSupport() 0314 { 0315 // IMPROVEME!!! 0316 // FIXME: since when does the kernel support this? 0317 return ( K3b::simpleKernelVersion() >= K3b::Version( 2, 4, 0 ) ); 0318 } 0319 0320 0321 QString K3b::externalBinDeviceParameter( K3b::Device::Device* dev, const K3b::ExternalBin* bin ) 0322 { 0323 Q_UNUSED( bin ); 0324 return dev->blockDeviceName(); 0325 } 0326 0327 0328 K3b::WritingApp K3b::writingAppFromString( const QString& s ) 0329 { 0330 if( s.toLower() == "cdrdao" ) 0331 return K3b::WritingAppCdrdao; 0332 else if( s.toLower() == "cdrecord" ) 0333 return K3b::WritingAppCdrecord; 0334 else if( s.toLower() == "growisofs" ) 0335 return K3b::WritingAppGrowisofs; 0336 else if( s.toLower() == "dvd+rw-format" ) 0337 return K3b::WritingAppDvdRwFormat; 0338 else if (s.toLower() == "cdrskin") 0339 return K3b::WritingAppCdrskin; 0340 else 0341 return K3b::WritingAppAuto; 0342 } 0343 0344 0345 QString K3b::writingAppToString( K3b::WritingApp app ) 0346 { 0347 switch( app ) { 0348 case WritingAppCdrecord: 0349 return "cdrecord"; 0350 case WritingAppCdrdao: 0351 return "cdrdao"; 0352 case WritingAppGrowisofs: 0353 return "growisofs"; 0354 case WritingAppDvdRwFormat: 0355 return "dvd+rw-format"; 0356 default: 0357 return "auto"; 0358 } 0359 } 0360 0361 0362 QString K3b::writingModeString( K3b::WritingModes modes ) 0363 { 0364 if( modes == WritingModeAuto ) 0365 return i18n("Auto"); 0366 else 0367 return K3b::Device::writingModeString( ( int )modes ); 0368 } 0369 0370 0371 QString K3b::resolveLink( const QString& file ) 0372 { 0373 QFileInfo f( file ); 0374 return f.canonicalFilePath(); 0375 } 0376 0377 0378 QUrl K3b::convertToLocalUrl( const QUrl& url ) 0379 { 0380 if( !url.isLocalFile() ) { 0381 KIO::StatJob* statJob = KIO::mostLocalUrl( url, KIO::HideProgressInfo ); 0382 QUrl result; 0383 QObject::connect( statJob, &KJob::result, [&](KJob*) { 0384 if( statJob->error() == KJob::NoError ) 0385 result = statJob->mostLocalUrl(); 0386 } ); 0387 statJob->exec(); 0388 return result; 0389 } 0390 0391 return url; 0392 } 0393 0394 0395 QList<QUrl> K3b::convertToLocalUrls( const QList<QUrl>& urls ) 0396 { 0397 QList<QUrl> r; 0398 for( QList<QUrl>::const_iterator it = urls.constBegin(); it != urls.constEnd(); ++it ) 0399 r.append( convertToLocalUrl( *it ) ); 0400 return r; 0401 } 0402 0403 0404 qint16 K3b::fromLe16( char* data ) 0405 { 0406 #ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN 0407 return swapByteOrder( *((qint16*)data) ); 0408 #else 0409 return *((qint16*)data); 0410 #endif 0411 } 0412 0413 0414 qint32 K3b::fromLe32( char* data ) 0415 { 0416 #ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN 0417 return swapByteOrder( *((qint32*)data) ); 0418 #else 0419 return *((qint32*)data); 0420 #endif 0421 } 0422 0423 0424 qint64 K3b::fromLe64( char* data ) 0425 { 0426 #ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN 0427 return swapByteOrder( *((qint64*)data) ); 0428 #else 0429 return *((qint64*)data); 0430 #endif 0431 } 0432 0433 0434 QString K3b::findExe( const QString& name ) 0435 { 0436 // first we search the path 0437 QString bin = QStandardPaths::findExecutable( name ); 0438 0439 // then go on with our own little list 0440 if( bin.isEmpty() ) 0441 bin = QStandardPaths::findExecutable( name, k3bcore->externalBinManager()->searchPath() ); 0442 0443 return bin; 0444 } 0445 0446 0447 bool K3b::isMounted( K3b::Device::Device* dev ) 0448 { 0449 if( !dev ) 0450 return false; 0451 else 0452 return( KMountPoint::currentMountPoints().findByDevice( dev->blockDeviceName() ).data() != 0 ); 0453 } 0454 0455 0456 bool K3b::unmount( K3b::Device::Device* dev ) 0457 { 0458 if( !dev ) 0459 return false; 0460 0461 Solid::StorageAccess *sa = dev->solidStorage(); 0462 if ( sa && sa->teardown() ){ 0463 return true; 0464 } 0465 0466 QString mntDev = dev->blockDeviceName(); 0467 0468 // first try to unmount it the standard way 0469 KIO::SimpleJob* unmountJob = KIO::unmount( mntDev ); 0470 bool result = true; 0471 QObject::connect( unmountJob, &KJob::result, [&](KJob* job){ result = ( job->error() != KJob::NoError ); } ); 0472 if( unmountJob->exec() && result ) 0473 return true; 0474 0475 QString mntPath; 0476 if ( KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev->blockDeviceName() ) ) { 0477 mntPath = mp->mountPoint(); 0478 } 0479 if ( mntPath.isEmpty() ) { 0480 mntPath = dev->blockDeviceName(); 0481 } 0482 0483 QString umountBin = K3b::findExe( "umount" ); 0484 if( !umountBin.isEmpty() ) { 0485 KProcess p; 0486 p << umountBin; 0487 p << "-l"; // lazy unmount 0488 p << mntPath; 0489 p.start(); 0490 if (p.waitForFinished(-1)) 0491 return true; 0492 } 0493 0494 // now try pmount 0495 QString pumountBin = K3b::findExe( "pumount" ); 0496 if( !pumountBin.isEmpty() ) { 0497 KProcess p; 0498 p << pumountBin; 0499 p << "-l"; // lazy unmount 0500 p << mntPath; 0501 p.start(); 0502 return p.waitForFinished(-1); 0503 } 0504 else { 0505 return false; 0506 } 0507 } 0508 0509 0510 bool K3b::mount( K3b::Device::Device* dev ) 0511 { 0512 if( !dev ) 0513 return false; 0514 0515 QString mntDev = dev->blockDeviceName(); 0516 0517 // first try to mount it the standard way 0518 KIO::SimpleJob* mountJob = KIO::mount( true, QByteArray(), mntDev, QString() ); 0519 bool result = true; 0520 QObject::connect( mountJob, &KJob::result, [&](KJob* job){ result = ( job->error() != KJob::NoError ); } ); 0521 if( mountJob->exec() && result ) 0522 return true; 0523 0524 Solid::StorageAccess* sa = dev->solidStorage(); 0525 if ( sa && sa->setup() ) { 0526 return true; 0527 } 0528 0529 // now try pmount 0530 QString pmountBin = K3b::findExe( "pmount" ); 0531 if( !pmountBin.isEmpty() ) { 0532 KProcess p; 0533 p << pmountBin; 0534 p << mntDev; 0535 p.start(); 0536 return p.waitForFinished(-1); 0537 } 0538 0539 // and the most simple one 0540 QString mountBin = K3b::findExe( "mount" ); 0541 if( !mountBin.isEmpty() ) { 0542 KProcess p; 0543 p << mountBin; 0544 p << mntDev; 0545 p.start(); 0546 return p.waitForFinished(-1); 0547 } 0548 0549 return false; 0550 } 0551 0552 0553 bool K3b::eject( K3b::Device::Device* dev ) 0554 { 0555 if( K3b::isMounted( dev ) ) 0556 K3b::unmount( dev ); 0557 0558 if ( dev->solidDevice().as<Solid::OpticalDrive>()->eject() || 0559 dev->eject() ) { 0560 // to be on the safe side, especially with respect to the EmptyDiscWaiter 0561 // we reset the device in the cache. 0562 k3bcore->mediaCache()->resetDevice( dev ); 0563 return true; 0564 } 0565 else { 0566 return false; 0567 } 0568 } 0569 0570 0571 K3b::Device::SpeedMultiplicator K3b::speedMultiplicatorForMediaType( K3b::Device::MediaType mediaType ) 0572 { 0573 if ( mediaType & K3b::Device::MEDIA_DVD_ALL ) { 0574 return K3b::Device::SPEED_FACTOR_DVD; 0575 } 0576 else if ( mediaType & K3b::Device::MEDIA_BD_ALL ) { 0577 return K3b::Device::SPEED_FACTOR_BD; 0578 } 0579 else { 0580 return K3b::Device::SPEED_FACTOR_CD; 0581 } 0582 } 0583 0584 0585 QString K3b::formatWritingSpeedFactor( int speed, K3b::Device::MediaType mediaType, SpeedFormat speedFormat ) 0586 { 0587 const int speedFactor = speedMultiplicatorForMediaType( mediaType ); 0588 int normalizedSpeed = speed; 0589 int diff = normalizedSpeed%speedFactor; 0590 if ( diff < 5 ) 0591 normalizedSpeed = speed-diff; 0592 else if ( diff > speedFactor-5 ) 0593 normalizedSpeed = speed+speedFactor-diff; 0594 0595 // speed may be a float number. example: DVD+R(W): 2.4x 0596 if ( mediaType & K3b::Device::MEDIA_DVD_ALL && 0597 normalizedSpeed%speedFactor > 0 && 0598 speedFormat != SpeedFormatInteger ) { 0599 return QString::number( ( float )normalizedSpeed/( float )speedFactor, 'f', 1 ); 0600 } 0601 else { 0602 return QString::number( normalizedSpeed/speedFactor ); 0603 } 0604 } 0605 0606 0607 bool K3b::IsOverburnAllowed( const K3b::Msf& projectSize, const K3b::Msf& capacity ) 0608 { 0609 return IsOverburnAllowed( projectSize, capacity, Msf() ); 0610 } 0611 0612 0613 bool K3b::IsOverburnAllowed( const Msf& projectSize, const Msf& capacity, const Msf& usedCapacity ) 0614 { 0615 return( k3bcore->globalSettings()->overburn() && 0616 (projectSize + usedCapacity) <= ( capacity.lba() + capacity.lba() / 4 ) ); // 25% tolerance in overburn mode 0617 } 0618 0619 0620 QDebug& K3b::operator<<( QDebug& dbg, K3b::WritingMode mode ) 0621 { 0622 return dbg << K3b::Device::WritingMode( mode ); 0623 } 0624 0625 0626 QDebug& K3b::operator<<( QDebug& dbg, K3b::WritingModes modes ) 0627 { 0628 return dbg << K3b::Device::WritingModes( ( int )modes ); 0629 }