File indexing completed on 2024-05-05 05:48:44

0001 /*
0002     SPDX-FileCopyrightText: 2008-2011 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2012-2020 Andrius Štikonas <andrius@stikonas.eu>
0004     SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org>
0005     SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com>
0006     SPDX-FileCopyrightText: 2017 Pali Rohár <pali.rohar@gmail.com>
0007     SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
0008     SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net>
0009     SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com>
0010     SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com>
0011 
0012     SPDX-License-Identifier: GPL-3.0-or-later
0013 */
0014 
0015 #include "fs/filesystem.h"
0016 #include "core/fstab.h"
0017 
0018 #include "fs/lvm2_pv.h"
0019 
0020 #include "backend/corebackend.h"
0021 #include "backend/corebackendmanager.h"
0022 
0023 #include "util/externalcommand.h"
0024 #include "util/externalcommand_trustedprefixes.h"
0025 #include "util/capacity.h"
0026 #include "util/helpers.h"
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <QColor>
0031 #include <QFile>
0032 #include <QFileInfo>
0033 #include <QStandardPaths>
0034 #include <QStorageInfo>
0035 #include <QTemporaryDir>
0036 
0037 const std::vector<QColor> FileSystem::defaultColorCode =
0038 {
0039 {
0040     QColor( 220,205,175 ), // unknown
0041     QColor( 187,249,207 ), // extended
0042     QColor( 102,121,150 ), // ext2
0043     QColor( 122,145,180 ), // ext3
0044     QColor( 143,170,210 ), // ext4
0045     QColor( 155,155,130 ), // swap
0046     QColor( 204,179,215 ), // fat16
0047     QColor( 229,201,240 ), // fat32
0048     QColor( 244,214,255 ), // ntfs
0049     QColor( 216,220,135 ), // reiser
0050     QColor( 251,255,157 ), // reiser4
0051     QColor( 200,255,254 ), // xfs
0052     QColor( 137,200,198 ), // jfs
0053     QColor( 210,136,142 ), // hfs
0054     QColor( 240,165,171 ), // hfs+
0055     QColor( 151,220,134 ), // ufs
0056     QColor( 220,205,175 ), // unformatted
0057     QColor( 173,205,255 ), // btrfs
0058     QColor( 176,155,185 ), // hpfs
0059     QColor( 170,30,77 ),   // luks
0060     QColor( 96,140,85 ),   // ocfs2
0061     QColor( 33,137,108 ),  // zfs
0062     QColor( 250,230,255 ), // exfat
0063     QColor( 242,155,104 ), // nilfs2
0064     QColor( 160,210,180 ), // lvm2 pv
0065     QColor( 255,170,0 ),   // f2fs
0066     QColor( 170,120,255 ), // udf
0067     QColor( 177,82,69 ),   // iso9660
0068     QColor( 223,39,104 ),  // luks2
0069     QColor( 204,179,255 ), // fat12
0070     QColor( 255,100,100 ), // linux_raid_member
0071     QColor( 110,20,50 ),   // bitlocker
0072     QColor( 255,155,174 ), // apfs
0073     QColor( 0,170,255 ), // minix
0074 }
0075 };
0076 
0077 struct FileSystemPrivate {
0078     FileSystem::Type m_Type;
0079     qint64 m_FirstSector;
0080     qint64 m_LastSector;
0081     qint64 m_SectorSize;
0082     qint64 m_SectorsUsed;
0083     QString m_Label;
0084     QString m_UUID;
0085     QString m_posixPermissions;
0086     QStringList m_AvailableFeatures;
0087     QVariantMap m_Features;
0088 };
0089 
0090 /** Creates a new FileSystem object
0091     @param firstsector the first sector used by this FileSystem on the Device
0092     @param lastsector the last sector used by this FileSystem on the Device
0093     @param sectorsused the number of sectors in use on the FileSystem
0094     @param label the FileSystem label
0095     @param type the FileSystem type
0096 */
0097 FileSystem::FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type type) :
0098     d(std::make_unique<FileSystemPrivate>())
0099 {
0100     d->m_Type = type;
0101     d->m_FirstSector = firstsector;
0102     d->m_LastSector = lastsector;
0103     d->m_SectorsUsed = sectorsused;
0104     d->m_Label = label;
0105     d->m_UUID = QString();
0106 }
0107 
0108 /** Creates a new FileSystem object
0109     @param firstsector the first sector used by this FileSystem on the Device
0110     @param lastsector the last sector used by this FileSystem on the Device
0111     @param sectorsused the number of sectors in use on the FileSystem
0112     @param label the FileSystem label
0113     @param features the FileSystem features
0114     @param type the FileSystem type
0115 */
0116 FileSystem::FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features, FileSystem::Type type) :
0117     d(std::make_unique<FileSystemPrivate>())
0118 {
0119     d->m_Type = type;
0120     d->m_FirstSector = firstsector;
0121     d->m_LastSector = lastsector;
0122     d->m_SectorsUsed = sectorsused;
0123     d->m_Label = label;
0124     d->m_Features = features;
0125     d->m_UUID = QString();
0126 }
0127 
0128 FileSystem::~FileSystem()
0129 {
0130 }
0131 
0132 QString FileSystem::implPosixPermissions() const
0133 {
0134     return d->m_posixPermissions;
0135 }
0136 
0137 void FileSystem::implSetPosixPermissions(const QString& permissions)
0138 {
0139     d->m_posixPermissions = permissions;
0140 }
0141 
0142 
0143 bool FileSystem::execChangePosixPermission(Report& report, const QString& deviceNode)
0144 {
0145     // do nothing if the posix permissions is not used here.
0146     if (d->m_posixPermissions.isEmpty()) {
0147         return true;
0148     }
0149 
0150     QTemporaryDir tmpDir;
0151 
0152     ExternalCommand mountCmd(report, QStringLiteral("mount"),
0153                              { deviceNode, tmpDir.path() });
0154 
0155     bool step =  mountCmd.run() && mountCmd.exitCode() == 0;
0156     if (!step) {
0157         return false;
0158     }
0159 
0160     ExternalCommand chmodCmd(report, QStringLiteral("chmod"),
0161                              // forcing recursive, should be empty but
0162                              // programming is weird.
0163                             {
0164                                 d->m_posixPermissions,
0165                                 tmpDir.path(),
0166                                 QStringLiteral("--recursive")
0167                             });
0168 
0169     const bool chmodStep =  chmodCmd.run() && chmodCmd.exitCode() == 0;
0170 
0171     ExternalCommand umountCmd(report, QStringLiteral("umount"),
0172                             // forcing recursive, should be empty but
0173                              // programming is weird.
0174                             {
0175                                 deviceNode,
0176                             });
0177 
0178     const bool umountStep =  umountCmd.run() && umountCmd.exitCode() == 0;
0179 
0180     // we can't return false if chmodStep fails because we still need to umount
0181     // the drive.
0182     if (!chmodStep) {
0183         return false;
0184     }
0185 
0186     if (!umountStep) {
0187         return false;
0188     }
0189 
0190     return true;
0191 }
0192 
0193 /** Reads the capacity in use on this FileSystem
0194     @param deviceNode the device node for the Partition the FileSystem is on
0195     @return the used capacity in bytes or -1 in case of an error
0196 */
0197 qint64 FileSystem::readUsedCapacity(const QString& deviceNode) const
0198 {
0199     Q_UNUSED(deviceNode)
0200 
0201     return -1;
0202 }
0203 
0204 FileSystem::Type FileSystem::detectFileSystem(const QString& partitionPath)
0205 {
0206     return CoreBackendManager::self()->backend()->detectFileSystem(partitionPath);
0207 }
0208 
0209 QString FileSystem::detectMountPoint(FileSystem* fs, const QString& partitionPath)
0210 {
0211     if (fs->type() == FileSystem::Type::Lvm2_PV)
0212         return FS::lvm2_pv::getVGName(partitionPath);
0213 
0214     if (partitionPath.isEmpty()) // Happens when during initial scan LUKS is closed
0215         return QString();
0216 
0217     QStringList mountPoints;
0218     QFileInfo partitionPathFileInfo(partitionPath);
0219     QString partitionCanonicalPath = partitionPathFileInfo.canonicalFilePath();
0220     const QList<QStorageInfo> mountedVolumes = QStorageInfo::mountedVolumes();
0221     for (const QStorageInfo &storage : mountedVolumes) {
0222         if (partitionCanonicalPath == QFileInfo(QFile::decodeName(storage.device())).canonicalFilePath() ) {
0223             mountPoints.append(storage.rootPath());
0224         }
0225     }
0226 
0227     mountPoints.append(possibleMountPoints(partitionPath));
0228 
0229     return mountPoints.isEmpty() ? QString() : mountPoints.first();
0230 }
0231 
0232 bool FileSystem::detectMountStatus(FileSystem* fs, const QString& partitionPath)
0233 {
0234     bool mounted = false;
0235 
0236     if (fs->type() == FileSystem::Type::Lvm2_PV) {
0237         mounted = !FS::lvm2_pv::getVGName(partitionPath).isEmpty();
0238     } else {
0239         mounted = isMounted(partitionPath);
0240     }
0241     return mounted;
0242 }
0243 
0244 /** Reads the label for this FileSystem
0245     @param deviceNode the device node for the Partition the FileSystem is on
0246     @return the FileSystem label or an empty string in case of error
0247 */
0248 QString FileSystem::readLabel(const QString& deviceNode) const
0249 {
0250     return CoreBackendManager::self()->backend()->readLabel(deviceNode);
0251 }
0252 
0253 /** Creates a new FileSystem
0254     @param report Report to write status information to
0255     @param deviceNode the device node for the Partition to create the FileSystem on
0256     @return true if successful
0257 */
0258 bool FileSystem::create(Report& report, const QString& deviceNode)
0259 {
0260     Q_UNUSED(report)
0261     Q_UNUSED(deviceNode)
0262 
0263     return true;
0264 }
0265 
0266 /** Creates a new FileSystem with a specified Label
0267     @param report Report to write status information to
0268     @param deviceNode the device node for the Partition to create the FileSystem on
0269     @param label the new label for the FileSystem
0270     @return true if successful
0271 */
0272 bool FileSystem::createWithLabel(Report& report, const QString& deviceNode, const QString& label)
0273 {
0274     Q_UNUSED(report)
0275     Q_UNUSED(deviceNode)
0276     Q_UNUSED(label)
0277 
0278     return true;
0279 }
0280 
0281 /** Scans a new FileSystem and load file system specific class variables.
0282  *  @param deviceNode the device node for the Partition to create the FileSystem on
0283 */
0284 void FileSystem::scan(const QString& deviceNode)
0285 {
0286     Q_UNUSED(deviceNode)
0287 }
0288 
0289 /** Resize a FileSystem to a given new length
0290     @param report Report to write status information to
0291     @param deviceNode the device node for the Partition the FileSystem is on
0292     @param newLength the new length for the FileSystem in bytes
0293     @return true on success
0294 */
0295 bool FileSystem::resize(Report& report, const QString& deviceNode, qint64 newLength) const
0296 {
0297     Q_UNUSED(report)
0298     Q_UNUSED(deviceNode)
0299     Q_UNUSED(newLength)
0300 
0301     return true;
0302 }
0303 
0304 /** Resize a mounted FileSystem to a given new length
0305     @param report Report to write status information to
0306     @param deviceNode the device node for the Partition the FileSystem is on
0307     @param mountPoint the mount point where FileSystem is mounted on
0308     @param newLength the new length for the FileSystem in bytes
0309     @return true on success
0310 */
0311 bool FileSystem::resizeOnline(Report& report, const QString& deviceNode, const QString& mountPoint, qint64 newLength) const
0312 {
0313     Q_UNUSED(report)
0314     Q_UNUSED(deviceNode)
0315     Q_UNUSED(mountPoint)
0316     Q_UNUSED(newLength)
0317 
0318     return true;
0319 }
0320 
0321 /** Move a FileSystem to a new start sector
0322     @param report Report to write status information to
0323     @param deviceNode the device node for the Partition the FileSystem is on
0324     @param newStartSector the new start sector for the FileSystem
0325     @return true on success
0326 */
0327 bool FileSystem::move(Report& report, const QString& deviceNode, qint64 newStartSector) const
0328 {
0329     Q_UNUSED(report)
0330     Q_UNUSED(deviceNode)
0331     Q_UNUSED(newStartSector)
0332 
0333     return true;
0334 }
0335 
0336 /** Writes a label for the FileSystem to disk
0337     @param report Report to write status information to
0338     @param deviceNode the device node for the Partition the FileSystem is on
0339     @param newLabel the new label for the FileSystem
0340     @return true on success
0341 */
0342 bool FileSystem::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel)
0343 {
0344     Q_UNUSED(report)
0345     Q_UNUSED(deviceNode)
0346     Q_UNUSED(newLabel)
0347 
0348     return true;
0349 }
0350 
0351 /** Writes a label for the FileSystem to disk
0352     @param report Report to write status information to
0353     @param deviceNode the device node for the Partition the FileSystem is on
0354     @param mountPoint the mount point where FileSystem is mounted on
0355     @param newLabel the new label for the FileSystem
0356     @return true on success
0357 */
0358 bool FileSystem::writeLabelOnline(Report& report, const QString& deviceNode, const QString& mountPoint, const QString& newLabel)
0359 {
0360     Q_UNUSED(report)
0361     Q_UNUSED(deviceNode)
0362     Q_UNUSED(mountPoint)
0363     Q_UNUSED(newLabel)
0364 
0365     return true;
0366 }
0367 
0368 /** Copies a FileSystem from one Partition to another
0369     @param report Report to write status information to
0370     @param targetDeviceNode device node of the target Partition
0371     @param sourceDeviceNode device node of the source Partition
0372     @return true on success
0373 */
0374 bool FileSystem::copy(Report& report, const QString& targetDeviceNode, const QString& sourceDeviceNode) const
0375 {
0376     Q_UNUSED(report)
0377     Q_UNUSED(targetDeviceNode)
0378     Q_UNUSED(sourceDeviceNode)
0379 
0380     return true;
0381 }
0382 
0383 /** Backs up a FileSystem to a file
0384     @param report Report to write status information to
0385     @param sourceDevice Device the source FileSystem is on
0386     @param deviceNode device node of the source Partition
0387     @param filename name of the file to backup to
0388     @return true on success
0389 */
0390 bool FileSystem::backup(Report& report, const Device& sourceDevice, const QString& deviceNode, const QString& filename) const
0391 {
0392     Q_UNUSED(report)
0393     Q_UNUSED(sourceDevice)
0394     Q_UNUSED(deviceNode)
0395     Q_UNUSED(filename)
0396 
0397     return false;
0398 }
0399 
0400 /** Removes a FileSystem
0401     @param report Report to write status information to
0402     @param deviceNode the device node for the Partition the FileSystem is on
0403     @return true if FileSystem is removed
0404 */
0405 bool FileSystem::remove(Report& report, const QString& deviceNode) const
0406 {
0407     Q_UNUSED(report)
0408     Q_UNUSED(deviceNode)
0409 
0410     return true;
0411 }
0412 
0413 /** Checks a FileSystem for errors
0414     @param report Report to write status information to
0415     @param deviceNode the device node for the Partition the FileSystem is on
0416     @return true if FileSystem is error-free
0417 */
0418 bool FileSystem::check(Report& report, const QString& deviceNode) const
0419 {
0420     Q_UNUSED(report)
0421     Q_UNUSED(deviceNode)
0422 
0423     return true;
0424 }
0425 
0426 /** Updates a FileSystem UUID on disk
0427     @param report Report to write status information to
0428     @param deviceNode the device node for the Partition the FileSystem is on
0429     @return true on success
0430 */
0431 bool FileSystem::updateUUID(Report& report, const QString& deviceNode) const
0432 {
0433     Q_UNUSED(report)
0434     Q_UNUSED(deviceNode)
0435 
0436     return true;
0437 }
0438 
0439 /** Returns the FileSystem UUID by calling a FileSystem-specific helper program
0440     @param deviceNode the device node for the Partition the FileSystem is on
0441     @return the UUID or an empty string if the FileSystem does not support UUIDs
0442  */
0443 QString FileSystem::readUUID(const QString& deviceNode) const
0444 {
0445     return CoreBackendManager::self()->backend()->readUUID(deviceNode);
0446 }
0447 
0448 /** Give implementations of FileSystem a chance to update the boot sector after the
0449     file system has been moved or copied.
0450     @param report Report to write status information to
0451     @param deviceNode the device node for the Partition the FileSystem is on
0452     @return true on success
0453 */
0454 bool FileSystem::updateBootSector(Report& report, const QString& deviceNode) const
0455 {
0456     Q_UNUSED(report)
0457     Q_UNUSED(deviceNode)
0458 
0459     return true;
0460 }
0461 
0462 /** @return the minimum capacity valid for this FileSystem in bytes */
0463 qint64 FileSystem::minCapacity() const
0464 {
0465     return 8 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB);
0466 }
0467 
0468 /** @return the maximum capacity valid for this FileSystem in bytes */
0469 qint64 FileSystem::maxCapacity() const
0470 {
0471     return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB);
0472 }
0473 
0474 /** @return the maximum label length valid for this FileSystem */
0475 int FileSystem::maxLabelLength() const
0476 {
0477     return 16;
0478 }
0479 
0480 /** Validates the label for this FileSystem
0481  * @param parent the parent widget passed to the QObject constructor
0482  * @return QValidator to validate the file system label line edit input */
0483 QValidator* FileSystem::labelValidator(QObject *parent) const
0484 {
0485     Q_UNUSED(parent)
0486     return nullptr;
0487 }
0488 
0489 /** @return this FileSystem's type as printable name */
0490 QString FileSystem::name(const QStringList& languages) const
0491 {
0492     return nameForType(type(), languages);
0493 }
0494 
0495 FileSystem::Type FileSystem::type() const
0496 {
0497     return d->m_Type;
0498 }
0499 
0500 /** @return a pointer to a QString C array with all FileSystem names */
0501 static const KLocalizedString* typeNames()
0502 {
0503     static const KLocalizedString s[] = {
0504         kxi18nc("@item filesystem name", "unknown"),
0505         kxi18nc("@item filesystem name", "extended"),
0506 
0507         kxi18nc("@item filesystem name", "ext2"),
0508         kxi18nc("@item filesystem name", "ext3"),
0509         kxi18nc("@item filesystem name", "ext4"),
0510         kxi18nc("@item filesystem name", "linuxswap"),
0511         kxi18nc("@item filesystem name", "fat16"),
0512         kxi18nc("@item filesystem name", "fat32"),
0513         kxi18nc("@item filesystem name", "ntfs"),
0514         kxi18nc("@item filesystem name", "reiser"),
0515         kxi18nc("@item filesystem name", "reiser4"),
0516         kxi18nc("@item filesystem name", "xfs"),
0517         kxi18nc("@item filesystem name", "jfs"),
0518         kxi18nc("@item filesystem name", "hfs"),
0519         kxi18nc("@item filesystem name", "hfsplus"),
0520         kxi18nc("@item filesystem name", "ufs"),
0521         kxi18nc("@item filesystem name", "unformatted"),
0522         kxi18nc("@item filesystem name", "btrfs"),
0523         kxi18nc("@item filesystem name", "hpfs"),
0524         kxi18nc("@item filesystem name", "luks"),
0525         kxi18nc("@item filesystem name", "ocfs2"),
0526         kxi18nc("@item filesystem name", "zfs"),
0527         kxi18nc("@item filesystem name", "exfat"),
0528         kxi18nc("@item filesystem name", "nilfs2"),
0529         kxi18nc("@item filesystem name", "lvm2 pv"),
0530         kxi18nc("@item filesystem name", "f2fs"),
0531         kxi18nc("@item filesystem name", "udf"),
0532         kxi18nc("@item filesystem name", "iso9660"),
0533         kxi18nc("@item filesystem name", "luks2"),
0534         kxi18nc("@item filesystem name", "fat12"),
0535         kxi18nc("@item filesystem name", "linux_raid_member"),
0536         kxi18nc("@item filesystem name", "BitLocker"),
0537         kxi18nc("@item filesystem name", "apfs"),
0538         kxi18nc("@item filesystem name", "minix"),
0539     };
0540 
0541     return s;
0542 }
0543 
0544 /** @param t the type to get the name for
0545     @return the printable name for the given type
0546 */
0547 QString FileSystem::nameForType(FileSystem::Type t, const QStringList& languages)
0548 {
0549     Q_ASSERT(t < Type::__lastType);
0550 
0551     return typeNames()[static_cast<int>(t)].toString(languages);
0552 }
0553 
0554 /** @param s the name to get the type for
0555     @return the type for the name or FileSystem::Unknown if not found
0556 */
0557 FileSystem::Type FileSystem::typeForName(const QString& s, const QStringList& languages )
0558 {
0559     for (quint32 i = 0; i < static_cast<int>(Type::__lastType); i++)
0560         if (typeNames()[i].toString(languages) == s)
0561             return static_cast<FileSystem::Type>(i);
0562 
0563     return Type::Unknown;
0564 }
0565 
0566 /** @return a QList of all known types */
0567 QList<FileSystem::Type> FileSystem::types()
0568 {
0569     QList<FileSystem::Type> result;
0570 
0571     int i = static_cast<int>(Type::Ext2); // first "real" filesystem
0572     while (i != static_cast<int>(Type::__lastType))
0573         result.append(static_cast<FileSystem::Type>(i++));
0574 
0575     return result;
0576 }
0577 
0578 /** @return printable menu title for mounting this FileSystem */
0579 QString FileSystem::mountTitle() const
0580 {
0581     return xi18nc("@title:menu", "Mount");
0582 }
0583 
0584 /** @return printable menu title for unmounting this FileSystem */
0585 QString FileSystem::unmountTitle() const
0586 {
0587     return xi18nc("@title:menu", "Unmount");
0588 }
0589 
0590 /** Moves a FileSystem to a new start sector.
0591     @param newStartSector where the FileSystem should be moved to
0592 */
0593 void FileSystem::move(qint64 newStartSector)
0594 {
0595     const qint64 savedLength = length();
0596     setFirstSector(newStartSector);
0597     setLastSector(newStartSector + savedLength - 1);
0598 }
0599 bool FileSystem::canMount(const QString& deviceNode, const QString& mountPoint) const
0600 {
0601     Q_UNUSED(deviceNode)
0602     // cannot mount if we have no mount points
0603     return !mountPoint.isEmpty();
0604 }
0605 
0606 /** Attempt to mount this FileSystem on a given mount point
0607     @param report the report to write information to
0608     @param deviceNode the path to the device that is to be unmounted
0609     @param mountPoint the mount point to mount the FileSystem on
0610     @return true on success
0611 */
0612 bool FileSystem::mount(Report& report, const QString &deviceNode, const QString &mountPoint)
0613 {
0614     ExternalCommand mountCmd(   report,
0615                                 QStringLiteral("mount"),
0616                               { QStringLiteral("--verbose"),
0617                                 deviceNode,
0618                                 mountPoint });
0619     if (mountCmd.run() && mountCmd.exitCode() == 0) {
0620         return true;
0621     }
0622     return false;
0623 }
0624 
0625 /** Attempt to unmount this FileSystem
0626     @param report the report to write information to
0627     @param deviceNode the path to the device that is to be unmounted
0628     @return true on success
0629  */
0630 bool FileSystem::unmount(Report& report, const QString& deviceNode)
0631 {
0632     ExternalCommand umountCmd(  report,
0633                                 QStringLiteral("umount"),
0634                               { QStringLiteral("--verbose"),
0635                                 QStringLiteral("--all-targets"),
0636                                 deviceNode });
0637     if ( umountCmd.run() && umountCmd.exitCode() == 0 )
0638         return true;
0639     return false;
0640 }
0641 
0642 qint64 FileSystem::firstSector() const
0643 {
0644     return d->m_FirstSector;
0645 }
0646 
0647 qint64 FileSystem::lastSector() const
0648 {
0649         return d->m_LastSector;
0650 }
0651 
0652 bool FileSystem::findExternal(const QString& cmdName, const QStringList& args, int expectedCode)
0653 {
0654     QString cmdFullPath = findTrustedCommand(cmdName);
0655 
0656     if (cmdFullPath.isEmpty())
0657         return false;
0658 
0659     ExternalCommand cmd(cmdFullPath, args);
0660     if (!cmd.run())
0661         return false;
0662 
0663     return cmd.exitCode() == 0 || cmd.exitCode() == expectedCode;
0664 }
0665 
0666 void FileSystem::addAvailableFeature(const QString& name)
0667 {
0668     d->m_AvailableFeatures.append(name);
0669 }
0670 
0671 void FileSystem::addFeature(const QString& name, const QVariant& value)
0672 {
0673     d->m_Features.insert(name, value);
0674 }
0675 
0676 void FileSystem::addFeatures(const QVariantMap& features)
0677 {
0678     for (const auto& k : features.keys()) {
0679         d->m_Features.insert(k, features.value(k));
0680     }
0681 }
0682 
0683 bool FileSystem::supportToolFound() const
0684 {
0685     return false;
0686 }
0687 
0688 FileSystem::SupportTool FileSystem::supportToolName() const
0689 {
0690     return SupportTool();
0691 }
0692 
0693 void FileSystem::setFirstSector(qint64 s)
0694 {
0695     d->m_FirstSector = s;
0696 }
0697 
0698 void FileSystem::setLastSector(qint64 s)
0699 {
0700     d->m_LastSector = s;
0701 }
0702 
0703 const QString& FileSystem::label() const
0704 {
0705     return d->m_Label;
0706 }
0707 
0708 const QStringList& FileSystem::availableFeatures() const
0709 {
0710     return d->m_AvailableFeatures;
0711 }
0712 
0713 const QVariantMap& FileSystem::features() const
0714 {
0715     return d->m_Features;
0716 }
0717 
0718 qint64 FileSystem::sectorSize() const
0719 {
0720     return d->m_SectorSize;
0721 }
0722 
0723 qint64 FileSystem::sectorsUsed() const
0724 {
0725     return d->m_SectorsUsed;
0726 }
0727 
0728 const QString& FileSystem::uuid() const
0729 {
0730     return d->m_UUID;
0731 }
0732 
0733 void FileSystem::setSectorSize(qint64 s)
0734 {
0735     d->m_SectorSize = s;
0736 }
0737 
0738 void FileSystem::setSectorsUsed(qint64 s)
0739 {
0740     d->m_SectorsUsed = s;
0741 }
0742 
0743 void FileSystem::setLabel(const QString& s)
0744 {
0745     d->m_Label = s;
0746 }
0747 
0748 void FileSystem::setUUID(const QString& s)
0749 {
0750     d->m_UUID = s;
0751 }