File indexing completed on 2024-04-28 04:49:22

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 }