File indexing completed on 2024-05-05 05:48:47
0001 /* 0002 SPDX-FileCopyrightText: 2017 Pali Rohár <pali.rohar@gmail.com> 0003 SPDX-FileCopyrightText: 2017-2018 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com> 0005 SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com> 0006 0007 SPDX-License-Identifier: GPL-3.0-or-later 0008 */ 0009 0010 #include "fs/udf.h" 0011 0012 #include "util/externalcommand.h" 0013 #include "util/capacity.h" 0014 #include "util/report.h" 0015 0016 #include <KLocalizedString> 0017 0018 #include <QRegularExpression> 0019 #include <QRegularExpressionValidator> 0020 #include <QString> 0021 #include <QStringList> 0022 0023 namespace FS 0024 { 0025 constexpr qint64 MIN_UDF_BLOCKS = 300; 0026 constexpr qint64 MAX_UDF_BLOCKS = ((1ULL << 32) - 1); 0027 0028 FileSystem::CommandSupportType udf::m_GetUsed = FileSystem::cmdSupportNone; 0029 FileSystem::CommandSupportType udf::m_SetLabel = FileSystem::cmdSupportNone; 0030 FileSystem::CommandSupportType udf::m_UpdateUUID = FileSystem::cmdSupportNone; 0031 FileSystem::CommandSupportType udf::m_Create = FileSystem::cmdSupportNone; 0032 bool udf::oldMkudffsVersion = false; 0033 0034 udf::udf(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features) : 0035 FileSystem(firstsector, lastsector, sectorsused, label, features, FileSystem::Type::Udf) 0036 { 0037 } 0038 0039 void udf::init() 0040 { 0041 m_GetUsed = findExternal(QStringLiteral("udfinfo"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; 0042 m_SetLabel = m_UpdateUUID = findExternal(QStringLiteral("udflabel"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; 0043 m_Create = findExternal(QStringLiteral("mkudffs"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; 0044 0045 if (m_Create == cmdSupportFileSystem) { 0046 // Detect old mkudffs prior to version 1.1 by lack of --label option 0047 ExternalCommand cmd(QStringLiteral("mkudffs"), { QStringLiteral("--help") }); 0048 oldMkudffsVersion = cmd.run(-1) && !cmd.output().contains(QStringLiteral("--label")); 0049 } 0050 } 0051 0052 bool udf::supportToolFound() const 0053 { 0054 return 0055 m_GetUsed != cmdSupportNone && 0056 m_SetLabel != cmdSupportNone && 0057 m_UpdateUUID != cmdSupportNone && 0058 m_Create != cmdSupportNone; 0059 } 0060 0061 FileSystem::SupportTool udf::supportToolName() const 0062 { 0063 return SupportTool(QStringLiteral("udftools"), QUrl(QStringLiteral("https://github.com/pali/udftools"))); 0064 } 0065 0066 qint64 udf::minCapacity() const 0067 { 0068 return MIN_UDF_BLOCKS * sectorSize(); 0069 } 0070 0071 qint64 udf::maxCapacity() const 0072 { 0073 return MAX_UDF_BLOCKS * sectorSize(); 0074 } 0075 0076 int udf::maxLabelLength() const 0077 { 0078 return 126; 0079 } 0080 0081 QValidator* udf::labelValidator(QObject *parent) const 0082 { 0083 QRegularExpressionValidator *m_LabelValidator = new QRegularExpressionValidator(parent); 0084 if (oldMkudffsVersion) { 0085 // Mkudffs from udftools prior to version 1.1 damages the label if it 0086 // contains non-ASCII characters. Therefore do not allow a label with 0087 // such characters with old versions of mkudffs. 0088 m_LabelValidator->setRegularExpression(QRegularExpression(QStringLiteral("[\\x{0001}-\\x{007F}]{0,126}"))); 0089 } else { 0090 // UDF label can only contain 126 bytes, either 126 ISO-8859-1 0091 // (Latin 1) characters or 63 UCS-2BE characters. 0092 m_LabelValidator->setRegularExpression(QRegularExpression(QStringLiteral("[\\x{0001}-\\x{00FF}]{0,126}|[\\x{0001}-\\x{FFFF}]{0,63}"))); 0093 } 0094 return m_LabelValidator; 0095 } 0096 0097 qint64 udf::readUsedCapacity(const QString& deviceNode) const 0098 { 0099 ExternalCommand cmd(QStringLiteral("udfinfo"), { QStringLiteral("--utf8"), deviceNode }); 0100 if (!cmd.run(-1) || cmd.exitCode() != 0) 0101 return -1; 0102 0103 QRegularExpressionMatch reBlockSize = QRegularExpression(QStringLiteral("^blocksize=([0-9]+)$"), QRegularExpression::MultilineOption).match(cmd.output()); 0104 QRegularExpressionMatch reUsedBlocks = QRegularExpression(QStringLiteral("^usedblocks=([0-9]+)$"), QRegularExpression::MultilineOption).match(cmd.output()); 0105 0106 if (!reBlockSize.hasMatch() || !reUsedBlocks.hasMatch()) 0107 return -1; 0108 0109 qint64 blockSize = reBlockSize.captured(1).toLongLong(); 0110 qint64 usedBlocks = reUsedBlocks.captured(1).toLongLong(); 0111 0112 return usedBlocks * blockSize; 0113 } 0114 0115 bool udf::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) 0116 { 0117 ExternalCommand cmd(report, QStringLiteral("udflabel"), { QStringLiteral("--utf8"), deviceNode, newLabel }); 0118 return cmd.run(-1) && cmd.exitCode() == 0; 0119 } 0120 0121 bool udf::updateUUID(Report& report, const QString& deviceNode) const 0122 { 0123 ExternalCommand cmd(report, QStringLiteral("udflabel"), { QStringLiteral("--utf8"), QStringLiteral("--uuid=random"), deviceNode }); 0124 return cmd.run(-1) && cmd.exitCode() == 0; 0125 } 0126 0127 bool udf::create(Report& report, const QString& deviceNode) 0128 { 0129 return createWithLabel(report, deviceNode, QString()); 0130 } 0131 0132 bool udf::createWithLabel(Report& report, const QString& deviceNode, const QString& label) 0133 { 0134 // It is not possible to create UDF filesystem without a label or with empty label 0135 // When --lvid or --vid option is not specified, mkudffs use sane default 0136 QStringList labelArgs; 0137 if (!label.isEmpty()) { 0138 // The Volume Identifier (--vid) can only contain 30 bytes, either 30 0139 // ISO-8859-1 (Latin 1) characters or 15 UCS-2BE characters. Store the 0140 // most characters possible in the Volume Identifier. Either up to 15 0141 // UCS-2BE characters when a character needing 16-bit encoding is found in 0142 // the first 15 characters, or up to 30 characters when a character 0143 // needing 16-bit encoding is found in the second 15 characters. 0144 const QRegularExpression nonLatin1RegExp = QRegularExpression(QStringLiteral("[^\\x{0000}-\\x{00FF}]")); 0145 QString shortLabel = label.left(30); 0146 int firstNonLatin1Pos = shortLabel.indexOf(nonLatin1RegExp); 0147 if (firstNonLatin1Pos != -1 && firstNonLatin1Pos < 15) 0148 shortLabel = shortLabel.left(15); 0149 else if (firstNonLatin1Pos != -1 && firstNonLatin1Pos < 30) 0150 shortLabel = shortLabel.left(firstNonLatin1Pos); 0151 0152 // UDF Logical Volume Identifier (--lvid) represents the label, but blkid 0153 // (from util-linux) prior to version v2.26 reads the Volume Identifier 0154 // (--vid). Therefore for compatibility reasons store the label in both 0155 // locations. 0156 labelArgs << QStringLiteral("--lvid=") + label; 0157 labelArgs << QStringLiteral("--vid=") + shortLabel; 0158 } 0159 0160 QStringList cmdArgs; 0161 cmdArgs << QStringLiteral("--utf8"); 0162 // TODO: Add GUI option for choosing different optical disks and UDF revision 0163 // For now format as UDF revision 2.01 for hard disk media type 0164 cmdArgs << QStringLiteral("--media-type=hd"); 0165 cmdArgs << QStringLiteral("--udfrev=0x201"); 0166 // mkudffs from udftools prior to 1.1 is not able to detect logical (sector) size 0167 // and UDF block size must match logical sector size of underlying media 0168 cmdArgs << QStringLiteral("--blocksize=") + QString::number(sectorSize()); 0169 cmdArgs << labelArgs; 0170 cmdArgs << deviceNode; 0171 0172 ExternalCommand cmd(report, QStringLiteral("mkudffs"), cmdArgs); 0173 return cmd.run(-1) && cmd.exitCode() == 0; 0174 } 0175 0176 }