diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index 823c7a6..0456bb6 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -1,402 +1,402 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/lvmdevice.h" #include "fs/filesystem.h" #include "fs/lvm2_pv.h" #include "fs/luks.h" #include "fs/filesystemfactory.h" #include "core/partition.h" #include "core/partitiontable.h" #include "util/externalcommand.h" #include "util/helpers.h" #include #include #include #include #include /** Constructs a representation of LVM device with functionning LV as Partition * * @param name Volume Group name */ LvmDevice::LvmDevice(const QString& name, const QString& iconname) : VolumeManagerDevice(name, (QStringLiteral("/dev/") + name), getPeSize(name), getTotalPE(name), iconname, Device::LVM_Device) , m_peSize(getPeSize(name)) , m_totalPE(getTotalPE(name)) , m_allocPE(getAllocatedPE(name)) , m_freePE(getFreePE(name)) , m_UUID(getUUID(name)) { initPartitions(); } void LvmDevice::initPartitions() { qint64 firstUsable = 0; qint64 lastusable = totalPE() - 1; PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastusable); foreach (Partition* p, scanPartitions(*this, pTable)) { pTable->append(p); } pTable->updateUnallocated(*this); setPartitionTable(pTable); } /** * @returns sorted Partition(LV) Array */ QList LvmDevice::scanPartitions(const LvmDevice& dev, PartitionTable* pTable) const { QList pList; foreach (QString lvPath, lvPathList()) { pList.append(scanPartition(lvPath, dev, pTable)); } return pList; } /** * @returns sorted Partition(LV) Array */ Partition* LvmDevice::scanPartition(const QString& lvpath, const LvmDevice& dev, PartitionTable* pTable) const { /* * NOTE: * LVM partition have 2 different start and end sector value * 1. representing the actual LV start from 0 -> size of LV - 1 * 2. representing abstract LV's sector inside a VG partitionTable * start from last sector + 1 of last Partitions -> size of LV - 1 * Reason for this is for the LV Partition to worrks nicely with other parts of the codebase * without too many special cases. */ qint64 lvSize = getTotalLE(lvpath); qint64 startSector = mappedSector(lvpath, 0); qint64 endSector = startSector + lvSize - 1; FileSystem::Type type = FileSystem::detectFileSystem(lvpath); FileSystem* fs = FileSystemFactory::create(type, 0, lvSize - 1); bool mounted = isMounted(lvpath); QString mountPoint = QString(); KMountPoint::List mountPointList = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPointList.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); PartitionRole::Roles r = PartitionRole::Lvm_Lv; if (type == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = dynamic_cast(fs); QString mapperNode = FS::luks::mapperName(lvpath); bool isCryptOpen = !mapperNode.isEmpty(); luksFs->setCryptOpen(isCryptOpen); luksFs->setLogicalSectorSize(dev.logicalSize()); if (isCryptOpen) { luksFs->loadInnerFileSystem(lvpath, mapperNode); mountPoint = mountPointList.findByDevice(mapperNode) ? mountPointList.findByDevice(mapperNode)->mountPoint() : QString(); mounted = isMounted(mapperNode); if (mounted) { const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (freeSpaceInfo.isValid() && mountPoint != QString()) - luksFs->setSectorsUsed(freeSpaceInfo.used() / dev.logicalSize() + luksFs->getPayloadOffset(lvpath)); + luksFs->setSectorsUsed((freeSpaceInfo.used() + luksFs->getPayloadOffset(lvpath)) / dev.logicalSize()); } } else { mounted = false; } luksFs->setMounted(mounted); } else { mountPoint = mountPointList.findByDevice(lvpath) ? mountPointList.findByDevice(lvpath)->mountPoint() : QString(); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); //TODO: test used space report. probably incorrect if (mounted && freeSpaceInfo.isValid() && mountPoint != QString()) { fs->setSectorsUsed(freeSpaceInfo.used() / logicalSize()); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) { fs->setSectorsUsed(fs->readUsedCapacity(lvpath) / logicalSize()); } } if (fs->supportGetLabel() != FileSystem::cmdSupportNone) { fs->setLabel(fs->readLabel(lvpath)); } Partition* part = new Partition(pTable, dev, PartitionRole(r), fs, startSector, endSector, lvpath, PartitionTable::Flag::FlagLvm, mountPoint, mounted); return part; } qint64 LvmDevice::mappedSector(const QString& lvpath, qint64 sector) const { qint64 mSector = 0; QList lvpathList = lvPathList(); qint32 devIndex = lvpathList.indexOf(lvpath); if (devIndex) { for (int i = 0; i < devIndex; i++) { //TODO: currently going over the same LV again and again is wasteful. Could use some more optimization mSector += getTotalLE(lvpathList[i]); } mSector += sector; } return mSector; } QStringList LvmDevice::getPVs(const QString& vgname) { QStringList devPathList; QString cmdOutput = getField(QStringLiteral("pv_name"), vgname); if (cmdOutput.size()) { QStringList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach(QString devPath, tempPathList) { devPathList.append(devPath.trimmed()); } } return devPathList; } QList LvmDevice::deviceNodeList() const { return getPVs(name()); } QStringList LvmDevice::getLVs(const QString& vgname) { QStringList lvPathList; QString cmdOutput = getField(QStringLiteral("lv_path"), vgname); if (cmdOutput.size()) { QStringList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach(QString lvPath, tempPathList) { lvPathList.append(lvPath.trimmed()); } } return lvPathList; } QList LvmDevice::lvPathList() const { return getLVs(name()); } qint64 LvmDevice::getPeSize(const QString& vgname) { QString val = getField(QStringLiteral("vg_extent_size"), vgname); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getTotalPE(const QString& vgname) { QString val = getField(QStringLiteral("vg_extent_count"), vgname); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getAllocatedPE(const QString& vgname) { return getTotalPE(vgname) - getFreePE(vgname); } qint64 LvmDevice::getFreePE(const QString& vgname) { QString val = getField(QStringLiteral("vg_free_count"), vgname); return val.isEmpty() ? -1 : val.toInt(); } QString LvmDevice::getUUID(const QString& vgname) { QString val = getField(QStringLiteral("vg_uuid"), vgname); return val.isEmpty() ? QStringLiteral("---") : val; } /** Get LVM vgs command output with field name * * @param fieldName lvm field name * @param vgname * @returns raw output of command output, usully with manay spaces within the returned string * */ QString LvmDevice::getField(const QString& fieldName, const QString& vgname) { QStringList args = { QStringLiteral("vgs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName }; if (!vgname.isEmpty()) { args << vgname; } ExternalCommand cmd(QStringLiteral("lvm"), args); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } qint64 LvmDevice::getTotalLE(const QString& lvpath) { ExternalCommand cmd(QStringLiteral("lvm"), { QStringLiteral("lvdisplay"), lvpath}); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Current LE\\h+(\\d+)")); QRegularExpressionMatch match = re.match(cmd.output()); if (match.hasMatch()) { return match.captured(1).toInt(); } } return -1; } bool LvmDevice::removeLV(Report& report, LvmDevice& dev, Partition& part) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvremove"), QStringLiteral("--yes"), part.partitionPath()}); if (cmd.run(-1) && cmd.exitCode() == 0) { //TODO: remove Partition from PartitionTable and delete from memory ?? dev.partitionTable()->remove(&part); return true; } return false; } bool LvmDevice::createLV(Report& report, LvmDevice& dev, Partition& part, const QString& lvname) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(part.length()), QStringLiteral("--name"), lvname, dev.name()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::resizeLV(Report& report, LvmDevice& dev, Partition& part) { Q_UNUSED(dev); //TODO: through tests and add warning that it could currupt the user data. ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvresize"), QStringLiteral("--force"), // this command could corrupt user data QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(part.length()), part.partitionPath()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removePV(Report& report, LvmDevice& dev, const QString& pvPath) { //TODO: through tests ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgreduce"), //QStringLiteral("--yes"), // potentially corrupt user data dev.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::insertPV(Report& report, LvmDevice& dev, const QString& pvPath) { //TODO: through tests ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgextend"), //QStringLiteral("--yes"), // potentially corrupt user data dev.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::movePV(Report& report, LvmDevice& dev, const QString& pvPath, const QStringList& destinations) { Q_UNUSED(dev); if (FS::lvm2_pv::getAllocatedPE(pvPath) <= 0) { return true; } QStringList args = QStringList(); args << QStringLiteral("pvmove"); args << pvPath; if (!destinations.isEmpty()) { foreach (QString destPath, destinations) { args << destPath.trimmed(); } } ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createVG(Report& report, const QString vgname, const QStringList pvlist, const qint32 peSize) { //TODO: check that all the pv in pvlist is lvm2_pv QStringList args = QStringList(); args << QStringLiteral("vgcreate") << QStringLiteral("--physicalextentsize") << QString::number(peSize); args << vgname; foreach (QString pvnode, pvlist) { args << pvnode.trimmed(); } ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removeVG(Report& report, LvmDevice& dev) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgremove"), dev.name() }); return (cmd.run(-1) && cmd.exitCode() == 0); } diff --git a/src/core/partition.cpp b/src/core/partition.cpp index b45204d..0bfa22e 100644 --- a/src/core/partition.cpp +++ b/src/core/partition.cpp @@ -1,387 +1,387 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/partition.h" #include "core/device.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/externalcommand.h" #include "util/report.h" #include #include #include #include #include #include /** Creates a new Partition object. @param parent the Partition's parent. May be another Partition (for logicals) or a PartitionTable. Must not be nullptr. @param device the Device this Partition is on. @param role the Partition's role(s) @param fs pointer to the Partition's FileSystem object. The Partition object will take ownership of this. @param sectorStart the first sector of the Partition on its Device @param sectorEnd the last sector of the Partition on its Device @param partitionPath the Partition's path, e.g. /dev/sda4 or /dev/mmcblk0p1 @param availableFlags the flags available for this Partition @param mountPoint mount point for this Partition @param mounted true if the Partition is mounted @param activeFlags active flags for this Partition @param state the Partition's state */ 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) : PartitionNode(), m_Children(), m_Parent(parent), m_FileSystem(fs), m_Roles(role), m_FirstSector(sectorStart), m_LastSector(sectorEnd), m_DevicePath(device.deviceNode()), m_MountPoint(mountPoint), m_AvailableFlags(availableFlags), m_ActiveFlags(activeFlags), m_IsMounted(mounted), m_State(state) { setPartitionPath(partitionPath); Q_ASSERT(m_Parent); m_SectorSize = device.logicalSize(); } /** Destroys a Partition, destroying its children and its FileSystem */ Partition::~Partition() { // FIXME: Design flaw: Currently, there are two ways a partition node can get children: Either // they're created and inserted as unallocated in PartitionTable (these unallocated then get // "converted" to real, new partitions in the GUI) or they're created and appended in the // backend plugin. There is however no defined way to remove partitions from parents. This might // either cause leaks (a partition is removed from the parent's list of children but never // deleted) or, worse, crashes (a partition is deleted but not removed from the parent's // list of children). As a workaround, always remove a partition from its parent here in the dtor. // This presumably fixes 232092, but backporting is too risky until we're sure this doesn't cause // side-effects. if (m_Parent) parent()->remove(this); clearChildren(); deleteFileSystem(); } /** @param other Partition to copy */ Partition::Partition(const Partition& other) : PartitionNode(), m_Children(), m_Parent(other.m_Parent), m_FileSystem(FileSystemFactory::create(other.fileSystem())), m_Roles(other.m_Roles), m_FirstSector(other.m_FirstSector), m_LastSector(other.m_LastSector), m_DevicePath(other.m_DevicePath), m_MountPoint(other.m_MountPoint), m_AvailableFlags(other.m_AvailableFlags), m_ActiveFlags(other.m_ActiveFlags), m_IsMounted(other.m_IsMounted), m_SectorSize(other.m_SectorSize), m_State(other.m_State) { setPartitionPath(other.m_PartitionPath); foreach(const Partition * child, other.children()) { Partition* p = new Partition(*child); p->setParent(this); m_Children.append(p); } } /** @param other Partition to assign from */ Partition& Partition::operator=(const Partition& other) { if (&other == this) return *this; clearChildren(); foreach(const Partition * child, other.children()) { Partition* p = new Partition(*child); p->setParent(this); m_Children.append(p); } m_Number = other.m_Number; m_FileSystem = FileSystemFactory::create(other.fileSystem()); m_Roles = other.m_Roles; m_FirstSector = other.m_FirstSector; m_LastSector = other.m_LastSector; m_DevicePath = other.m_DevicePath; m_PartitionPath = other.m_PartitionPath; m_MountPoint = other.m_MountPoint; m_AvailableFlags = other.m_AvailableFlags; m_ActiveFlags = other.m_ActiveFlags; m_IsMounted = other.m_IsMounted; m_SectorSize = other.m_SectorSize; m_State = other.m_State; return *this; } bool Partition::operator==(const Partition& other) const { return other.deviceNode() == deviceNode(); } bool Partition::operator!=(const Partition& other) const { return !(other == *this); } /** @return a short descriptive text or, in case the Partition has StateNone, its device node. */ QString Partition::deviceNode() const { if (roles().has(PartitionRole::None) || roles().has(PartitionRole::Unallocated)) return i18nc("@item partition name", "unallocated"); if (state() == StateNew) return i18nc("@item partition name", "New Partition"); if (state() == StateRestore) return i18nc("@item partition name", "Restored Partition"); if (state() == StateCopy) return i18nc("@item partition name", "Copy of %1", partitionPath()); return partitionPath(); } /** @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 */ qint64 Partition::sectorsUsed() const { if (!roles().has(PartitionRole::Extended)) return fileSystem().sectorsUsed(); qint64 result = 0; foreach(const Partition * p, children()) if (!p->roles().has(PartitionRole::Unallocated)) result += p->length(); return result; } /** @return the minimum number of sectors this Partition must be long */ qint64 Partition::minimumSectors() const { if (roles().has(PartitionRole::Luks)) - return fileSystem().minCapacity() / sectorSize() + 4096; // 4096 is the default cryptsetup payload offset + return ( fileSystem().minCapacity() + (4096 * 512) ) / sectorSize(); // 4096 is the default cryptsetup payload offset return fileSystem().minCapacity() / sectorSize(); } /** @return the maximum number of sectors this Partition may be long */ qint64 Partition::maximumSectors() const { return fileSystem().maxCapacity() / sectorSize(); } /** Adjusts the numbers of logical Partitions for an extended Partition. This is required if a logical Partition is deleted or inserted because logicals must be numberd from 5 onwards without a gap. So if the user deletes Partition number 7 and there is a number 8, 8 becomes the "new" 7. And since this happens somewhere in the middle of a DeleteOperation, we have to adjust to that so the next Job still finds the Partition it wants to deal with. @param deletedNumber the number of a deleted logical or -1 if none has been deleted @param insertedNumber the number of an inserted logical or -1 if none has been inserted */ void Partition::adjustLogicalNumbers(qint32 deletedNumber, qint32 insertedNumber) { if (!roles().has(PartitionRole::Extended)) return; foreach(Partition * p, children()) { QString path = p->partitionPath(); path.remove(QRegularExpression(QStringLiteral("(\\d+$)"))); if (deletedNumber > 4 && p->number() > deletedNumber) p->setPartitionPath(path + QString::number(p->number() - 1)); else if (insertedNumber > 4 && p->number() >= insertedNumber) p->setPartitionPath(path + QString::number(p->number() + 1)); } } /** @return the highest sector number an extended Partition can begin at */ qint64 Partition::maxFirstSector() const { qint64 rval = -1; foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated) && (child->firstSector() < rval || rval == -1)) rval = child->firstSector(); return rval; } /** @return the lowest sector number an extended Partition can end at */ qint64 Partition::minLastSector() const { qint64 rval = -1; foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated) && child->lastSector() > rval) rval = child->lastSector(); return rval; } /** @return true if the Partition has children */ bool Partition::hasChildren() const { foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated)) return true; return false; } /** Sets an extended Partition to mounted if any of its children are mounted */ void Partition::checkChildrenMounted() { setMounted(isChildMounted()); } /** @return true if this Partition can be mounted */ bool Partition::canMount() const { // cannot mount if already mounted if (isMounted()) { return false; } if (fileSystem().canMount(deviceNode(), mountPoint())) { return true; } return false; } /** @return true if this Partition can be unmounted */ bool Partition::canUnmount() const { return !roles().has(PartitionRole::Extended) && isMounted(); } /** Tries to mount a Partition. @return true on success */ bool Partition::mount(Report& report) { if (isMounted()) return false; bool success = false; if (fileSystem().canMount(deviceNode(), mountPoint())) { success = fileSystem().mount(report, deviceNode(), mountPoint()); } setMounted(success); return success; } /** Tries to unmount a Partition. @return true on success */ bool Partition::unmount(Report& report) { if (!isMounted()) return false; bool success = true; while (success) { if (fileSystem().canUnmount(deviceNode())) { success = fileSystem().unmount(report, deviceNode()); } KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); if (!mountPoints.findByDevice(deviceNode())) break; } setMounted(!success); return success; } void Partition::deleteFileSystem() { delete m_FileSystem; m_FileSystem = nullptr; } void Partition::setPartitionPath(const QString& s) { m_PartitionPath = s; QRegularExpression re(QStringLiteral("(\\d+$)")); QRegularExpressionMatch rePartitionNumber = re.match(partitionPath()); if (rePartitionNumber.hasMatch()) { setNumber(rePartitionNumber.captured().toInt()); return; } setNumber(-1); } void Partition::setFileSystem(FileSystem* fs) { m_FileSystem = fs; } void Partition::move(qint64 newStartSector) { const qint64 savedLength = length(); setFirstSector(newStartSector); setLastSector(newStartSector + savedLength - 1); } QTextStream& operator<<(QTextStream& stream, const Partition& p) { QStringList flagList; foreach(const PartitionTable::Flag & f, PartitionTable::flagList()) { if (p.activeFlags() & f) flagList.append(PartitionTable::flagName(f)); } const QString sep(QStringLiteral(";")); // number - start - end - type - roles - label - flags stream << p.number() << sep << p.firstSector() << sep << p.lastSector() << sep << p.fileSystem().name() << sep << p.roles().toString() << sep << "\"" << p.fileSystem().label() << QStringLiteral("\"") << sep << "\"" << flagList.join(QStringLiteral(",")) << QStringLiteral("\"") << "\n"; return stream; } diff --git a/src/fs/luks.cpp b/src/fs/luks.cpp index 0e1e8d3..8e16a96 100644 --- a/src/fs/luks.cpp +++ b/src/fs/luks.cpp @@ -1,636 +1,632 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2013 by Andrius Štikonas * * Copyright (C) 2015-2016 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "fs/luks.h" #include "fs/filesystemfactory.h" #include "util/capacity.h" #include "util/externalcommand.h" #include "util/report.h" #include #include #include #include #include #include #include #include #include namespace FS { FileSystem::CommandSupportType luks::m_GetUsed = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_GetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Create = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Grow = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Shrink = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Move = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Check = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_Backup = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_SetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType luks::m_GetUUID = FileSystem::cmdSupportNone; luks::luks(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Luks) , m_innerFs(nullptr) , m_isCryptOpen(false) , m_isMounted(false) { } luks::~luks() { delete m_innerFs; } void luks::init() { CommandSupportType cryptsetupFound = findExternal(QStringLiteral("cryptsetup")) ? cmdSupportFileSystem : cmdSupportNone; m_Create = cryptsetupFound; m_UpdateUUID = cryptsetupFound; m_GetUUID = cryptsetupFound; m_Grow = cryptsetupFound; m_Shrink = cryptsetupFound; m_SetLabel = cmdSupportNone; m_GetLabel = cmdSupportFileSystem; m_Check = cmdSupportCore; m_Copy = cmdSupportCore; m_Move = cmdSupportCore; m_Backup = cmdSupportCore; m_GetUsed = cmdSupportNone; // libparted does not support LUKS, we do this as a special case } bool luks::supportToolFound() const { m_cryptsetupFound = findExternal(QStringLiteral("cryptsetup")) ? cmdSupportFileSystem : cmdSupportNone; return m_cryptsetupFound && ((m_isCryptOpen && m_innerFs) ? m_innerFs->supportToolFound() : true); } FileSystem::SupportTool luks::supportToolName() const { if (m_isCryptOpen && m_innerFs && m_cryptsetupFound) return m_innerFs->supportToolName(); return SupportTool(QStringLiteral("cryptsetup"), QUrl(QStringLiteral("https://code.google.com/p/cryptsetup/"))); } bool luks::create(Report& report, const QString& deviceNode) const { Q_ASSERT(m_innerFs); Q_ASSERT(!m_passphrase.isEmpty()); ExternalCommand createCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("-s"), QStringLiteral("512"), QStringLiteral("--batch-mode"), QStringLiteral("luksFormat"), deviceNode }); if (!( createCmd.start(-1) && createCmd.write(m_passphrase.toUtf8() + '\n') == m_passphrase.toUtf8().length() + 1 && createCmd.waitFor() && createCmd.exitCode() == 0)) { return false; } ExternalCommand openCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("open"), deviceNode, suggestedMapperName(deviceNode) }); if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toUtf8() + '\n') == m_passphrase.toUtf8().length() + 1 && openCmd.waitFor())) return false; QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; if (!m_innerFs->create(report, mapperNode)) return false; m_isCryptOpen = (m_innerFs != nullptr); if (m_isCryptOpen) return true; return false; } QString luks::mountTitle() const { return i18nc("@title:menu", "Mount"); } QString luks::unmountTitle() const { return i18nc("@title:menu", "Unmount"); } QString luks::cryptOpenTitle() const { return i18nc("@title:menu", "Decrypt"); } QString luks::cryptCloseTitle() const { return i18nc("@title:menu", "Deactivate"); } void luks::setPassphrase(const QString& passphrase) { m_passphrase = passphrase; } QString luks::passphrase() const { return m_passphrase; } bool luks::canMount(const QString& deviceNode, const QString& mountPoint) const { return m_isCryptOpen && !m_isMounted && m_innerFs && m_innerFs->canMount(mapperName(deviceNode), mountPoint); } bool luks::canUnmount(const QString& deviceNode) const { return m_isCryptOpen && m_isMounted && m_innerFs && m_innerFs->canUnmount(mapperName(deviceNode)); } bool luks::isMounted() const { return m_isCryptOpen && m_isMounted; } void luks::setMounted(bool mounted) { m_isMounted = mounted; } bool luks::canCryptOpen(const QString&) const { return !m_isCryptOpen && !m_isMounted && supportToolFound(); } bool luks::canCryptClose(const QString&) const { return m_isCryptOpen && !m_isMounted && m_cryptsetupFound; } bool luks::isCryptOpen() const { return m_isCryptOpen; } void luks::setCryptOpen(bool cryptOpen) { m_isCryptOpen = cryptOpen; } bool luks::cryptOpen(QWidget* parent, const QString& deviceNode) { if (m_isCryptOpen) { if (!mapperName(deviceNode).isEmpty()) { qWarning() << "LUKS device" << deviceNode << "already decrypted." << "Cannot decrypt again."; return false; } else { qWarning() << "LUKS device" << deviceNode << "reportedly decrypted but mapper node not found." << "Marking device as NOT decrypted and trying to " "decrypt again anyway."; m_isCryptOpen = false; } } KPasswordDialog dlg( parent ); dlg.setPrompt(i18n("Enter passphrase for %1:", deviceNode)); if( !dlg.exec() ) return false; QString passphrase = dlg.password(); ExternalCommand openCmd(QStringLiteral("cryptsetup"), { QStringLiteral("open"), deviceNode, suggestedMapperName(deviceNode) }); if (!( openCmd.start(-1) && openCmd.write(passphrase.toUtf8() + '\n') == passphrase.toUtf8().length() + 1 && openCmd.waitFor() && openCmd.exitCode() == 0) ) { return false; } if (m_innerFs) { delete m_innerFs; m_innerFs = nullptr; } QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; loadInnerFileSystem(deviceNode, mapperNode); m_isCryptOpen = (m_innerFs != nullptr); if (m_isCryptOpen) { m_passphrase = passphrase; return true; } return false; } bool luks::cryptClose(const QString& deviceNode) { if (!m_isCryptOpen) { qWarning() << "Cannot close LUKS device" << deviceNode << "because it's not open."; return false; } if (m_isMounted) { qWarning() << "Cannot close LUKS device" << deviceNode << "because the filesystem is mounted."; return false; } ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("close"), mapperName(deviceNode) }); if (!(cmd.run(-1) && cmd.exitCode() == 0)) return false; delete m_innerFs; m_innerFs = nullptr; m_passphrase.clear(); setLabel({}); setUUID(readUUID(deviceNode)); setSectorsUsed(-1); m_isCryptOpen = (m_innerFs != nullptr); if (!m_isCryptOpen) return true; return false; } void luks::loadInnerFileSystem(const QString& deviceNode, const QString& mapperNode) { Q_ASSERT(!m_innerFs); FileSystem::Type innerFsType = detectFileSystem(mapperNode); m_innerFs = FileSystemFactory::cloneWithNewType(innerFsType, *this); setLabel(m_innerFs->readLabel(mapperNode)); setUUID(m_innerFs->readUUID(mapperNode)); if (m_innerFs->supportGetUsed() == FileSystem::cmdSupportFileSystem) - setSectorsUsed(m_innerFs->readUsedCapacity(mapperNode)/m_logicalSectorSize + getPayloadOffset(deviceNode)); + setSectorsUsed((m_innerFs->readUsedCapacity(mapperNode) + getPayloadOffset(deviceNode)) / m_logicalSectorSize ); } void luks::createInnerFileSystem(FileSystem::Type type) { Q_ASSERT(!m_innerFs); m_innerFs = FileSystemFactory::cloneWithNewType(type, *this); } bool luks::check(Report& report, const QString& deviceNode) const { Q_ASSERT(m_innerFs); QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; return m_innerFs->check(report, mapperNode); } qint64 luks::readUsedCapacity(const QString& deviceNode) const { if (!m_isCryptOpen) return -1; if (m_innerFs) return m_innerFs->readUsedCapacity(deviceNode); return -1; } bool luks::mount(Report& report, const QString& deviceNode, const QString& mountPoint) { if (!m_isCryptOpen) { qWarning() << "Cannot mount device" << deviceNode << "before decrypting it first."; return false; } if (m_isMounted) { qWarning() << "Cannot mount device" << deviceNode << "because it's already mounted."; return false; } Q_ASSERT(m_innerFs); QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; if (m_innerFs->canMount(mapperNode, mountPoint)) { if (m_innerFs->mount(report, mapperNode, mountPoint)) { m_isMounted = true; const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (freeSpaceInfo.isValid() && mountPoint != QString()) - setSectorsUsed(freeSpaceInfo.used() / m_logicalSectorSize + getPayloadOffset(deviceNode)); + setSectorsUsed((freeSpaceInfo.used() + getPayloadOffset(deviceNode)) / m_logicalSectorSize); return true; } } else { ExternalCommand mountCmd( report, QStringLiteral("mount"), { QStringLiteral("--verbose"), mapperNode, mountPoint }); if (mountCmd.run() && mountCmd.exitCode() == 0) { m_isMounted = true; return true; } } return false; } bool luks::unmount(Report& report, const QString& deviceNode) { if (!m_isCryptOpen) { qWarning() << "Cannot unmount device" << deviceNode << "before decrypting it first."; return false; } if (!m_isMounted) { qWarning() << "Cannot unmount device" << deviceNode << "because it's not mounted."; return false; } Q_ASSERT(m_innerFs); QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; if (m_innerFs->canUnmount(mapperNode)) { if (m_innerFs->unmount(report, mapperNode)) { m_isMounted = false; return true; } } else { ExternalCommand unmountCmd( report, QStringLiteral("umount"), { QStringLiteral("--verbose"), QStringLiteral("--all-targets"), mapperNode }); if (unmountCmd.run() && unmountCmd.exitCode() == 0) { m_isMounted = false; return true; } } return false; } FileSystem::Type luks::type() const { if (m_isCryptOpen && m_innerFs) return m_innerFs->type(); return FileSystem::Luks; } QString luks::suggestedMapperName(const QString& deviceNode) const { return QStringLiteral("luks-") + readOuterUUID(deviceNode); } QString luks::readLabel(const QString& deviceNode) const { if (m_isCryptOpen && m_innerFs) return m_innerFs->readLabel(mapperName(deviceNode)); return QString(); } bool luks::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) { Q_ASSERT(m_innerFs); return m_innerFs->writeLabel(report, mapperName(deviceNode), newLabel); } bool luks::resize(Report& report, const QString& deviceNode, qint64 newLength) const { Q_ASSERT(m_innerFs); QString mapperNode = mapperName(deviceNode); if (mapperNode.isEmpty()) return false; - qint64 payloadLength = newLength - getPayloadOffset(deviceNode) * m_logicalSectorSize; + qint64 payloadLength = newLength - getPayloadOffset(deviceNode); if ( newLength - length() * m_logicalSectorSize > 0 ) { ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperNode }); report.line() << xi18nc("@info/plain", "Resizing LUKS crypt on partition %1.", deviceNode); if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0) { - return m_innerFs->resize(report, mapperNode, newLength - getPayloadOffset(deviceNode) * m_logicalSectorSize); + return m_innerFs->resize(report, mapperNode, payloadLength); } } else if (m_innerFs->resize(report, mapperNode, payloadLength)) { ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("--size"), QString::number(payloadLength / m_logicalSectorSize), QStringLiteral("resize"), mapperNode }); report.line() << xi18nc("@info/plain", "Resizing LUKS crypt on partition %1.", deviceNode); if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0) { return true; } } report.line() << xi18nc("@info/plain", "Resizing encrypted file system on partition %1 failed.", deviceNode); return false; } QString luks::readUUID(const QString& deviceNode) const { if (m_isCryptOpen && m_innerFs) return m_innerFs->readUUID(mapperName(deviceNode)); return readOuterUUID(deviceNode); } QString luks::readOuterUUID(const QString &deviceNode) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksUUID"), deviceNode }); if (cmd.run()) { return cmd.output().trimmed(); } return QStringLiteral("---"); } bool luks::updateUUID(Report& report, const QString& deviceNode) const { const QString uuid = QUuid::createUuid().toString().remove(QRegularExpression(QStringLiteral("\\{|\\}"))); ExternalCommand cmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("luksUUID"), deviceNode, QStringLiteral("--uuid"), uuid }); return cmd.run(-1) && cmd.exitCode() == 0; } QString luks::mapperName(const QString& deviceNode) { ExternalCommand cmd(QStringLiteral("lsblk"), { QStringLiteral("--list"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("name"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QStringList output=cmd.output().split(QStringLiteral("\n")); output.removeFirst(); if (!output.first().isEmpty()) return QStringLiteral("/dev/mapper/") + output.first(); } return QString(); } QString luks::getCipherName(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Cipher name:\\s+(\\w+)")); QRegularExpressionMatch reCipherName = re.match(cmd.output()); if (reCipherName.hasMatch()) return reCipherName.captured(1); } return QStringLiteral("---"); } QString luks::getCipherMode(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Cipher mode:\\s+(\\w+)")); QRegularExpressionMatch reCipherMode = re.match(cmd.output()); if (reCipherMode.hasMatch()) return reCipherMode.captured(1); } return QStringLiteral("---"); } QString luks::getHashName(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Hash spec:\\s+(\\w+)")); QRegularExpressionMatch reHash = re.match(cmd.output()); if (reHash.hasMatch()) return reHash.captured(1); } return QStringLiteral("---"); } qint64 luks::getKeySize(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("MK bits:\\s+(\\d+)")); QRegularExpressionMatch reKeySize = re.match(cmd.output()); if (reKeySize.hasMatch()) return reKeySize.captured(1).toLongLong(); } return -1; } +/* + * @return size of payload offset in bytes. + */ qint64 luks::getPayloadOffset(const QString& deviceNode) const { - ExternalCommand cmd(QStringLiteral("cryptsetup"), - { QStringLiteral("luksDump"), deviceNode }); - if (cmd.run(-1) && cmd.exitCode() == 0) { - QRegularExpression re(QStringLiteral("Payload offset:\\s+(\\d+)")); - QRegularExpressionMatch rePayloadOffset = re.match(cmd.output()); - if (rePayloadOffset.hasMatch()) - return rePayloadOffset.captured(1).toLongLong(); - } - return -1; + //4096 sectors and 512 bytes. + return 4096 * 512; } bool luks::canEncryptType(FileSystem::Type type) { switch (type) { case Ext2: case Ext3: case Ext4: case LinuxSwap: case ReiserFS: case Reiser4: case Xfs: case Jfs: case Btrfs: case Zfs: case Lvm2_PV: return true; default: return false; } } } diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 5c67371..c5410b7 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -1,597 +1,597 @@ /************************************************************************* * Copyright (C) 2008-2012 by Volker Lanz * * Copyright (C) 2015-2016 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "plugins/libparted/libpartedbackend.h" #include "plugins/libparted/libparteddevice.h" #include "core/diskdevice.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "fs/fat16.h" #include "fs/hfs.h" #include "fs/hfsplus.h" #include "fs/luks.h" #include "fs/lvm2_pv.h" #include "util/globallog.h" #include "util/helpers.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) static struct { PedPartitionFlag pedFlag; PartitionTable::Flag flag; } flagmap[] = { { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, { PED_PARTITION_RAID, PartitionTable::FlagRaid }, { PED_PARTITION_LVM, PartitionTable::FlagLvm }, { PED_PARTITION_LBA, PartitionTable::FlagLba }, { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, { PED_PARTITION_PALO, PartitionTable::FlagPalo }, { PED_PARTITION_PREP, PartitionTable::FlagPrep }, { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved }, { PED_PARTITION_BIOS_GRUB, PartitionTable::FlagBiosGrub }, { PED_PARTITION_APPLE_TV_RECOVERY, PartitionTable::FlagAppleTvRecovery }, { PED_PARTITION_DIAG, PartitionTable::FlagDiag }, // generic diagnostics flag { PED_PARTITION_LEGACY_BOOT, PartitionTable::FlagLegacyBoot }, { PED_PARTITION_MSFT_DATA, PartitionTable::FlagMsftData }, { PED_PARTITION_IRST, PartitionTable::FlagIrst }, // Intel Rapid Start partition { PED_PARTITION_ESP, PartitionTable::FlagEsp } // EFI system }; static QString s_lastPartedExceptionMessage; /** Callback to handle exceptions from libparted @param e the libparted exception to handle */ static PedExceptionOption pedExceptionHandler(PedException* e) { Log(Log::error) << i18nc("@info/plain", "LibParted Exception: %1", QString::fromLocal8Bit(e->message)); s_lastPartedExceptionMessage = QString::fromLocal8Bit(e->message); return PED_EXCEPTION_UNHANDLED; } // -------------------------------------------------------------------------- // The following structs and the typedef come from libparted's internal gpt sources. // It's very unfortunate there is no public API to get at the first and last usable // sector for GPT a partition table, so this is the only (libparted) way to get that // information (another way would be to read the GPT header and parse the // information ourselves; if the libparted devs begin changing these internal // structs for each point release and break our code, we'll have to do just that). typedef struct { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; } /* __attribute__ ((packed)) */ efi_guid_t; struct __attribute__((packed)) _GPTDiskData { PedGeometry data_area; int entry_count; efi_guid_t uuid; }; typedef struct _GPTDiskData GPTDiskData; // -------------------------------------------------------------------------- /** Get the first sector a Partition may cover on a given Device @param d the Device in question @return the first sector usable by a Partition */ static quint64 firstUsableSector(const DiskDevice& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = pedDisk->dev->bios_geom.sectors; if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = geom->start; else rval += 32; } return rval; } /** Get the last sector a Partition may cover on a given Device @param d the Device in question @return the last sector usable by a Partition */ static quint64 lastUsableSector(const DiskDevice& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = static_cast< quint64 >( pedDisk->dev->bios_geom.sectors ) * pedDisk->dev->bios_geom.heads * pedDisk->dev->bios_geom.cylinders - 1; if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = geom->end; else rval -= 32; } return rval; } /** Reads sectors used on a FileSystem using libparted functions. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @return the number of sectors used */ #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p) { Q_ASSERT(pedDisk); qint64 rval = -1; PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector()); if (pedPartition) { PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom); if (pedFileSystem) { if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) { rval = pedConstraint->min_size; ped_constraint_destroy(pedConstraint); } ped_file_system_close(pedFileSystem); } } return rval; } #endif /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ static void readSectorsUsed(PedDisk* pedDisk, const DiskDevice& d, Partition& p, const QString& mountPoint) { Q_ASSERT(pedDisk); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (p.isMounted() && freeSpaceInfo.isValid() && mountPoint != QString()) p.fileSystem().setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize()); else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSectorSize()); #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportCore) p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p)); #else Q_UNUSED(pedDisk); #endif } static PartitionTable::Flags activeFlags(PedPartition* p) { PartitionTable::Flags flags = PartitionTable::FlagNone; // We might get here with a pedPartition just picked up from libparted that is // unallocated. Libparted doesn't like it if we ask for flags for unallocated // space. if (p->num <= 0) return flags; for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].pedFlag) && ped_partition_get_flag(p, flagmap[i].pedFlag)) flags |= flagmap[i].flag; return flags; } static PartitionTable::Flags availableFlags(PedPartition* p) { PartitionTable::Flags flags; // see above. if (p->num <= 0) return flags; for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].pedFlag)) { // Workaround: libparted claims the hidden flag is available for extended partitions, but // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. if (p->type != PED_PARTITION_EXTENDED || flagmap[i].flag != PartitionTable::FlagHidden) flags |= flagmap[i].flag; } return flags; } /** Constructs a LibParted object. */ LibPartedBackend::LibPartedBackend(QObject*, const QList&) : CoreBackend() { ped_exception_set_handler(pedExceptionHandler); } void LibPartedBackend::initFSSupport() { #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT if (FS::fat16::m_Shrink == FileSystem::cmdSupportNone) FS::fat16::m_Shrink = FileSystem::cmdSupportBackend; if (FS::fat16::m_Grow == FileSystem::cmdSupportNone) FS::fat16::m_Grow = FileSystem::cmdSupportBackend; if (FS::hfs::m_Shrink == FileSystem::cmdSupportNone) FS::hfs::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_Shrink == FileSystem::cmdSupportNone) FS::hfsplus::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfs::m_GetUsed == FileSystem::cmdSupportNone) FS::hfs::m_GetUsed = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_GetUsed == FileSystem::cmdSupportNone) FS::hfsplus::m_GetUsed = FileSystem::cmdSupportBackend; #endif } /** Scans a Device for Partitions. This method will scan a Device for all Partitions on it, detect the FileSystem for each Partition, try to determine the FileSystem usage, read the FileSystem label and store it all in newly created objects that are in the end added to the Device's PartitionTable. @param d Device @param pedDisk libparted pointer to the partition table */ void LibPartedBackend::scanDevicePartitions(DiskDevice& d, PedDisk* pedDisk) { Q_ASSERT(pedDisk); Q_ASSERT(d.partitionTable()); PedPartition* pedPartition = nullptr; KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPoints.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); QList partitions; while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { if (pedPartition->num < 1) continue; PartitionRole::Roles r = PartitionRole::None; char* pedPath = ped_partition_get_path(pedPartition); FileSystem::Type type = FileSystem::Unknown; if (pedPath) type = detectFileSystem(QString::fromUtf8(pedPath)); free(pedPath); switch (pedPartition->type) { case PED_PARTITION_NORMAL: r = PartitionRole::Primary; break; case PED_PARTITION_EXTENDED: r = PartitionRole::Extended; type = FileSystem::Extended; break; case PED_PARTITION_LOGICAL: r = PartitionRole::Logical; break; default: continue; } // Find an extended partition this partition is in. PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) parent = d.partitionTable(); const QString node = QString::fromUtf8(ped_partition_get_path(pedPartition)); FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end); // libparted does not handle LUKS partitions QString mountPoint; bool mounted = false; if (type == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = dynamic_cast(fs); QString mapperNode = FS::luks::mapperName(node); bool isCryptOpen = !mapperNode.isEmpty(); luksFs->setCryptOpen(isCryptOpen); luksFs->setLogicalSectorSize(d.logicalSectorSize()); if (isCryptOpen) { luksFs->loadInnerFileSystem(node, mapperNode); if (luksFs->type() == FileSystem::Lvm2_PV) { mountPoint = FS::lvm2_pv::getVGName(mapperNode); mounted = false; } else { mountPoint = mountPoints.findByDevice(mapperNode) ? mountPoints.findByDevice(mapperNode)->mountPoint() : QString(); // We cannot use libparted to check the mounted status because // we don't have a PedPartition for the mapper device, so we use lsblk mounted = isMounted(mapperNode); } if (mounted) { const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (freeSpaceInfo.isValid() && mountPoint != QString()) - luksFs->setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize() + luksFs->getPayloadOffset(node)); + luksFs->setSectorsUsed((freeSpaceInfo.used() + luksFs->getPayloadOffset(node)) / d.logicalSectorSize()); } } else { mounted = false; } luksFs->setMounted(mounted); } else if (type == FileSystem::Lvm2_PV) { //TODO: adding PartitionRole mountPoint = FS::lvm2_pv::getVGName(node); mounted = false; } else { mountPoint = mountPoints.findByDevice(node) ? mountPoints.findByDevice(node)->mountPoint() : QString(); mounted = ped_partition_is_busy(pedPartition); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, node, availableFlags(pedPartition), mountPoint, mounted, activeFlags(pedPartition)); if (!part->roles().has(PartitionRole::Luks)) readSectorsUsed(pedDisk, d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(part->deviceNode())); parent->append(part); partitions.append(part); } d.partitionTable()->updateUnallocated(d); if (d.partitionTable()->isSectorBased(d)) d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); foreach(const Partition * part, partitions) PartitionAlignment::isAligned(d, *part); ped_disk_destroy(pedDisk); } /** Create a Device for the given device_node and scan it for partitions. @param device_node the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ Device* LibPartedBackend::scanDevice(const QString& device_node) { PedDevice* pedDevice = ped_device_get(device_node.toLocal8Bit().constData()); if (pedDevice == nullptr) { Log(Log::warning) << xi18nc("@info/plain", "Could not access device %1", device_node); return nullptr; } Log(Log::information) << i18nc("@info/plain", "Device found: %1", QString::fromUtf8(pedDevice->model)); DiskDevice* d = new DiskDevice(QString::fromUtf8(pedDevice->model), QString::fromUtf8(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); PedDisk* pedDisk = ped_disk_new(pedDevice); if (pedDisk) { const PartitionTable::TableType type = PartitionTable::nameToTableType(QString::fromUtf8(pedDisk->type->name)); CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector(*d), lastUsableSector(*d))); CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), ped_disk_get_max_primary_partition_count(pedDisk)); scanDevicePartitions(*d, pedDisk); } return d; } QList LibPartedBackend::scanDevices(bool excludeReadOnly) { QList result; ped_device_probe_all(); PedDevice* pedDevice = nullptr; QVector path; quint32 totalDevices = 0; while (true) { pedDevice = ped_device_get_next(pedDevice); if (!pedDevice) break; if (pedDevice->type == PED_DEVICE_DM) continue; if (excludeReadOnly && ( pedDevice->type == PED_DEVICE_LOOP || pedDevice->read_only)) continue; path.push_back(QString::fromUtf8(pedDevice->path)); ++totalDevices; } for (quint32 i = 0; i < totalDevices; ++i) { emitScanProgress(path[i], i * 100 / totalDevices); DiskDevice* d = dynamic_cast(scanDevice(path[i])); if (d) result.append(d); } return result; } /** Detects the type of a FileSystem given a PedDevice and a PedPartition @param partitionPath path to the partition @return the detected FileSystem type (FileSystem::Unknown if not detected) */ FileSystem::Type LibPartedBackend::detectFileSystem(const QString& partitionPath) { FileSystem::Type rval = FileSystem::Unknown; blkid_cache cache; if (blkid_get_cache(&cache, nullptr) == 0) { blkid_dev dev; if ((dev = blkid_get_dev(cache, partitionPath.toLocal8Bit().constData(), BLKID_DEV_NORMAL)) != nullptr) { QString s = QString::fromUtf8(blkid_get_tag_value(cache, "TYPE", partitionPath.toLocal8Bit().constData())); if (s == QStringLiteral("ext2")) rval = FileSystem::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; else if (s == QStringLiteral("ntfs")) rval = FileSystem::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; else if (s == QStringLiteral("vfat")) { // libblkid uses SEC_TYPE to distinguish between FAT16 and FAT32 QString st = QString::fromUtf8(blkid_get_tag_value(cache, "SEC_TYPE", partitionPath.toLocal8Bit().constData())); if (st == QStringLiteral("msdos")) rval = FileSystem::Fat16; else rval = FileSystem::Fat32; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) rval = FileSystem::Luks; else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; else qWarning() << "blkid: unknown file system type " << s << " on " << partitionPath; } blkid_put_cache(cache); } return rval; } CoreBackendDevice* LibPartedBackend::openDevice(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool LibPartedBackend::closeDevice(CoreBackendDevice* core_device) { return core_device->close(); } PedPartitionFlag LibPartedBackend::getPedFlag(PartitionTable::Flag flag) { for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (flagmap[i].flag == flag) return flagmap[i].pedFlag; return static_cast(-1); } QString LibPartedBackend::lastPartedExceptionMessage() { return s_lastPartedExceptionMessage; } #include "libpartedbackend.moc"