Merge branch 'fstab'

This commit is contained in:
Andrius Štikonas 2020-09-15 01:09:38 +01:00
commit fbe54e21f3
6 changed files with 126 additions and 48 deletions

View File

@ -21,6 +21,8 @@
#include "util/externalcommand.h"
#include "util/report.h"
#include <algorithm>
#if defined(Q_OS_LINUX)
#include <blkid/blkid.h>
#endif
@ -34,6 +36,8 @@
static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType, QString& m_deviceNode);
static QString findBlkIdDevice(const char *token, const QString& value);
static void writeEntry(QTextStream& s, const FstabEntry& entry, std::array<unsigned int, 4> columnWidth);
std::array<unsigned int, 4> fstabColumnWidth(const FstabEntryList& fstabEntries);
struct FstabEntryPrivate
{
@ -59,6 +63,7 @@ FstabEntry::FstabEntry(const QString& fsSpec, const QString& mountPoint, const Q
d->m_comment = comment;
d->m_options = options.split(QLatin1Char(','));
d->m_options.removeAll(QStringLiteral("defaults"));
parseFsSpec(d->m_fsSpec, d->m_entryType, d->m_deviceNode);
}
@ -88,15 +93,20 @@ FstabEntryList readFstabEntries( const QString& fstabPath )
// (4) dump frequency (optional, defaults to 0), no comment is allowed if omitted,
// (5) pass number (optional, defaults to 0), no comment is allowed if omitted,
// (#) comment (optional).
auto fsSpec = splitLine.at(0);
auto mountPoint = splitLine.at(1);
auto fsType = splitLine.at(2);
auto options = splitLine.at(3);
switch (splitLine.length()) {
case 4:
fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3) } );
fstabEntries.push_back( {fsSpec, mountPoint, fsType, options } );
break;
case 5:
fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt() } );
fstabEntries.push_back( {fsSpec, mountPoint, fsType, options, splitLine.at(4).toInt() } );
break;
case 6:
fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt(), splitLine.at(5).toInt(), comment.isEmpty() ? QString() : QLatin1Char('#') + comment } );
fstabEntries.push_back( {fsSpec, mountPoint, fsType, options, splitLine.at(4).toInt(), splitLine.at(5).toInt(), comment.isEmpty() ? QString() : QLatin1Char('#') + comment } );
break;
default:
fstabEntries.push_back( { {}, {}, {}, {}, {}, {}, QLatin1Char('#') + line } );
@ -142,6 +152,11 @@ const QStringList& FstabEntry::options() const
return d->m_options;
}
const QString FstabEntry::optionsString() const
{
return options().size() > 0 ? options().join(QLatin1Char(',')) : QStringLiteral("defaults");
}
int FstabEntry::dumpFreq() const
{
return d->m_dumpFreq;
@ -232,40 +247,50 @@ static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType,
}
}
static void writeEntry(QTextStream& s, const FstabEntry& entry)
// Used to nicely format fstab file
std::array<unsigned int, 4> fstabColumnWidth(const FstabEntryList& fstabEntries)
{
std::array<unsigned int, 4> columnWidth;
#define FIELD_WIDTH(x) 3 + std::max_element(fstabEntries.begin(), fstabEntries.end(), [](const FstabEntry& a, const FstabEntry& b) {return a.x().length() < b.x().length(); })->x().length();
columnWidth[0] = FIELD_WIDTH(fsSpec);
columnWidth[1] = FIELD_WIDTH(mountPoint);
columnWidth[2] = FIELD_WIDTH(type);
columnWidth[3] = FIELD_WIDTH(optionsString);
return columnWidth;
}
static void writeEntry(QTextStream& s, const FstabEntry& entry, std::array<unsigned int, 4> columnWidth)
{
if (entry.entryType() == FstabEntry::Type::comment) {
s << entry.comment() << "\n";
return;
}
QString options;
if (entry.options().size() > 0) {
options = entry.options().join(QLatin1Char(','));
if (options.isEmpty())
options = QStringLiteral("defaults");
}
else
options = QStringLiteral("defaults");
s << entry.fsSpec() << "\t"
<< (entry.mountPoint().isEmpty() ? QStringLiteral("none") : entry.mountPoint()) << "\t"
<< entry.type() << "\t"
<< options << "\t"
<< entry.dumpFreq() << "\t"
<< entry.passNumber() << "\t"
s.setFieldAlignment(QTextStream::AlignLeft);
s.setFieldWidth(columnWidth[0]);
s << entry.fsSpec()
<< qSetFieldWidth(columnWidth[1]) << (entry.mountPoint().isEmpty() ? QStringLiteral("none") : entry.mountPoint())
<< qSetFieldWidth(columnWidth[2]) << entry.type()
<< qSetFieldWidth(columnWidth[3]) << entry.optionsString() << qSetFieldWidth(0)
<< entry.dumpFreq() << " "
<< entry.passNumber() << " "
<< entry.comment() << "\n";
}
bool writeMountpoints(const FstabEntryList& fstabEntries, const QString& filename)
{
Report report(nullptr);
QByteArray fstabContents;
QString fstabContents;
QTextStream out(&fstabContents);
std::array<unsigned int, 4> columnWidth = fstabColumnWidth(fstabEntries);
for (const auto &e : fstabEntries)
writeEntry(out, e);
writeEntry(out, e, columnWidth);
ExternalCommand cmd;
return cmd.writeData(report, fstabContents, filename, 0);
return cmd.createFile(fstabContents.toLocal8Bit(), filename);
}

View File

@ -66,6 +66,11 @@ public:
*/
const QStringList& options() const;
/**
* @return the mount options associated with the file system
*/
const QString optionsString() const;
/**
* @return the fs_freq field of fstab entry
*/

View File

@ -124,11 +124,6 @@ bool ExternalCommand::start(int timeout)
if (command().isEmpty())
return false;
if (!QDBusConnection::systemBus().isConnected()) {
qWarning() << QDBusConnection::systemBus().lastError().message();
return false;
}
if (report())
report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" "))));
@ -139,10 +134,9 @@ bool ExternalCommand::start(int timeout)
if (cmd.isEmpty())
cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") });
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"),
QStringLiteral("/Helper"), QDBusConnection::systemBus(), this);
interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days
auto interface = helperInterface();
if (!interface)
return false;
bool rval = false;
@ -176,18 +170,13 @@ bool ExternalCommand::copyBlocks(const CopySource& source, CopyTarget& target)
bool rval = true;
const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy
if (!QDBusConnection::systemBus().isConnected()) {
qWarning() << QDBusConnection::systemBus().lastError().message();
return false;
}
// TODO KF6:Use new signal-slot syntax
connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long)));
connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport);
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"),
QStringLiteral("/Helper"), QDBusConnection::systemBus(), this);
interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days
auto interface = helperInterface();
if (!interface)
return false;
QDBusPendingCall pcall = interface->copyblocks(source.path(), source.firstByte(), source.length(),
target.path(), target.firstByte(), blockSize);
@ -222,19 +211,40 @@ bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer,
if (report())
report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" "))));
bool rval = true;
auto interface = helperInterface();
if (!interface)
return false;
QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte);
return waitForDbusReply(pcall);
}
bool ExternalCommand::createFile(const QByteArray& buffer, const QString& deviceNode)
{
auto interface = helperInterface();
if (!interface)
return false;
QDBusPendingCall pcall = interface->createFile(buffer, deviceNode);
return waitForDbusReply(pcall);
}
OrgKdeKpmcoreExternalcommandInterface* ExternalCommand::helperInterface()
{
if (!QDBusConnection::systemBus().isConnected()) {
qWarning() << QDBusConnection::systemBus().lastError().message();
return false;
return nullptr;
}
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"),
QStringLiteral("/Helper"), QDBusConnection::systemBus(), this);
interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days
QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte);
return interface;
}
bool ExternalCommand::waitForDbusReply(QDBusPendingCall &pcall)
{
bool rval = true;
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
QEventLoop loop;
@ -255,7 +265,6 @@ bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer,
return rval;
}
bool ExternalCommand::write(const QByteArray& input)
{
if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" ))

View File

@ -38,6 +38,8 @@ class Report;
class CopySource;
class CopyTarget;
class QDBusInterface;
class QDBusPendingCall;
class OrgKdeKpmcoreExternalcommandInterface;
struct ExternalCommandPrivate;
@ -71,6 +73,7 @@ public:
public:
bool copyBlocks(const CopySource& source, CopyTarget& target);
bool writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte); // same as copyBlocks but from QByteArray
bool createFile(const QByteArray& buffer, const QString& deviceNode); // similar to writeData but creates a new file
/**< @param cmd the command to run */
void setCommand(const QString& cmd);
@ -128,6 +131,8 @@ public Q_SLOTS:
private:
void setExitCode(int i);
void onReadOutput();
bool waitForDbusReply(QDBusPendingCall &pcall);
OrgKdeKpmcoreExternalcommandInterface* helperInterface();
private:
std::unique_ptr<ExternalCommandPrivate> d;

View File

@ -118,7 +118,7 @@ bool ExternalCommandHelper::readData(const QString& sourceDevice, QByteArray& bu
return true;
}
/** Writes the data from buffer to a given device or file.
/** Writes the data from buffer to a given device.
@param targetDevice device or file to write to
@param buffer the data that we write
@param offset offset where to begin writing
@ -128,7 +128,8 @@ bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteAr
{
QFile device(targetDevice);
if (!device.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered)) {
auto flags = QIODevice::WriteOnly | QIODevice::Unbuffered | QIODevice::Append;
if (!device.open(flags)) {
qCritical() << xi18n("Could not open device <filename>%1</filename> for writing.", targetDevice);
return false;
}
@ -146,6 +147,29 @@ bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteAr
return true;
}
/** Creates a new file with given contents.
@param filePath file to write to
@param fileContents the data that we write
@return true on success
*/
bool ExternalCommandHelper::createFile(const QString &filePath, const QByteArray& fileContents)
{
QFile device(filePath);
auto flags = QIODevice::WriteOnly | QIODevice::Unbuffered;
if (!device.open(flags)) {
qCritical() << xi18n("Could not open file <filename>%1</filename> for writing.", filePath);
return false;
}
if (device.write(fileContents) != fileContents.size()) {
qCritical() << xi18n("Could not write to file <filename>%1</filename>.", filePath);
return false;
}
return true;
}
// If targetDevice is empty then return QByteArray with data that was read from disk.
QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize)
{
@ -239,12 +263,20 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const
bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte)
{
// Do not allow using this helper for writing to arbitrary location
if ( targetDevice.left(5) != QStringLiteral("/dev/") && !targetDevice.contains(QStringLiteral("/etc/fstab")))
if ( targetDevice.left(5) != QStringLiteral("/dev/") )
return false;
return writeData(targetDevice, buffer, targetFirstByte);
}
bool ExternalCommandHelper::createFile(const QByteArray& fileContents, const QString& filePath)
{
// Do not allow using this helper for writing to arbitrary location
if ( !filePath.contains(QStringLiteral("/etc/fstab")) )
return false;
return createFile(filePath, fileContents);
}
QVariantMap ExternalCommandHelper::start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode)
{

View File

@ -41,12 +41,14 @@ Q_SIGNALS:
public:
bool readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size);
bool writeData(const QString& targetDevice, const QByteArray& buffer, const qint64 offset);
bool createFile(const QString& filePath, const QByteArray& fileContents);
public Q_SLOTS:
ActionReply init(const QVariantMap& args);
Q_SCRIPTABLE QVariantMap start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode);
Q_SCRIPTABLE QVariantMap copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize);
Q_SCRIPTABLE bool writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte);
Q_SCRIPTABLE bool createFile(const QByteArray& fileContents, const QString& filePath);
Q_SCRIPTABLE void exit();
private: