Add an experimental sfdisk backend.

This commit is contained in:
Andrius Štikonas 2017-09-17 15:33:25 +01:00
parent a6415561ae
commit 0d88e26c8c
13 changed files with 991 additions and 1 deletions

View File

@ -455,6 +455,7 @@ static struct {
{ QLatin1String("dasd"), 1, false, true, PartitionTable::dasd },
{ QLatin1String("msdos"), 4, true, false, PartitionTable::msdos },
{ QLatin1String("msdos"), 4, true, false, PartitionTable::msdos_sectorbased },
{ QLatin1String("dos"), 4, true, false, PartitionTable::msdos_sectorbased },
{ QLatin1String("dvh"), 16, true, true, PartitionTable::dvh },
{ QLatin1String("gpt"), 128, false, false, PartitionTable::gpt },
{ QLatin1String("loop"), 1, false, true, PartitionTable::loop },

View File

@ -29,3 +29,4 @@ if (PARTMAN_DUMMYBACKEND)
add_subdirectory(dummy)
endif (PARTMAN_DUMMYBACKEND)
add_subdirectory(sfdisk)

View File

@ -47,4 +47,3 @@ Name[uk]=Додаток Керування розділами KDE
Name[x-test]=xxKDE Partition Manager Pluginxx
Name[zh_CN]=KDE 分区管理器插件
Name[zh_TW]=KDE 磁碟分割區管理員外掛程式

View File

@ -0,0 +1,29 @@
# Copyright (C) 2010 by Volker Lanz <vl@fidra.de>
#
# 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 <http://www.gnu.org/licenses/>.
set (pmsfdiskbackendplugin_SRCS
sfdiskbackend.cpp
sfdiskdevice.cpp
sfdiskpartition.cpp
sfdiskpartitiontable.cpp
)
add_library(pmsfdiskbackendplugin SHARED ${pmsfdiskbackendplugin_SRCS})
target_link_libraries(pmsfdiskbackendplugin kpmcore)
install(TARGETS pmsfdiskbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR})
kcoreaddons_desktop_to_json(pmsfdiskbackendplugin pmsfdiskbackendplugin.desktop)
install(FILES pmsfdiskbackendplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR})

View File

@ -0,0 +1,17 @@
[Desktop Entry]
Encoding=UTF-8
Name=KDE Partition Manager sfdisk Backend
Comment=A KDE Partition Manager sfdisk backend.
Type=Service
ServiceTypes=PartitionManager/Plugin
Icon=preferences-plugin
X-KDE-Library=pmsfdiskbackendplugin
X-KDE-PluginInfo-Name=pmsfdiskbackendplugin
X-KDE-PluginInfo-Author=Andrius Štikonas
X-KDE-PluginInfo-Email=andrius@stikonas.eu
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-Category=BackendPlugin
X-KDE-PluginInfo-EnabledByDefault=true
X-KDE-PluginInfo-Version=1
X-KDE-PluginInfo-Website=http://www.partitionmanager.org

View File

@ -0,0 +1,395 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
/** @file
*/
#include "plugins/sfdisk/sfdiskbackend.h"
#include "plugins/sfdisk/sfdiskdevice.h"
#include "core/diskdevice.h"
#include "core/lvmdevice.h"
#include "core/partitiontable.h"
#include "core/partitionalignment.h"
#include "fs/filesystemfactory.h"
#include "fs/luks.h"
#include "util/globallog.h"
#include "util/externalcommand.h"
#include "util/helpers.h"
#include <QDataStream>
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStorageInfo>
#include <QString>
#include <QStringList>
#include <KLocalizedString>
#include <KPluginFactory>
K_PLUGIN_FACTORY_WITH_JSON(SfdiskBackendFactory, "pmsfdiskbackendplugin.json", registerPlugin<SfdiskBackend>();)
SfdiskBackend::SfdiskBackend(QObject*, const QList<QVariant>&) :
CoreBackend()
{
}
void SfdiskBackend::initFSSupport()
{
}
QList<Device*> SfdiskBackend::scanDevices(bool excludeReadOnly)
{
// TODO: add another bool option for loopDevices
QList<Device*> result;
QStringList deviceNodes;
ExternalCommand cmd(QStringLiteral("lsblk"),
{ QStringLiteral("--nodeps"),
QStringLiteral("--paths"),
QStringLiteral("--sort"), QStringLiteral("name"),
QStringLiteral("--json"),
QStringLiteral("--output"),
QStringLiteral("type,name") });
if (cmd.run(-1) && cmd.exitCode() == 0) {
const QJsonDocument jsonDocument = QJsonDocument::fromJson(cmd.output().toUtf8());
const QJsonObject jsonObject = jsonDocument.object();
const QJsonArray jsonArray = jsonObject[QLatin1String("blockdevices")].toArray();
for (const auto &deviceLine : jsonArray) {
QJsonObject deviceObject = deviceLine.toObject();
if (deviceObject[QLatin1String("type")].toString() != QLatin1String("disk"))
continue;
const QString deviceNode = deviceObject[QLatin1String("name")].toString();
if (excludeReadOnly) {
QString deviceName = deviceNode;
deviceName.remove(QStringLiteral("/dev/"));
QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName));
if (f.open(QIODevice::ReadOnly))
if (f.readLine().trimmed().toInt() == 1)
continue;
}
deviceNodes << deviceNode;
}
int totalDevices = deviceNodes.length();
for (int i = 0; i < totalDevices; ++i) {
const QString deviceNode = deviceNodes[i];
emitScanProgress(deviceNode, i * 100 / totalDevices);
Device* device = scanDevice(deviceNode);
if (device != nullptr) {
result.append(device);
}
}
LvmDevice::scanSystemLVM(result);
}
return result;
}
/** Create a Device for the given device_node and scan it for partitions.
@param deviceNode the device node (e.g. "/dev/sda")
@return the created Device object. callers need to free this.
*/
Device* SfdiskBackend::scanDevice(const QString& deviceNode)
{
ExternalCommand modelCommand(QStringLiteral("lsblk"),
{ QStringLiteral("--nodeps"),
QStringLiteral("--noheadings"),
QStringLiteral("--output"), QStringLiteral("model"),
deviceNode });
ExternalCommand sizeCommand(QStringLiteral("blockdev"), { QStringLiteral("--getsize64"), deviceNode });
ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode });
ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode } );
if ( modelCommand.run(-1) && modelCommand.exitCode() == 0
&& sizeCommand.run(-1) && sizeCommand.exitCode() == 0
&& sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0
&& jsonCommand.run(-1) )
{
QString modelName = modelCommand.output();
modelName = modelName.left(modelName.length() - 1);
qint64 deviceSize = sizeCommand.output().trimmed().toLongLong();
Log(Log::information) << xi18nc("@info:status", "Device found: %1", modelName);
int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong();
DiskDevice* d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize);
if (jsonCommand.exitCode() != 0)
return d;
const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.output().toUtf8()).object();
const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject();
QString tableType = partitionTable[QLatin1String("label")].toString();
const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType);
qint64 firstUsableSector = 0, lastUsableSector = d->totalSectors();
if (type == PartitionTable::gpt) {
firstUsableSector = partitionTable[QLatin1String("firstlba")].toVariant().toLongLong();
lastUsableSector = partitionTable[QLatin1String("lastlba")].toVariant().toLongLong();
}
if (lastUsableSector < firstUsableSector) {
return nullptr;
}
setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector));
switch (type) {
case PartitionTable::gpt:
{
// Read the maximum number of GPT partitions
qint32 maxEntries;
ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), QStringLiteral("if=") + deviceNode}, QProcess::SeparateChannels);
if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) {
QByteArray gptHeader = ddCommand.rawOutput();
QByteArray gptMaxEntries = gptHeader.mid(80, 4);
QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> maxEntries;
}
else
maxEntries = 128;
CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries);
}
default:
break;
}
scanDevicePartitions(*d, partitionTable[QLatin1String("partitions")].toArray());
return d;
}
return nullptr;
}
/** 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.
*/
void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions)
{
Q_ASSERT(d.partitionTable());
QList<Partition*> partitions;
for (const auto &partition : jsonPartitions) {
const QJsonObject partitionObject = partition.toObject();
const QString partitionNode = partitionObject[QLatin1String("node")].toString();
const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong();
const qint64 size = partitionObject[QLatin1String("size")].toVariant().toLongLong();
const QString partitionType = partitionObject[QLatin1String("type")].toString();
PartitionTable::Flag activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::FlagBoot : PartitionTable::FlagNone;
if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B"))
activeFlags = PartitionTable::FlagEsp;
else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649"))
activeFlags = PartitionTable::FlagBiosGrub;
FileSystem::Type type = FileSystem::Unknown;
type = detectFileSystem(partitionNode);
PartitionRole::Roles r = PartitionRole::Primary;
if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && partitionType.toInt() == 5 ) {
r = PartitionRole::Extended;
type = FileSystem::Extended;
}
// Find an extended partition this partition is in.
PartitionNode* parent = d.partitionTable()->findPartitionBySector(start, PartitionRole(PartitionRole::Extended));
// None found, so it's a primary in the device's partition table.
if (parent == nullptr)
parent = d.partitionTable();
else
r = PartitionRole::Logical;
FileSystem* fs = FileSystemFactory::create(type, start, start + size - 1, d.logicalSize());
fs->scan(partitionNode);
QString mountPoint;
bool mounted;
// sfdisk does not handle LUKS partitions
if (fs->type() == FileSystem::Luks) {
r |= PartitionRole::Luks;
FS::luks* luksFs = static_cast<FS::luks*>(fs);
luksFs->initLUKS();
QString mapperNode = luksFs->mapperName();
mountPoint = FileSystem::detectMountPoint(fs, mapperNode);
mounted = FileSystem::detectMountStatus(fs, mapperNode);
} else {
mountPoint = FileSystem::detectMountPoint(fs, partitionNode);
mounted = FileSystem::detectMountStatus(fs, partitionNode);
}
Partition* part = new Partition(parent, d, PartitionRole(r), fs, start, start + size - 1, partitionNode, availableFlags(d.partitionTable()->type()), mountPoint, mounted, activeFlags);
if (!part->roles().has(PartitionRole::Luks))
readSectorsUsed(d, *part, mountPoint);
if (fs->supportGetLabel() != FileSystem::cmdSupportNone)
fs->setLabel(fs->readLabel(part->deviceNode()));
if (d.partitionTable()->type() == PartitionTable::TableType::gpt) {
part->setLabel(partitionObject[QLatin1String("name")].toString());
part->setUUID(partitionObject[QLatin1String("uuid")].toString());
}
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);
for (const Partition * part : qAsConst(partitions))
PartitionAlignment::isAligned(d, *part);
}
/** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object.
@param p the Partition the FileSystem is on
@param mountPoint mount point of the partition in question
*/
void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint)
{
if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::LinuxSwap && p.fileSystem().type() != FileSystem::Lvm2_PV) {
const QStorageInfo storage = QStorageInfo(mountPoint);
if (p.isMounted() && storage.isValid())
p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize());
}
else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem)
p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize());
}
FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath)
{
FileSystem::Type rval = FileSystem::Unknown;
ExternalCommand lsblkCommand(QStringLiteral("lsblk"), {
QStringLiteral("--json"),
QStringLiteral("--paths"),
QStringLiteral("--output=name,fstype"),
partitionPath });
if (lsblkCommand.run(-1) && lsblkCommand.exitCode() == 0) {
const QJsonArray partitionArray = QJsonDocument::fromJson(lsblkCommand.output().toUtf8()).object()[QLatin1String("blockdevices")].toArray();
for (const auto &partition : partitionArray) {
QJsonObject partitionObject = partition.toObject();
QString node = partitionObject[QLatin1String("name")].toString();
QString s = partitionObject[QLatin1String("fstype")].toString();
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")) {
ExternalCommand blkidCommand(QStringLiteral("blkid"), {
QStringLiteral("--output=value"),
QStringLiteral("--match-tag"),
QStringLiteral("SEC_TYPE"),
partitionPath });
// blkid uses SEC_TYPE to distinguish between FAT16 and FAT32
if (blkidCommand.run(-1) && blkidCommand.exitCode() == 0 && blkidCommand.output().trimmed() == 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 if (s == QStringLiteral("udf")) rval = FileSystem::Udf;
else if (s == QStringLiteral("iso9660")) rval = FileSystem::Iso9660;
else
qWarning() << "lsblk: unknown file system type " << s << " on " << partitionPath;
}
}
return rval;
}
PartitionTable::Flags SfdiskBackend::availableFlags(PartitionTable::TableType type)
{
PartitionTable::Flags flags;
if (type == PartitionTable::gpt) {
// These are not really flags but for now keep them for compatibility
// We should implement changing partition type
flags = PartitionTable::FlagBiosGrub |
PartitionTable::FlagEsp;
}
else if (type == PartitionTable::msdos || type == PartitionTable::msdos_sectorbased)
flags = PartitionTable::FlagBoot;
return flags;
}
CoreBackendDevice* SfdiskBackend::openDevice(const QString& deviceNode)
{
SfdiskDevice* device = new SfdiskDevice(deviceNode);
if (device == nullptr || !device->open()) {
delete device;
device = nullptr;
}
return device;
}
CoreBackendDevice* SfdiskBackend::openDeviceExclusive(const QString& deviceNode)
{
SfdiskDevice* device = new SfdiskDevice(deviceNode);
if (device == nullptr || !device->openExclusive()) {
delete device;
device = nullptr;
}
return device;
}
bool SfdiskBackend::closeDevice(CoreBackendDevice* coreDevice)
{
return coreDevice->close();
}
#include "sfdiskbackend.moc"

View File

@ -0,0 +1,62 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(SFDISKBACKEND__H)
#define SFDISKBACKEND__H
#include "backend/corebackend.h"
#include "core/partition.h"
#include "fs/filesystem.h"
#include <QList>
#include <QVariant>
class Device;
class KPluginFactory;
class QString;
/** Backend plugin for sfdisk
@author Andrius Štikonas <andrius@stikonas.eu>
*/
class SfdiskBackend : public CoreBackend
{
friend class KPluginFactory;
Q_DISABLE_COPY(SfdiskBackend)
private:
SfdiskBackend(QObject* parent, const QList<QVariant>& args);
public:
void initFSSupport() override;
QList<Device*> scanDevices(bool excludeReadOnly = false) override;
CoreBackendDevice* openDevice(const QString& deviceNode) override;
CoreBackendDevice* openDeviceExclusive(const QString& deviceNode) override;
bool closeDevice(CoreBackendDevice* coreDevice) override;
Device* scanDevice(const QString& deviceNode) override;
FileSystem::Type detectFileSystem(const QString& partitionPath) override;
private:
static void readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint);
void scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions);
static PartitionTable::Flags availableFlags(PartitionTable::TableType type);
};
#endif

View File

@ -0,0 +1,120 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#include "plugins/sfdisk/sfdiskdevice.h"
#include "plugins/sfdisk/sfdiskpartitiontable.h"
#include "core/partitiontable.h"
#include "util/externalcommand.h"
#include "util/report.h"
SfdiskDevice::SfdiskDevice(const QString& deviceNode) :
CoreBackendDevice(deviceNode),
m_deviceNode(deviceNode)
{
}
SfdiskDevice::~SfdiskDevice()
{
}
bool SfdiskDevice::open()
{
return true;
}
bool SfdiskDevice::openExclusive()
{
setExclusive(true);
return true;
}
bool SfdiskDevice::close()
{
if (isExclusive())
setExclusive(false);
return true;
}
CoreBackendPartitionTable* SfdiskDevice::openPartitionTable()
{
CoreBackendPartitionTable* ptable = new SfdiskPartitionTable(m_deviceNode);
if (ptable == nullptr || !ptable->open()) {
delete ptable;
ptable = nullptr;
}
return ptable;
}
bool SfdiskDevice::createPartitionTable(Report& report, const PartitionTable& ptable)
{
QByteArray tableType;
if (ptable.type() == PartitionTable::msdos || ptable.type() == PartitionTable::msdos_sectorbased)
tableType = QByteArrayLiteral("dos");
else
tableType = ptable.typeName().toLatin1();
ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { m_deviceNode } );
if ( createCommand.start(-1) && createCommand.write(QByteArrayLiteral("label: ") + tableType +
QByteArrayLiteral("\nwrite\n")) && createCommand.waitFor() ) {
return createCommand.output().contains(QStringLiteral("Script header accepted."));
}
return false;
}
bool SfdiskDevice::readData(QByteArray& buffer, qint64 offset, qint64 size)
{
if (!isExclusive())
return false;
ExternalCommand ddCommand(QStringLiteral("dd"), {
QStringLiteral("skip=") + QString::number(offset),
QStringLiteral("bs=") + QString::number(size),
QStringLiteral("count=1"),
QStringLiteral("iflag=skip_bytes"),
QStringLiteral("if=") + m_deviceNode }, QProcess::SeparateChannels);
if (ddCommand.run(-1) && ddCommand.exitCode() == 0) {
buffer = ddCommand.rawOutput();
return true;
}
return false;
}
bool SfdiskDevice::writeData(QByteArray& buffer, qint64 offset)
{
if (!isExclusive())
return false;
ExternalCommand ddCommand(QStringLiteral("dd"), {
QStringLiteral("of=") + m_deviceNode,
QStringLiteral("seek=") + QString::number(offset),
QStringLiteral("bs=1M"),
QStringLiteral("oflag=seek_bytes"),
QStringLiteral("conv=fsync") }, QProcess::SeparateChannels);
if ( ddCommand.start(-1) && ddCommand.write(buffer) == buffer.size() && ddCommand.waitFor() && ddCommand.exitCode() == 0 ) {
return true;
}
return false;
}

View File

@ -0,0 +1,55 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(SFDISKDEVICE__H)
#define SFDISKDEVICE__H
#include "backend/corebackenddevice.h"
#include <QtGlobal>
class Partition;
class PartitionTable;
class Report;
class CoreBackendPartitionTable;
class SfdiskDevice : public CoreBackendDevice
{
Q_DISABLE_COPY(SfdiskDevice);
public:
SfdiskDevice(const QString& deviceNode);
~SfdiskDevice();
public:
bool open() override;
bool openExclusive() override;
bool close() override;
CoreBackendPartitionTable* openPartitionTable() override;
bool createPartitionTable(Report& report, const PartitionTable& ptable) override;
bool readData(QByteArray& buffer, qint64 offset, qint64 size) override;
bool writeData(QByteArray& buffer, qint64 offset) override;
private:
QString m_deviceNode;
};
#endif

View File

@ -0,0 +1,35 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#include "plugins/sfdisk/sfdiskpartition.h"
#include "plugins/sfdisk/sfdiskbackend.h"
#include "util/report.h"
SfdiskPartition::SfdiskPartition() :
CoreBackendPartition()
{
}
bool SfdiskPartition::setFlag(Report& report, PartitionTable::Flag partitionManagerFlag, bool state)
{
Q_UNUSED(report);
Q_UNUSED(partitionManagerFlag);
Q_UNUSED(state);
return true;
}

View File

@ -0,0 +1,40 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(SFDISKPARTITION__H)
#define SFDISKPARTITION__H
#include "backend/corebackendpartition.h"
#include "core/partitiontable.h"
class Report;
class SfdiskPartition : public CoreBackendPartition
{
Q_DISABLE_COPY(SfdiskPartition);
public:
SfdiskPartition();
public:
bool setFlag(Report& report, PartitionTable::Flag flag, bool state) override;
};
#endif

View File

@ -0,0 +1,178 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#include "plugins/sfdisk/sfdiskpartitiontable.h"
#include "plugins/sfdisk/sfdiskpartition.h"
#include "backend/corebackend.h"
#include "backend/corebackendmanager.h"
#include "core/partition.h"
#include "core/device.h"
#include "fs/filesystem.h"
#include "util/report.h"
#include "util/externalcommand.h"
#include <unistd.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRegularExpression>
#include <KLocalizedString>
SfdiskPartitionTable::SfdiskPartitionTable(const QString& deviceNode) :
CoreBackendPartitionTable(),
m_deviceNode(deviceNode)
{
}
SfdiskPartitionTable::~SfdiskPartitionTable()
{
}
bool SfdiskPartitionTable::open()
{
return true;
}
bool SfdiskPartitionTable::commit(quint32 timeout)
{
if (!ExternalCommand(QStringLiteral("udevadm"), QStringList() << QStringLiteral("settle") << QStringLiteral("--timeout=") + QString::number(timeout)).run() &&
!ExternalCommand(QStringLiteral("udevsettle"), QStringList() << QStringLiteral("--timeout=") + QString::number(timeout)).run())
sleep(timeout);
return true;
}
CoreBackendPartition* SfdiskPartitionTable::getExtendedPartition()
{
return new SfdiskPartition();
}
CoreBackendPartition* SfdiskPartitionTable::getPartitionBySector(qint64 sector)
{
Q_UNUSED(sector);
return nullptr;
}
QString SfdiskPartitionTable::createPartition(Report& report, const Partition& partition)
{
if ( !(partition.roles().has(PartitionRole::Extended) || partition.roles().has(PartitionRole::Logical) || partition.roles().has(PartitionRole::Primary) ) ) {
report.line() << xi18nc("@info:progress", "Unknown partition role for new partition <filename>%1</filename> (roles: %2)", partition.deviceNode(), partition.roles().toString());
return QString();
}
QByteArray type = QByteArray(); // FIXME add map between fs types and default partition types
if (partition.roles().has(PartitionRole::Extended))
type = QByteArrayLiteral(" type=5");
// NOTE: at least on GPT partition types "are" partition flags
ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } );
if ( createCommand.start(-1) && createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) +
type +
QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.waitFor() ) {
QRegularExpression re(QStringLiteral("Created a new partition (\\d)"));
QRegularExpressionMatch rem = re.match(createCommand.output());
if (rem.hasMatch())
return partition.devicePath() + rem.captured(1);
}
report.line() << xi18nc("@info:progress", "Failed to add partition <filename>%1</filename> to device <filename>%2</filename>.", partition.deviceNode(), m_deviceNode);
return QString();
}
bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& partition)
{
ExternalCommand deleteCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--delete"), partition.devicePath(), QString::number(partition.number()) } );
if (deleteCommand.run(-1) && deleteCommand.exitCode() == 0)
return true;
report.line() << xi18nc("@info:progress", "Could not delete partition <filename>%1</filename>.", partition.devicePath());
return false;
}
bool SfdiskPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sectorStart, qint64 sectorEnd)
{
ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), partition.devicePath(), QStringLiteral("-N"), QString::number(partition.number()) } );
if ( sfdiskCommand.start(-1) && sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) +
QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) +
QByteArrayLiteral("\nY\n"))
&& sfdiskCommand.waitFor() && sfdiskCommand.exitCode() == 0) {
return true;
}
report.line() << xi18nc("@info:progress", "Could not set geometry for partition <filename>%1</filename> while trying to resize/move it.", partition.devicePath());
return false;
}
bool SfdiskPartitionTable::clobberFileSystem(Report& report, const Partition& partition)
{
ExternalCommand wipeCommand(report, QStringLiteral("wipefs"), { QStringLiteral("--all"), partition.partitionPath() } );
if (wipeCommand.run(-1) && wipeCommand.exitCode() == 0)
return true;
report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition <filename>%1</filename>.", partition.partitionPath());
return false;
}
bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength)
{
// sfdisk does not have any partition resize capabilities
Q_UNUSED(report);
Q_UNUSED(partition);
Q_UNUSED(newLength);
return false;
}
FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector)
{
FileSystem::Type type = FileSystem::Unknown;
ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } );
if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) {
const QJsonArray partitionTable = QJsonDocument::fromJson(jsonCommand.output().toUtf8()).object()[QLatin1String("partitiontable")].toObject()[QLatin1String("partitions")].toArray();
for (const auto &partition : partitionTable) {
const QJsonObject partitionObject = partition.toObject();
const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong();
if (start == sector) {
const QString deviceNode = partitionObject[QLatin1String("node")].toString();
type = CoreBackendManager::self()->backend()->detectFileSystem(deviceNode);
return type;
}
}
}
report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device <filename>%2</filename>.", sector, device.deviceNode());
return type;
}
bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition)
{
Q_UNUSED(report);
Q_UNUSED(partition);
return true;
}

View File

@ -0,0 +1,58 @@
/*************************************************************************
* Copyright (C) 2017 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(SFDISKPARTITIONTABLE__H)
#define SFDISKPARTITIONTABLE__H
#include "backend/corebackendpartitiontable.h"
#include "fs/filesystem.h"
#include <QtGlobal>
class CoreBackendPartition;
class Report;
class Partition;
class SfdiskPartitionTable : public CoreBackendPartitionTable
{
public:
SfdiskPartitionTable(const QString& deviceNode);
~SfdiskPartitionTable();
public:
bool open() override;
bool commit(quint32 timeout = 10) override;
CoreBackendPartition* getExtendedPartition() override;
CoreBackendPartition* getPartitionBySector(qint64 sector) override;
QString createPartition(Report& report, const Partition& partition) override;
bool deletePartition(Report& report, const Partition& partition) override;
bool updateGeometry(Report& report, const Partition& partition, qint64 sector_start, qint64 sector_end) override;
bool clobberFileSystem(Report& report, const Partition& partition) override;
bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override;
FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override;
bool setPartitionSystemType(Report& report, const Partition& partition) override;
private:
QString m_deviceNode;
};
#endif