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