File indexing completed on 2024-05-19 05:49:06
0001 /* 0002 SPDX-FileCopyrightText: 2017-2019 Andrius Štikonas <andrius@stikonas.eu> 0003 SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com> 0004 SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com> 0005 0006 SPDX-License-Identifier: GPL-3.0-or-later 0007 */ 0008 0009 #include "plugins/sfdisk/sfdiskpartitiontable.h" 0010 #include "plugins/sfdisk/sfdiskgptattributes.h" 0011 0012 #include "backend/corebackend.h" 0013 #include "backend/corebackendmanager.h" 0014 0015 #include "core/partition.h" 0016 #include "core/device.h" 0017 #include "core/raid/softwareraid.h" 0018 0019 #include "fs/filesystem.h" 0020 0021 #include "util/report.h" 0022 #include "util/externalcommand.h" 0023 0024 #include <QJsonArray> 0025 #include <QJsonDocument> 0026 #include <QJsonObject> 0027 #include <QRegularExpression> 0028 0029 #include <KLocalizedString> 0030 0031 SfdiskPartitionTable::SfdiskPartitionTable(const Device* d) : 0032 CoreBackendPartitionTable(), 0033 m_device(d) 0034 { 0035 } 0036 0037 SfdiskPartitionTable::~SfdiskPartitionTable() 0038 { 0039 } 0040 0041 bool SfdiskPartitionTable::open() 0042 { 0043 return true; 0044 } 0045 0046 bool SfdiskPartitionTable::commit(quint32 timeout) 0047 { 0048 if (m_device->type() == Device::Type::SoftwareRAID_Device) 0049 ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--stop-exec-queue") }).run(); 0050 0051 ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); 0052 ExternalCommand(QStringLiteral("partx"), { QStringLiteral("--update"), m_device->deviceNode() }).run(); 0053 ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("trigger"), QStringLiteral("--subsystem-match=block") }).run(); 0054 0055 if (m_device->type() == Device::Type::SoftwareRAID_Device) 0056 ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--start-exec-queue") }).run(); 0057 0058 ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); 0059 return true; 0060 } 0061 0062 QString SfdiskPartitionTable::createPartition(Report& report, const Partition& partition) 0063 { 0064 if ( !(partition.roles().has(PartitionRole::Extended) || partition.roles().has(PartitionRole::Logical) || partition.roles().has(PartitionRole::Primary) ) ) { 0065 report.line() << xi18nc("@info:progress", "Unknown partition role for new partition <filename>%1</filename> (roles: %2)", partition.deviceNode(), partition.roles().toString()); 0066 return QString(); 0067 } 0068 0069 QByteArray type = QByteArray(); 0070 if (partition.roles().has(PartitionRole::Extended)) 0071 type = QByteArrayLiteral(" type=5"); 0072 0073 // NOTE: at least on GPT partition types "are" partition flags 0074 ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } ); 0075 if ( createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + 0076 type + 0077 QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.start(-1) ) { 0078 QRegularExpression re(QStringLiteral("Created a new partition (\\d+)")); 0079 QRegularExpressionMatch rem = re.match(createCommand.output()); 0080 0081 if (rem.hasMatch()) { 0082 if ( partition.devicePath().back().isDigit() ) 0083 return partition.devicePath() + QLatin1Char('p') + rem.captured(1); 0084 else 0085 return partition.devicePath() + rem.captured(1); 0086 } 0087 } 0088 0089 report.line() << xi18nc("@info:progress", "Failed to add partition <filename>%1</filename> to device <filename>%2</filename>.", partition.deviceNode(), m_device->deviceNode()); 0090 0091 return QString(); 0092 } 0093 0094 bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& partition) 0095 { 0096 ExternalCommand deleteCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--delete"), partition.devicePath(), QString::number(partition.number()) } ); 0097 if (deleteCommand.run(-1) && deleteCommand.exitCode() == 0) 0098 return true; 0099 0100 report.line() << xi18nc("@info:progress", "Could not delete partition <filename>%1</filename>.", partition.devicePath()); 0101 return false; 0102 } 0103 0104 bool SfdiskPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sectorStart, qint64 sectorEnd) 0105 { 0106 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), partition.devicePath(), QStringLiteral("-N"), QString::number(partition.number()) } ); 0107 if ( sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + 0108 QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) + 0109 QByteArrayLiteral("\nY\n")) 0110 && sfdiskCommand.start(-1) && sfdiskCommand.exitCode() == 0) { 0111 return true; 0112 } 0113 0114 report.line() << xi18nc("@info:progress", "Could not set geometry for partition <filename>%1</filename> while trying to resize/move it.", partition.devicePath()); 0115 return false; 0116 } 0117 0118 bool SfdiskPartitionTable::clobberFileSystem(Report& report, const Partition& partition) 0119 { 0120 ExternalCommand wipeCommand(report, QStringLiteral("wipefs"), { QStringLiteral("--all"), partition.partitionPath() } ); 0121 if (wipeCommand.run(-1) && wipeCommand.exitCode() == 0) 0122 return true; 0123 0124 report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition <filename>%1</filename>.", partition.partitionPath()); 0125 0126 return false; 0127 } 0128 0129 bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) 0130 { 0131 // sfdisk does not have any partition resize capabilities 0132 Q_UNUSED(report) 0133 Q_UNUSED(partition) 0134 Q_UNUSED(newLength) 0135 0136 return false; 0137 } 0138 0139 FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) 0140 { 0141 FileSystem::Type type = FileSystem::Type::Unknown; 0142 0143 ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } ); 0144 if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) { 0145 const QJsonArray partitionTable = QJsonDocument::fromJson(jsonCommand.rawOutput()).object()[QLatin1String("partitiontable")].toObject()[QLatin1String("partitions")].toArray(); 0146 for (const auto &partition : partitionTable) { 0147 const QJsonObject partitionObject = partition.toObject(); 0148 const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); 0149 if (start == sector) { 0150 const QString deviceNode = partitionObject[QLatin1String("node")].toString(); 0151 type = CoreBackendManager::self()->backend()->detectFileSystem(deviceNode); 0152 return type; 0153 } 0154 } 0155 } 0156 0157 report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device <filename>%2</filename>.", sector, device.deviceNode()); 0158 0159 return type; 0160 } 0161 0162 static struct { 0163 FileSystem::Type type; 0164 QLatin1String partitionType[2]; // GPT, MBR 0165 } typemap[] = { 0166 { FileSystem::Type::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0167 { FileSystem::Type::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0168 { FileSystem::Type::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0169 { FileSystem::Type::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0170 { FileSystem::Type::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, 0171 { FileSystem::Type::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, 0172 { FileSystem::Type::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, 0173 { FileSystem::Type::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("c") } }, 0174 { FileSystem::Type::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0175 { FileSystem::Type::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, 0176 { FileSystem::Type::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, 0177 { FileSystem::Type::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0178 { FileSystem::Type::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0179 { FileSystem::Type::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0180 { FileSystem::Type::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, 0181 { FileSystem::Type::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, 0182 { FileSystem::Type::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, 0183 { FileSystem::Type::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } 0184 // Add ZFS too 0185 }; 0186 0187 static QLatin1String getPartitionType(FileSystem::Type t, PartitionTable::TableType tableType) 0188 { 0189 quint8 type; 0190 switch (tableType) { 0191 case PartitionTable::TableType::gpt: 0192 type = 0; 0193 break; 0194 case PartitionTable::TableType::msdos: 0195 case PartitionTable::TableType::msdos_sectorbased: 0196 type = 1; 0197 break; 0198 default:; 0199 return QLatin1String(); 0200 } 0201 for (quint32 i = 0; i < sizeof(typemap) / sizeof(typemap[0]); i++) 0202 if (typemap[i].type == t) 0203 return typemap[i].partitionType[type]; 0204 0205 return QLatin1String(); 0206 } 0207 0208 bool SfdiskPartitionTable::setPartitionLabel(Report& report, const Partition& partition, const QString& label) 0209 { 0210 if (label.isEmpty()) 0211 return true; 0212 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-label"), m_device->deviceNode(), QString::number(partition.number()), 0213 label } ); 0214 return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; 0215 } 0216 0217 QString SfdiskPartitionTable::getPartitionUUID(Report& report, const Partition& partition) 0218 { 0219 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--list"), QStringLiteral("--output"), QStringLiteral("Device,UUID"), 0220 m_device->deviceNode() }); 0221 if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) { 0222 QRegularExpression re(m_device->deviceNode() + QString::number(partition.number()) + QStringLiteral(" +(.+)")); 0223 QRegularExpressionMatch rem = re.match(sfdiskCommand.output()); 0224 0225 if (rem.hasMatch()) 0226 return rem.captured(1); 0227 } 0228 0229 return QString(); 0230 } 0231 0232 bool SfdiskPartitionTable::setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) 0233 { 0234 if (uuid.isEmpty()) 0235 return true; 0236 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-uuid"), m_device->deviceNode(), QString::number(partition.number()), 0237 uuid } ); 0238 return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; 0239 } 0240 0241 bool SfdiskPartitionTable::setPartitionAttributes(Report& report, const Partition& partition, quint64 attrs) 0242 { 0243 QStringList attributes = SfdiskGptAttributes::toStringList(attrs); 0244 if (attributes.isEmpty()) 0245 return true; 0246 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-attrs"), m_device->deviceNode(), QString::number(partition.number()), 0247 attributes.join(QStringLiteral(",")) } ); 0248 return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; 0249 } 0250 0251 bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) 0252 { 0253 QString partitionType = partition.type(); 0254 if (partitionType.isEmpty()) 0255 partitionType = getPartitionType(partition.fileSystem().type(), m_device->partitionTable()->type()); 0256 if (partitionType.isEmpty()) 0257 return true; 0258 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), 0259 partitionType } ); 0260 return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; 0261 } 0262 0263 bool SfdiskPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) 0264 { 0265 if (m_device->partitionTable()->type() == PartitionTable::TableType::msdos || 0266 m_device->partitionTable()->type() == PartitionTable::TableType::msdos_sectorbased) { 0267 // We only allow setting one active partition per device 0268 if (flag == PartitionTable::Flag::Boot && state == true) { 0269 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QString::number(partition.number()) } ); 0270 if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) 0271 return true; 0272 else 0273 return false; 0274 } else if (flag == PartitionTable::Flag::Boot && state == false) { 0275 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); 0276 if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) 0277 return true; 0278 else 0279 return false; 0280 } 0281 } 0282 0283 if (flag == PartitionTable::Flag::Boot && state == true) { 0284 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), 0285 QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") } ); 0286 if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) 0287 return true; 0288 else 0289 return false; 0290 } 0291 if (flag == PartitionTable::Flag::Boot && state == false) 0292 setPartitionSystemType(report, partition); 0293 0294 if (flag == PartitionTable::Flag::BiosGrub && state == true) { 0295 ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), 0296 QStringLiteral("21686148-6449-6E6F-744E-656564454649") } ); 0297 if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) 0298 return true; 0299 else 0300 return false; 0301 } 0302 if (flag == PartitionTable::Flag::BiosGrub && state == false) 0303 setPartitionSystemType(report, partition); 0304 0305 return true; 0306 }