Skip to content

Commit

Permalink
Linux: overwrite start and end of the drive with zeroes when restoring
Browse files Browse the repository at this point in the history
Similar to Windows, overwrite the beginning of the drive and the end of
the drive with zeroes before creating a new partition table. This is to
make sure there are no partition table residues after a Fedora image was
written onto the drive.

Resolves #575
  • Loading branch information
grulja committed Nov 22, 2024
1 parent 8895227 commit 4545e4c
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 107 deletions.
1 change: 1 addition & 0 deletions src/helper/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ include_directories(

set(HELPER_SRCS
main.cpp
job.cpp
restorejob.cpp
writejob.cpp
)
Expand Down
83 changes: 83 additions & 0 deletions src/helper/linux/job.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Fedora Media Writer
* Copyright (C) 2024 Jan Grulich <[email protected]>
* Copyright (C) 2016 Martin Bříza <[email protected]>
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "job.h"

#include <QDBusArgument>
#include <QDBusInterface>
#include <QtDBus>

Q_DECLARE_METATYPE(Properties)
Q_DECLARE_METATYPE(InterfacesAndProperties)
Q_DECLARE_METATYPE(DBusIntrospection)

Job::Job(const QString &where)
: Job(QString(), where)
{
}

Job::Job(const QString &what, const QString &where)
: what(what)
, where(where)
{
qDBusRegisterMetaType<Properties>();
qDBusRegisterMetaType<InterfacesAndProperties>();
qDBusRegisterMetaType<DBusIntrospection>();
}

QDBusUnixFileDescriptor Job::getDescriptor()
{
QDBusInterface device("org.freedesktop.UDisks2", where, "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus(), this);
QString drivePath = qvariant_cast<QDBusObjectPath>(device.property("Drive")).path();
QDBusInterface manager("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2", "org.freedesktop.DBus.ObjectManager", QDBusConnection::systemBus());
QDBusMessage message = manager.call("GetManagedObjects");

if (message.arguments().length() == 1) {
QDBusArgument arg = qvariant_cast<QDBusArgument>(message.arguments().first());
DBusIntrospection objects;
arg >> objects;
for (auto i : objects.keys()) {
if (objects[i].contains("org.freedesktop.UDisks2.Filesystem")) {
QString currentDrivePath = qvariant_cast<QDBusObjectPath>(objects[i]["org.freedesktop.UDisks2.Block"]["Drive"]).path();
if (currentDrivePath == drivePath) {
QDBusInterface partition("org.freedesktop.UDisks2", i.path(), "org.freedesktop.UDisks2.Filesystem", QDBusConnection::systemBus());
message = partition.call("Unmount", Properties{{"force", true}});
}
}
}
} else {
err << message.errorMessage();
err.flush();
qApp->exit(2);
return QDBusUnixFileDescriptor(-1);
}

QDBusReply<QDBusUnixFileDescriptor> reply = device.call(QDBus::Block, "OpenDevice", "rw", Properties{{"flags", O_DIRECT | O_SYNC | O_CLOEXEC}});
QDBusUnixFileDescriptor fd = reply.value();

if (!fd.isValid()) {
err << reply.error().message();
err.flush();
qApp->exit(2);
return QDBusUnixFileDescriptor(-1);
}

return fd;
}
61 changes: 61 additions & 0 deletions src/helper/linux/job.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Fedora Media Writer
* Copyright (C) 2024 Jan Grulich <[email protected]>
* Copyright (C) 2016 Martin Bříza <[email protected]>
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef JOB_H
#define JOB_H

#include <QDBusObjectPath>
#include <QDBusUnixFileDescriptor>
#include <QObject>
#include <QTextStream>

#include <fcntl.h>
#include <unistd.h>

#include <memory>
#include <tuple>

typedef QHash<QString, QVariant> Properties;
typedef QHash<QString, Properties> InterfacesAndProperties;
typedef QHash<QDBusObjectPath, InterfacesAndProperties> DBusIntrospection;

class Job : public QObject
{
Q_OBJECT
public:
explicit Job(const QString &where);
Job(const QString &what, const QString &where);

QDBusUnixFileDescriptor getDescriptor();

public slots:
virtual void work() = 0;

protected:
QString what;
QString where;
QTextStream out{stdout};
QTextStream err{stderr};
QDBusUnixFileDescriptor fd{-1};
};

std::tuple<std::unique_ptr<char[]>, char *, std::size_t> pageAlignedBuffer(std::size_t pages = 1024);

#endif // JOB_H
83 changes: 71 additions & 12 deletions src/helper/linux/restorejob.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Fedora Media Writer
* Copyright (C) 2024 Jan Grulich <[email protected]>
* Copyright (C) 2016 Martin Bříza <[email protected]>
*
* This program is free software; you can redistribute it and/or
Expand All @@ -20,24 +21,19 @@
#include "restorejob.h"

#include <QCoreApplication>
#include <QTextStream>
#include <QThread>
#include <QTimer>

#include <QDBusArgument>
#include <QDBusInterface>
#include <QDBusUnixFileDescriptor>
#include <QtDBus>
#include <QDBusMessage>
#include <QDBusReply>

typedef QHash<QString, QVariant> Properties;
typedef QHash<QString, Properties> InterfacesAndProperties;
typedef QHash<QDBusObjectPath, InterfacesAndProperties> DBusIntrospection;
Q_DECLARE_METATYPE(Properties)
Q_DECLARE_METATYPE(InterfacesAndProperties)
Q_DECLARE_METATYPE(DBusIntrospection)
#include <stdio.h>
#include <sys/types.h>

RestoreJob::RestoreJob(const QString &where)
: QObject(nullptr)
, where(where)
: Job(where)
{
QTimer::singleShot(0, this, SLOT(work()));
}
Expand Down Expand Up @@ -65,7 +61,70 @@ void RestoreJob::work()
}
}

QDBusReply<void> formatReply = device.call("Format", "dos", Properties());
// Wipe out first and last 128 blocks with zeroes
fd = getDescriptor();
if (fd.fileDescriptor() < 0) {
err << tr("Failed to open device for writing");
err.flush();
qApp->exit(1);
}

auto bufferOwner = pageAlignedBuffer();
char *buffer = std::get<1>(bufferOwner);
qint64 size = std::get<2>(bufferOwner);

memset(buffer, '\0', size);

// Overwrite first 128 blocks with zeroes
for (int i = 0; i < 128; i++) {
qint64 written = ::write(fd.fileDescriptor(), buffer, size);
if (written != size) {
err << tr("Destination drive is not writable");
err.flush();
qApp->exit(1);
}
}

// Rewind the filepointer to the last 128 blocks
off_t filesize = lseek(fd.fileDescriptor(), 0, SEEK_END);
if (filesize == static_cast<off_t>(-1)) {
err << tr("Failed to get file size");
err.flush();
qApp->exit(1);
}

off_t offset = filesize - (128 * size);
if (offset < 0) {
err << tr("File size is smaller than 128 blocks");
err.flush();
qApp->exit(1);
}

// Move the file pointer to the calculated offset
if (lseek(fd.fileDescriptor(), offset, SEEK_SET) == static_cast<off_t>(-1)) {
err << tr("Failed to move file pointer to the end region");
err.flush();
qApp->exit(1);
}

// Overwrite last 128 blocks with zeroes
for (int i = 0; i < 128; i++) {
qint64 written = ::write(fd.fileDescriptor(), buffer, size);
if (written != size) {
err << tr("Destination drive is not writable");
err.flush();
qApp->exit(1);
}
}

// Ensure data is flushed to disk
if (::fsync(fd.fileDescriptor()) == -1) {
err << tr("Failed to sync data to disk");
err.flush();
qApp->exit(1);
}

QDBusReply<void> formatReply = device.call("Format", "gpt", Properties());
if (!formatReply.isValid() && formatReply.error().type() != QDBusError::NoReply) {
err << formatReply.error().message() << "\n";
err.flush();
Expand Down
15 changes: 5 additions & 10 deletions src/helper/linux/restorejob.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Fedora Media Writer
* Copyright (C) 2024 Jan Grulich <[email protected]>
* Copyright (C) 2016 Martin Bříza <[email protected]>
*
* This program is free software; you can redistribute it and/or
Expand All @@ -20,22 +21,16 @@
#ifndef RESTOREJOB_H
#define RESTOREJOB_H

#include <QObject>
#include <QTextStream>
#include "job.h"

class RestoreJob : public QObject
class RestoreJob : public Job
{
Q_OBJECT
public:
explicit RestoreJob(const QString &where);
public slots:
void work();

private:
QTextStream out{stdout};
QTextStream err{stderr};

QString where;
public slots:
void work() override;
};

#endif // RESTOREJOB_H
69 changes: 3 additions & 66 deletions src/helper/linux/writejob.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Fedora Media Writer
* Copyright (C) 2024 Jan Grulich <[email protected]>
* Copyright (C) 2016 Martin Bříza <[email protected]>
*
* This program is free software; you can redistribute it and/or
Expand All @@ -21,41 +22,17 @@

#include <QCoreApplication>
#include <QProcess>
#include <QTextStream>
#include <QRegularExpression>
#include <QTimer>
#include <QtGlobal>

#include <QDBusInterface>
#include <QDBusUnixFileDescriptor>
#include <QtDBus>

#include <fcntl.h>
#include <unistd.h>

#include <tuple>
#include <utility>

#include <lzma.h>

#include "isomd5/libcheckisomd5.h"

#include <QDebug>

typedef QHash<QString, QVariant> Properties;
typedef QHash<QString, Properties> InterfacesAndProperties;
typedef QHash<QDBusObjectPath, InterfacesAndProperties> DBusIntrospection;
Q_DECLARE_METATYPE(Properties)
Q_DECLARE_METATYPE(InterfacesAndProperties)
Q_DECLARE_METATYPE(DBusIntrospection)

WriteJob::WriteJob(const QString &what, const QString &where)
: QObject(nullptr)
, what(what)
, where(where)
: Job(what, where)
{
qDBusRegisterMetaType<Properties>();
qDBusRegisterMetaType<InterfacesAndProperties>();
qDBusRegisterMetaType<DBusIntrospection>();
connect(&watcher, &QFileSystemWatcher::fileChanged, this, &WriteJob::onFileChanged);
QTimer::singleShot(0, this, SLOT(work()));
}
Expand All @@ -73,46 +50,6 @@ int WriteJob::onMediaCheckAdvanced(long long offset, long long total)
return 0;
}

QDBusUnixFileDescriptor WriteJob::getDescriptor()
{
QDBusInterface device("org.freedesktop.UDisks2", where, "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus(), this);
QString drivePath = qvariant_cast<QDBusObjectPath>(device.property("Drive")).path();
QDBusInterface manager("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2", "org.freedesktop.DBus.ObjectManager", QDBusConnection::systemBus());
QDBusMessage message = manager.call("GetManagedObjects");

if (message.arguments().length() == 1) {
QDBusArgument arg = qvariant_cast<QDBusArgument>(message.arguments().first());
DBusIntrospection objects;
arg >> objects;
for (auto i : objects.keys()) {
if (objects[i].contains("org.freedesktop.UDisks2.Filesystem")) {
QString currentDrivePath = qvariant_cast<QDBusObjectPath>(objects[i]["org.freedesktop.UDisks2.Block"]["Drive"]).path();
if (currentDrivePath == drivePath) {
QDBusInterface partition("org.freedesktop.UDisks2", i.path(), "org.freedesktop.UDisks2.Filesystem", QDBusConnection::systemBus());
message = partition.call("Unmount", Properties{{"force", true}});
}
}
}
} else {
err << message.errorMessage();
err.flush();
qApp->exit(2);
return QDBusUnixFileDescriptor(-1);
}

QDBusReply<QDBusUnixFileDescriptor> reply = device.call(QDBus::Block, "OpenDevice", "rw", Properties{{"flags", O_DIRECT | O_SYNC | O_CLOEXEC}});
QDBusUnixFileDescriptor fd = reply.value();

if (!fd.isValid()) {
err << reply.error().message();
err.flush();
qApp->exit(2);
return QDBusUnixFileDescriptor(-1);
}

return fd;
}

bool WriteJob::write(int fd)
{
if (what.endsWith(".xz"))
Expand Down
Loading

0 comments on commit 4545e4c

Please sign in to comment.