File indexing completed on 2024-05-05 05:48:47
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2013-2018 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net> 0005 SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com> 0006 SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com> 0007 0008 SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 0011 #include "fs/ntfs.h" 0012 0013 #include "util/externalcommand.h" 0014 #include "util/capacity.h" 0015 #include "util/report.h" 0016 #include "util/globallog.h" 0017 0018 #include <KLocalizedString> 0019 0020 #include <QRegularExpression> 0021 #include <QString> 0022 #include <QStringList> 0023 #include <QFile> 0024 0025 #include <algorithm> 0026 #include <ctime> 0027 0028 namespace FS 0029 { 0030 FileSystem::CommandSupportType ntfs::m_GetUsed = FileSystem::cmdSupportNone; 0031 FileSystem::CommandSupportType ntfs::m_GetLabel = FileSystem::cmdSupportNone; 0032 FileSystem::CommandSupportType ntfs::m_Create = FileSystem::cmdSupportNone; 0033 FileSystem::CommandSupportType ntfs::m_Grow = FileSystem::cmdSupportNone; 0034 FileSystem::CommandSupportType ntfs::m_Shrink = FileSystem::cmdSupportNone; 0035 FileSystem::CommandSupportType ntfs::m_Move = FileSystem::cmdSupportNone; 0036 FileSystem::CommandSupportType ntfs::m_Check = FileSystem::cmdSupportNone; 0037 FileSystem::CommandSupportType ntfs::m_Copy = FileSystem::cmdSupportNone; 0038 FileSystem::CommandSupportType ntfs::m_Backup = FileSystem::cmdSupportNone; 0039 FileSystem::CommandSupportType ntfs::m_SetLabel = FileSystem::cmdSupportNone; 0040 FileSystem::CommandSupportType ntfs::m_UpdateUUID = FileSystem::cmdSupportNone; 0041 FileSystem::CommandSupportType ntfs::m_GetUUID = FileSystem::cmdSupportNone; 0042 0043 ntfs::ntfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features) : 0044 FileSystem(firstsector, lastsector, sectorsused, label, features, FileSystem::Type::Ntfs) 0045 { 0046 } 0047 0048 void ntfs::init() 0049 { 0050 m_Shrink = m_Grow = m_Check = findExternal(QStringLiteral("ntfsresize")) ? cmdSupportFileSystem : cmdSupportNone; 0051 m_GetUsed = findExternal(QStringLiteral("ntfsinfo")) ? cmdSupportFileSystem : cmdSupportNone; 0052 m_GetLabel = cmdSupportCore; 0053 m_SetLabel = findExternal(QStringLiteral("ntfslabel")) ? cmdSupportFileSystem : cmdSupportNone; 0054 m_Create = findExternal(QStringLiteral("mkfs.ntfs")) ? cmdSupportFileSystem : cmdSupportNone; 0055 m_Copy = findExternal(QStringLiteral("ntfsclone")) ? cmdSupportFileSystem : cmdSupportNone; 0056 m_Backup = cmdSupportCore; 0057 m_UpdateUUID = cmdSupportCore; 0058 m_Move = (m_Check != cmdSupportNone) ? cmdSupportCore : cmdSupportNone; 0059 m_GetUUID = cmdSupportCore; 0060 } 0061 0062 bool ntfs::supportToolFound() const 0063 { 0064 return 0065 m_GetUsed != cmdSupportNone && 0066 m_GetLabel != cmdSupportNone && 0067 m_SetLabel != cmdSupportNone && 0068 m_Create != cmdSupportNone && 0069 m_Check != cmdSupportNone && 0070 m_UpdateUUID != cmdSupportNone && 0071 m_Grow != cmdSupportNone && 0072 m_Shrink != cmdSupportNone && 0073 m_Copy != cmdSupportNone && 0074 m_Move != cmdSupportNone && 0075 m_Backup != cmdSupportNone && 0076 m_GetUUID != cmdSupportNone; 0077 } 0078 0079 FileSystem::SupportTool ntfs::supportToolName() const 0080 { 0081 return SupportTool(QStringLiteral("ntfs-3g"), QUrl(QStringLiteral("https://www.tuxera.com/community/open-source-ntfs-3g/"))); 0082 } 0083 0084 qint64 ntfs::minCapacity() const 0085 { 0086 return 2 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); 0087 } 0088 0089 qint64 ntfs::maxCapacity() const 0090 { 0091 return 256 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); 0092 } 0093 0094 int ntfs::maxLabelLength() const 0095 { 0096 return 128; 0097 } 0098 0099 qint64 ntfs::readUsedCapacity(const QString& deviceNode) const 0100 { 0101 ExternalCommand cmd(QStringLiteral("ntfsinfo"), { QStringLiteral("--mft"), QStringLiteral("--force"), deviceNode }); 0102 0103 if (cmd.run(-1) && cmd.exitCode() == 0) { 0104 QRegularExpression re(QStringLiteral("Cluster Size: (\\d+)")); 0105 QRegularExpressionMatch reClusterSize = re.match(cmd.output()); 0106 qint64 clusterSize = reClusterSize.hasMatch() ? reClusterSize.captured(1).toLongLong() : -1; 0107 0108 QRegularExpression re2(QStringLiteral("Free Clusters: (\\d+)")); 0109 QRegularExpressionMatch reFreeClusters = re2.match(cmd.output()); 0110 qint64 freeClusters = reFreeClusters.hasMatch() ? reFreeClusters.captured(1).toLongLong() : -1; 0111 0112 QRegularExpression re3(QStringLiteral("Volume Size in Clusters: (\\d+)")); 0113 QRegularExpressionMatch reVolumeSize = re3.match(cmd.output()); 0114 qint64 volumeSize = reVolumeSize.hasMatch() ? reVolumeSize.captured(1).toLongLong() : -1; 0115 0116 qint64 usedBytes = -1; 0117 qDebug () << volumeSize << freeClusters << clusterSize; 0118 if (clusterSize > -1 && freeClusters > -1 && volumeSize > -1) { 0119 usedBytes = (volumeSize - freeClusters) * clusterSize; 0120 } 0121 return usedBytes; 0122 } 0123 0124 return -1; 0125 } 0126 0127 bool ntfs::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) 0128 { 0129 ExternalCommand writeCmd(report, QStringLiteral("ntfslabel"), { QStringLiteral("--force"), deviceNode, newLabel }, QProcess::SeparateChannels); 0130 0131 if (!writeCmd.run(-1)) 0132 return false; 0133 0134 return true; 0135 } 0136 0137 bool ntfs::check(Report& report, const QString& deviceNode) const 0138 { 0139 ExternalCommand cmd(report, QStringLiteral("ntfsresize"), { QStringLiteral("--no-progress-bar"), QStringLiteral("--info"), QStringLiteral("--force"), QStringLiteral("--verbose"), deviceNode }); 0140 return cmd.run(-1) && cmd.exitCode() == 0; 0141 } 0142 0143 bool ntfs::create(Report& report, const QString& deviceNode) 0144 { 0145 ExternalCommand cmd(report, QStringLiteral("mkfs.ntfs"), { QStringLiteral("--quick"), QStringLiteral("--verbose"), deviceNode }); 0146 return cmd.run(-1) && cmd.exitCode() == 0; 0147 } 0148 0149 bool ntfs::copy(Report& report, const QString& targetDeviceNode, const QString& sourceDeviceNode) const 0150 { 0151 ExternalCommand cmd(report, QStringLiteral("ntfsclone"), { QStringLiteral("--force"), QStringLiteral("--overwrite"), targetDeviceNode, sourceDeviceNode }); 0152 0153 return cmd.run(-1) && cmd.exitCode() == 0; 0154 } 0155 0156 bool ntfs::resize(Report& report, const QString& deviceNode, qint64 length) const 0157 { 0158 QStringList args = { QStringLiteral("--no-progress-bar"), QStringLiteral("--force"), deviceNode, QStringLiteral("--size"), QString::number(length) }; 0159 0160 QStringList dryRunArgs = args; 0161 dryRunArgs << QStringLiteral("--no-action"); 0162 ExternalCommand cmdDryRun(QStringLiteral("ntfsresize"), dryRunArgs); 0163 0164 if (cmdDryRun.run(-1) && cmdDryRun.exitCode() == 0) { 0165 ExternalCommand cmd(report, QStringLiteral("ntfsresize"), args); 0166 return cmd.run(-1) && cmd.exitCode() == 0; 0167 } 0168 0169 return false; 0170 } 0171 0172 bool ntfs::updateUUID(Report& report, const QString& deviceNode) const 0173 { 0174 Q_UNUSED(report) 0175 ExternalCommand cmd(QStringLiteral("ntfslabel"), { QStringLiteral("--new-serial"), deviceNode }); 0176 0177 return cmd.run(-1) && cmd.exitCode() == 0; 0178 } 0179 0180 bool ntfs::updateBootSector(Report& report, const QString& deviceNode) const 0181 { 0182 report.line() << xi18nc("@info:progress", "Updating boot sector for NTFS file system on partition <filename>%1</filename>.", deviceNode); 0183 0184 qint64 n = firstSector(); 0185 char* s = reinterpret_cast<char*>(&n); 0186 0187 #if Q_BYTE_ORDER == Q_BIG_ENDIAN 0188 std::swap(s[0], s[3]); 0189 std::swap(s[1], s[2]); 0190 #endif 0191 0192 ExternalCommand cmd; 0193 if (!cmd.writeData(report, QByteArray(s, sizeof(s)), deviceNode, 28)) { 0194 Log() << xi18nc("@info:progress", "Could not write new start sector to partition <filename>%1</filename> when trying to update the NTFS boot sector.", deviceNode); 0195 return false; 0196 } 0197 0198 // Also update backup NTFS boot sector located at the end of the partition 0199 // NOTE: this should fail if filesystem does not span the whole partition 0200 qint64 pos = (lastSector() - firstSector()) * sectorSize() + 28; 0201 if (!cmd.writeData(report, QByteArray(s, sizeof(s)), deviceNode, pos)) { 0202 Log() << xi18nc("@info:progress", "Could not write new start sector to partition <filename>%1</filename> when trying to update the NTFS boot sector.", deviceNode); 0203 return false; 0204 } 0205 0206 Log() << xi18nc("@info:progress", "Updated NTFS boot sector for partition <filename>%1</filename> successfully.", deviceNode); 0207 0208 return true; 0209 } 0210 }