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

0001 /*
0002     SPDX-FileCopyrightText: 2008-2011 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2013-2018 Andrius Štikonas <andrius@stikonas.eu>
0004     SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
0005     SPDX-FileCopyrightText: 2017 Pali Rohár <pali.rohar@gmail.com>
0006     SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com>
0007     SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com>
0008 
0009     SPDX-License-Identifier: GPL-3.0-or-later
0010 */
0011 
0012 #include "fs/fat12.h"
0013 
0014 #include "util/externalcommand.h"
0015 #include "util/capacity.h"
0016 #include "util/report.h"
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <QRegularExpression>
0021 #include <QRegularExpressionValidator>
0022 #include <QString>
0023 #include <QStringList>
0024 
0025 #include <QDebug>
0026 #include <QtMath>
0027 
0028 #include <ctime>
0029 
0030 namespace FS
0031 {
0032 FileSystem::CommandSupportType fat12::m_GetUsed = FileSystem::cmdSupportNone;
0033 FileSystem::CommandSupportType fat12::m_GetLabel = FileSystem::cmdSupportNone;
0034 FileSystem::CommandSupportType fat12::m_SetLabel = FileSystem::cmdSupportNone;
0035 FileSystem::CommandSupportType fat12::m_Create = FileSystem::cmdSupportNone;
0036 FileSystem::CommandSupportType fat12::m_Grow = FileSystem::cmdSupportNone;
0037 FileSystem::CommandSupportType fat12::m_Shrink = FileSystem::cmdSupportNone;
0038 FileSystem::CommandSupportType fat12::m_Move = FileSystem::cmdSupportNone;
0039 FileSystem::CommandSupportType fat12::m_Check = FileSystem::cmdSupportNone;
0040 FileSystem::CommandSupportType fat12::m_Copy = FileSystem::cmdSupportNone;
0041 FileSystem::CommandSupportType fat12::m_Backup = FileSystem::cmdSupportNone;
0042 FileSystem::CommandSupportType fat12::m_UpdateUUID = FileSystem::cmdSupportNone;
0043 FileSystem::CommandSupportType fat12::m_GetUUID = FileSystem::cmdSupportNone;
0044 
0045 fat12::fat12(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features, FileSystem::Type t) :
0046     FileSystem(firstsector, lastsector, sectorsused, label, features, t)
0047 {
0048 }
0049 
0050 void fat12::init()
0051 {
0052     m_Create = m_GetUsed = m_Check = findExternal(QStringLiteral("mkfs.fat"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone;
0053     m_GetLabel = cmdSupportCore;
0054     m_SetLabel = findExternal(QStringLiteral("fatlabel")) ? cmdSupportFileSystem : cmdSupportNone;
0055     m_Move = cmdSupportCore;
0056     m_Copy = cmdSupportCore;
0057     m_Backup = cmdSupportCore;
0058     m_UpdateUUID = cmdSupportCore;
0059     m_GetUUID = cmdSupportCore;
0060 
0061     if (m_Create == cmdSupportFileSystem) {
0062         addAvailableFeature(QStringLiteral("sector-size"));
0063         addAvailableFeature(QStringLiteral("sectors-per-cluster"));
0064     }
0065 }
0066 
0067 bool fat12::supportToolFound() const
0068 {
0069     return
0070         m_GetUsed != cmdSupportNone &&
0071         m_GetLabel != cmdSupportNone &&
0072         m_SetLabel != cmdSupportNone &&
0073         m_Create != cmdSupportNone &&
0074         m_Check != cmdSupportNone &&
0075         m_UpdateUUID != cmdSupportNone &&
0076         m_Copy != cmdSupportNone &&
0077         m_Move != cmdSupportNone &&
0078         m_Backup != cmdSupportNone &&
0079         m_GetUUID != cmdSupportNone;
0080 }
0081 
0082 FileSystem::SupportTool fat12::supportToolName() const
0083 {
0084     // also, dd for updating the UUID, but let's assume it's there ;-)
0085     return SupportTool(QStringLiteral("dosfstools"), QUrl(QStringLiteral("http://www.daniel-baumann.ch/software/dosfstools/")));
0086 }
0087 
0088 
0089 qint64 fat12::minCapacity() const
0090 {
0091     return 1 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB);
0092 }
0093 
0094 qint64 fat12::maxCapacity() const
0095 {
0096     return 255 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB);
0097 }
0098 
0099 int fat12::maxLabelLength() const
0100 {
0101     return 11;
0102 }
0103 
0104 QValidator* fat12::labelValidator(QObject *parent) const
0105 {
0106     QRegularExpressionValidator *m_LabelValidator = new QRegularExpressionValidator(parent);
0107     m_LabelValidator->setRegularExpression(QRegularExpression(QStringLiteral(R"(^[^\x{0000}-\x{001F}\x{007F}-\x{FFFF}*?.,;:\/\\|+=<>\[\]"]*$)")));
0108     return m_LabelValidator;
0109 }
0110 
0111 qint64 fat12::readUsedCapacity(const QString& deviceNode) const
0112 {
0113     ExternalCommand cmd(QStringLiteral("fsck.fat"), { QStringLiteral("-n"), QStringLiteral("-v"), deviceNode });
0114 
0115     // Exit code 1 is returned when FAT dirty bit is set
0116     if (cmd.run(-1) && (cmd.exitCode() == 0 || cmd.exitCode() == 1)) {
0117         qint64 usedClusters = -1;
0118         QRegularExpression re(QStringLiteral("files, (\\d+)/\\d+ "));
0119         QRegularExpressionMatch reClusters = re.match(cmd.output());
0120 
0121         if (reClusters.hasMatch())
0122             usedClusters = reClusters.captured(1).toLongLong();
0123 
0124         qint64 clusterSize = -1;
0125 
0126         re.setPattern(QStringLiteral("(\\d+) bytes per cluster"));
0127         QRegularExpressionMatch reClusterSize = re.match(cmd.output());
0128 
0129         if (reClusterSize.hasMatch())
0130             clusterSize = reClusterSize.captured(1).toLongLong();
0131 
0132         if (usedClusters > -1 && clusterSize > -1)
0133             return usedClusters * clusterSize;
0134     }
0135 
0136     return -1;
0137 }
0138 
0139 bool fat12::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel)
0140 {
0141     report.line() << xi18nc("@info:progress", "Setting label for partition <filename>%1</filename> to %2", deviceNode, newLabel);
0142 
0143     const QString label = newLabel.isEmpty() ? QStringLiteral("-r") : newLabel;
0144     ExternalCommand cmd(report, QStringLiteral("fatlabel"), { deviceNode, label });
0145     return cmd.run(-1) && cmd.exitCode() == 0;
0146 }
0147 
0148 bool fat12::check(Report& report, const QString& deviceNode) const
0149 {
0150     ExternalCommand cmd(report, QStringLiteral("fsck.fat"), { QStringLiteral("-a"), QStringLiteral("-w"), QStringLiteral("-v"), deviceNode });
0151     return cmd.run(-1) && cmd.exitCode() == 0;
0152 }
0153 
0154 bool fat12::create(Report& report, const QString& deviceNode)
0155 {
0156     return createWithFatSize(report, deviceNode, 12);
0157 }
0158 
0159 bool fat12::createWithFatSize(Report &report, const QString& deviceNode, int fatSize)
0160 {
0161     QStringList args = QStringList();
0162 
0163     if (fatSize != 12 && fatSize != 16 && fatSize != 32)
0164         return false;
0165 
0166     for (const auto& k : this->features().keys()) {
0167     const auto& v = this->features().value(k);
0168         if (k == QStringLiteral("sector-size")) {
0169             quint32 sectorSize = v.toInt();
0170 
0171             /* sectorSize has to be a power of 2 between 512 and 32768 */
0172             if (sectorSize >= 512 && sectorSize <= 32768 && sectorSize == qNextPowerOfTwo(sectorSize - 1))
0173                 args << QStringLiteral("-S%1").arg(sectorSize);
0174             else
0175                 qWarning() << QStringLiteral("FAT sector size %1 is invalid, using default").arg(sectorSize);
0176         } else if (k == QStringLiteral("sectors-per-cluster")) {
0177             quint32 sectorsPerCluster = v.toInt();
0178 
0179             /* sectorsPerCluster has to be a power of 2 between 2 and 128 */
0180             if (sectorsPerCluster <= 128 && sectorsPerCluster == qNextPowerOfTwo(sectorsPerCluster - 1))
0181                 args << QStringLiteral("-s%1").arg(sectorsPerCluster);
0182             else
0183                 qWarning() << QStringLiteral("FAT sector size %1 is invalid, using default").arg(sectorsPerCluster);
0184         }
0185     }
0186     args << QStringLiteral("-F%1").arg(fatSize) << QStringLiteral("-I") << QStringLiteral("-v") << deviceNode;
0187 
0188     ExternalCommand cmd(report, QStringLiteral("mkfs.fat"), args);
0189     return cmd.run(-1) && cmd.exitCode() == 0;
0190 }
0191 
0192 bool fat12::updateUUID(Report& report, const QString& deviceNode) const
0193 {
0194     long int t = time(nullptr);
0195 
0196     char uuid[4];
0197     for (auto &u : uuid) {
0198         u = static_cast<char>(t & 0xff);
0199         t >>= 8;
0200     }
0201 
0202     ExternalCommand cmd;
0203     return cmd.writeData(report, QByteArray(uuid, sizeof(uuid)), deviceNode, 39);
0204 }
0205 }