Warning, file /system/kpmcore/src/core/partition.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2008 Laurent Montel <montel@kde.org> 0004 SPDX-FileCopyrightText: 2013-2020 Andrius Štikonas <andrius@stikonas.eu> 0005 SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org> 0006 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 0011 #include "core/partition.h" 0012 0013 #include "core/device.h" 0014 0015 #include "fs/filesystem.h" 0016 #include "fs/filesystemfactory.h" 0017 #include "fs/luks.h" 0018 0019 #include "util/externalcommand.h" 0020 #include "util/report.h" 0021 0022 #include <QFile> 0023 #include <QRegularExpression> 0024 #include <QStorageInfo> 0025 #include <QString> 0026 #include <QStringList> 0027 #include <QTextStream> 0028 0029 #include <KLocalizedString> 0030 0031 /** Creates a new Partition object. 0032 @param parent the Partition's parent. May be another Partition (for logicals) or a PartitionTable. Must not be nullptr. 0033 @param device the Device this Partition is on. 0034 @param role the Partition's role(s) 0035 @param fs pointer to the Partition's FileSystem object. The Partition object will take ownership of this. 0036 @param sectorStart the first sector of the Partition on its Device 0037 @param sectorEnd the last sector of the Partition on its Device 0038 @param partitionPath the Partition's path, e.g. /dev/sda4 or /dev/mmcblk0p1 0039 @param availableFlags the flags available for this Partition 0040 @param mountPoint mount point for this Partition 0041 @param mounted true if the Partition is mounted 0042 @param activeFlags active flags for this Partition 0043 @param state the Partition's state 0044 */ 0045 Partition::Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags, const QString& mountPoint, bool mounted, PartitionTable::Flags activeFlags, State state) : 0046 PartitionNode(), 0047 m_Children(), 0048 m_Parent(parent), 0049 m_FileSystem(fs), 0050 m_Roles(role), 0051 m_FirstSector(sectorStart), 0052 m_LastSector(sectorEnd), 0053 m_DevicePath(device.deviceNode()), 0054 m_MountPoint(mountPoint), 0055 m_AvailableFlags(availableFlags), 0056 m_ActiveFlags(activeFlags), 0057 m_IsMounted(mounted), 0058 m_State(state) 0059 { 0060 setPartitionPath(partitionPath); 0061 Q_ASSERT(m_Parent); 0062 m_SectorSize = device.logicalSize(); 0063 } 0064 0065 /** Destroys a Partition, destroying its children and its FileSystem */ 0066 Partition::~Partition() 0067 { 0068 // FIXME: Design flaw: Currently, there are two ways a partition node can get children: Either 0069 // they're created and inserted as unallocated in PartitionTable (these unallocated then get 0070 // "converted" to real, new partitions in the GUI) or they're created and appended in the 0071 // backend plugin. There is however no defined way to remove partitions from parents. This might 0072 // either cause leaks (a partition is removed from the parent's list of children but never 0073 // deleted) or, worse, crashes (a partition is deleted but not removed from the parent's 0074 // list of children). As a workaround, always remove a partition from its parent here in the dtor. 0075 // This presumably fixes 232092. 0076 if (m_Parent) 0077 parent()->remove(this); 0078 clearChildren(); 0079 deleteFileSystem(); 0080 } 0081 0082 /** @param other Partition to copy 0083 */ 0084 Partition::Partition(const Partition& other, PartitionNode* parent) : 0085 PartitionNode(), 0086 m_Children(), 0087 m_Parent(other.m_Parent), 0088 m_FileSystem(FileSystemFactory::create(other.fileSystem())), 0089 m_Roles(other.m_Roles), 0090 m_FirstSector(other.m_FirstSector), 0091 m_LastSector(other.m_LastSector), 0092 m_DevicePath(other.m_DevicePath), 0093 m_Label(other.m_Label), 0094 m_UUID(other.m_UUID), 0095 m_MountPoint(other.m_MountPoint), 0096 m_AvailableFlags(other.m_AvailableFlags), 0097 m_ActiveFlags(other.m_ActiveFlags), 0098 m_IsMounted(other.m_IsMounted), 0099 m_SectorSize(other.m_SectorSize), 0100 m_State(other.m_State) 0101 { 0102 if ( parent ) 0103 m_Parent = parent; 0104 0105 setPartitionPath(other.m_PartitionPath); 0106 for (const auto &child : other.children()) { 0107 Partition* p = new Partition(*child, this); 0108 m_Children.append(p); 0109 } 0110 } 0111 0112 /** @param other Partition to assign from */ 0113 Partition& Partition::operator=(const Partition& other) 0114 { 0115 if (&other == this) 0116 return *this; 0117 0118 clearChildren(); 0119 0120 for (const auto &child : other.children()) { 0121 Partition* p = new Partition(*child); 0122 p->setParent(this); 0123 m_Children.append(p); 0124 } 0125 0126 m_Number = other.m_Number; 0127 m_FileSystem = FileSystemFactory::create(other.fileSystem()); 0128 m_Roles = other.m_Roles; 0129 m_FirstSector = other.m_FirstSector; 0130 m_LastSector = other.m_LastSector; 0131 m_DevicePath = other.m_DevicePath; 0132 m_Label = other.m_Label; 0133 m_UUID = other.m_UUID; 0134 m_PartitionPath = other.m_PartitionPath; 0135 m_MountPoint = other.m_MountPoint; 0136 m_AvailableFlags = other.m_AvailableFlags; 0137 m_ActiveFlags = other.m_ActiveFlags; 0138 m_IsMounted = other.m_IsMounted; 0139 m_SectorSize = other.m_SectorSize; 0140 m_State = other.m_State; 0141 0142 return *this; 0143 } 0144 0145 bool Partition::operator==(const Partition& other) const 0146 { 0147 return other.deviceNode() == deviceNode(); 0148 } 0149 0150 bool Partition::operator!=(const Partition& other) const 0151 { 0152 return !(other == *this); 0153 } 0154 0155 /** @return a short descriptive text or, in case the Partition has StateNone, its device node. */ 0156 QString Partition::deviceNode() const 0157 { 0158 if (roles().has(PartitionRole::None) || roles().has(PartitionRole::Unallocated)) 0159 return xi18nc("@item partition name", "unallocated"); 0160 0161 if (state() == State::New) 0162 return xi18nc("@item partition name", "New Partition"); 0163 0164 if (state() == State::Restore) 0165 return xi18nc("@item partition name", "Restored Partition"); 0166 0167 if (state() == State::Copy) 0168 return xi18nc("@item partition name", "Copy of %1", partitionPath()); 0169 0170 return partitionPath(); 0171 } 0172 0173 /** @return the sectors used in the Partition's FileSystem or, in case of an extended partition, the sum of used sectors of the Partition's children */ 0174 qint64 Partition::sectorsUsed() const 0175 { 0176 // Make sure file system exists. In some cases (due to bugs elsewhere?) file system pointer did not exist, especially for unallocated space. 0177 if (m_FileSystem == nullptr) 0178 return -1; 0179 0180 if (!roles().has(PartitionRole::Extended)) 0181 return fileSystem().sectorsUsed(); 0182 0183 qint64 result = 0; 0184 for (const auto &p : children()) 0185 if (!p->roles().has(PartitionRole::Unallocated)) 0186 result += p->length(); 0187 0188 return result; 0189 } 0190 0191 /** @return the minimum number of sectors this Partition must be long */ 0192 qint64 Partition::minimumSectors() const 0193 { 0194 if (roles().has(PartitionRole::Luks)) 0195 return ( fileSystem().minCapacity() + (4096 * 512) ) / sectorSize(); // 4096 is the default cryptsetup payload offset 0196 return fileSystem().minCapacity() / sectorSize(); 0197 } 0198 0199 /** @return the maximum number of sectors this Partition may be long */ 0200 qint64 Partition::maximumSectors() const 0201 { 0202 return fileSystem().maxCapacity() / sectorSize(); 0203 } 0204 0205 /** Adjusts the numbers of logical Partitions for an extended Partition. 0206 0207 This is required if a logical Partition is deleted or inserted because logicals must be numberd from 0208 5 onwards without a gap. So if the user deletes Partition number 7 and there is a number 8, 8 becomes the 0209 "new" 7. And since this happens somewhere in the middle of a DeleteOperation, we have to adjust to that so the 0210 next Job still finds the Partition it wants to deal with. 0211 0212 @param deletedNumber the number of a deleted logical or -1 if none has been deleted 0213 @param insertedNumber the number of an inserted logical or -1 if none has been inserted 0214 */ 0215 void Partition::adjustLogicalNumbers(qint32 deletedNumber, qint32 insertedNumber) const 0216 { 0217 if (!roles().has(PartitionRole::Extended)) 0218 return; 0219 0220 for (const auto &p : children()) { 0221 QString path = p->partitionPath(); 0222 path.remove(QRegularExpression(QStringLiteral("(\\d+$)"))); 0223 if (deletedNumber > 4 && p->number() > deletedNumber) 0224 p->setPartitionPath(path + QString::number(p->number() - 1)); 0225 else if (insertedNumber > 4 && p->number() >= insertedNumber) 0226 p->setPartitionPath(path + QString::number(p->number() + 1)); 0227 } 0228 } 0229 0230 /** @return the highest sector number an extended Partition can begin at */ 0231 qint64 Partition::maxFirstSector() const 0232 { 0233 qint64 rval = -1; 0234 0235 for (const auto &child : children()) 0236 if (!child->roles().has(PartitionRole::Unallocated) && (child->firstSector() < rval || rval == -1)) 0237 rval = child->firstSector(); 0238 0239 return rval; 0240 } 0241 0242 /** @return the lowest sector number an extended Partition can end at */ 0243 qint64 Partition::minLastSector() const 0244 { 0245 qint64 rval = -1; 0246 0247 for (const auto &child : children()) 0248 if (!child->roles().has(PartitionRole::Unallocated) && child->lastSector() > rval) 0249 rval = child->lastSector(); 0250 0251 return rval; 0252 } 0253 0254 /** @return true if the Partition has children */ 0255 bool Partition::hasChildren() const 0256 { 0257 for (const auto &child : children()) 0258 if (!child->roles().has(PartitionRole::Unallocated)) 0259 return true; 0260 0261 return false; 0262 } 0263 0264 /** @return returns the Partition Table which contains this Partition */ 0265 const PartitionTable* Partition::partitionTable() const { 0266 const PartitionNode *p = this; 0267 while (p->parent() != nullptr) { 0268 p = p->parent(); 0269 } 0270 return static_cast<const PartitionTable*>(p); 0271 } 0272 0273 /** Sets an extended Partition to mounted if any of its children are mounted */ 0274 void Partition::checkChildrenMounted() 0275 { 0276 setMounted(isChildMounted()); 0277 } 0278 0279 /** @return true if this Partition can be mounted */ 0280 bool Partition::canMount() const 0281 { 0282 // cannot mount if already mounted 0283 if (isMounted()) { 0284 return false; 0285 } 0286 0287 if (fileSystem().canMount(deviceNode(), mountPoint())) { 0288 return true; 0289 } 0290 0291 return false; 0292 } 0293 0294 /** @return true if this Partition can be unmounted */ 0295 bool Partition::canUnmount() const 0296 { 0297 return !roles().has(PartitionRole::Extended) && isMounted() && fileSystem().canUnmount(deviceNode()); 0298 } 0299 0300 void Partition::setMounted(bool b) { 0301 m_IsMounted = b; 0302 if (roles().has(PartitionRole::Luks)) 0303 static_cast<FS::luks*>(m_FileSystem)->setMounted(b); 0304 } 0305 0306 /** Tries to mount a Partition. 0307 @return true on success 0308 */ 0309 bool Partition::mount(Report& report) 0310 { 0311 if (isMounted()) 0312 return false; 0313 0314 bool success = false; 0315 0316 if (fileSystem().canMount(deviceNode(), mountPoint())) { 0317 success = fileSystem().mount(report, deviceNode(), mountPoint()); 0318 } 0319 0320 setMounted(success); 0321 0322 return success; 0323 } 0324 0325 /** Tries to unmount a Partition. 0326 @return true on success 0327 */ 0328 bool Partition::unmount(Report& report) 0329 { 0330 if (!isMounted()) 0331 return false; 0332 0333 bool success = false; 0334 0335 if (fileSystem().canUnmount(deviceNode())) { 0336 success = fileSystem().unmount(report, deviceNode()); 0337 } 0338 0339 const QString canonicalDeviceNode = QFileInfo(deviceNode()).canonicalFilePath(); 0340 const QList<QStorageInfo> mountedVolumes = QStorageInfo::mountedVolumes(); 0341 for (const QStorageInfo &storage : mountedVolumes) { 0342 if (QFileInfo(QFile::decodeName(storage.device())).canonicalFilePath() == canonicalDeviceNode ) { 0343 success = false; 0344 break; 0345 } 0346 } 0347 0348 setMounted(!success); 0349 0350 return success; 0351 } 0352 0353 void Partition::deleteFileSystem() 0354 { 0355 delete m_FileSystem; 0356 m_FileSystem = nullptr; 0357 } 0358 0359 void Partition::setPartitionPath(const QString& s) 0360 { 0361 m_PartitionPath = s; 0362 QRegularExpression re(QStringLiteral("(\\d+$)")); 0363 QRegularExpressionMatch rePartitionNumber = re.match(partitionPath()); 0364 if (rePartitionNumber.hasMatch()) { 0365 setNumber(rePartitionNumber.captured().toInt()); 0366 return; 0367 } 0368 setNumber(-1); 0369 } 0370 0371 void Partition::setFileSystem(FileSystem* fs) 0372 { 0373 m_FileSystem = fs; 0374 } 0375 0376 void Partition::move(qint64 newStartSector) 0377 { 0378 const qint64 savedLength = length(); 0379 setFirstSector(newStartSector); 0380 setLastSector(newStartSector + savedLength - 1); 0381 } 0382 0383 QTextStream& operator<<(QTextStream& stream, const Partition& p) 0384 { 0385 QStringList flagList; 0386 0387 for (const auto &f : PartitionTable::flagList()) { 0388 if (p.activeFlags() & f) 0389 flagList.append(PartitionTable::flagName(f)); 0390 } 0391 0392 const QString sep(QStringLiteral(";")); 0393 0394 // number - start - end - type - roles - label - flags 0395 stream << p.number() << sep 0396 << p.firstSector() << sep 0397 << p.lastSector() << sep 0398 << p.fileSystem().name({ QStringLiteral("C") }) << sep 0399 << p.roles().toString({ QStringLiteral("C") }) << sep 0400 << "\"" << p.fileSystem().label() << QStringLiteral("\"") << sep 0401 << "\"" << flagList.join(QStringLiteral(",")) << QStringLiteral("\"") 0402 << "\n"; 0403 0404 return stream; 0405 } 0406