Commit f7f2f626 authored by Kwon-Young Choi's avatar Kwon-Young Choi Committed by Nate Graham
Browse files

Implement mount ISO service plugin for Dolphin

The mountiso plugin adds a contextual menu entry for .iso files to
either mounting or unmounting the file as a loopback device depending on
the mount state.
This plugin uses udisksctl to either mount or unmount the iso file.
It also uses losetup to check if an iso file is already mounted

FEATURE: 175051
FIXED-IN: 20.08.0
parent 37b909f5
...@@ -21,6 +21,8 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS ...@@ -21,6 +21,8 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
I18n I18n
KIO KIO
TextWidgets TextWidgets
CoreAddons
Solid
) )
find_package(DolphinVcs) find_package(DolphinVcs)
...@@ -46,6 +48,9 @@ ecm_optional_add_subdirectory(git) ...@@ -46,6 +48,9 @@ ecm_optional_add_subdirectory(git)
ecm_optional_add_subdirectory(bazaar) ecm_optional_add_subdirectory(bazaar)
ecm_optional_add_subdirectory(dropbox) ecm_optional_add_subdirectory(dropbox)
ecm_optional_add_subdirectory(hg) ecm_optional_add_subdirectory(hg)
if(UNIX AND NOT APPLE)
ecm_optional_add_subdirectory(mountiso)
endif()
install(FILES org.kde.dolphin-plugins.metainfo.xml install(FILES org.kde.dolphin-plugins.metainfo.xml
DESTINATION ${KDE_INSTALL_METAINFODIR}) DESTINATION ${KDE_INSTALL_METAINFODIR})
......
project(mountisoplugin)
kcoreaddons_add_plugin(
mountisoaction
SOURCES mountisoaction.cpp
JSON mountisoaction.json
INSTALL_NAMESPACE "kf5/kfileitemaction")
target_link_libraries(mountisoaction
KF5::I18n
KF5::KIOWidgets
KF5::Solid)
/*
* Copyright (C) 2020 Kwon-Young Choi <kwon-young.choi@hotmail.fr>
*
* 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 "mountisoaction.h"
#include <fcntl.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <QAction>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDBusUnixFileDescriptor>
#include <QDebug>
#include <QIcon>
#include <QMap>
#include <QProcess>
#include <QString>
#include <QVariant>
#include <KLocalizedString>
#include <KPluginFactory>
#include <Solid/Block>
#include <Solid/Device>
#include <Solid/DeviceInterface>
#include <Solid/GenericInterface>
K_PLUGIN_CLASS_WITH_JSON(MountIsoAction, "mountisoaction.json")
MountIsoAction::MountIsoAction(QObject *parent, const QVariantList &)
: KAbstractFileItemActionPlugin(parent)
{
}
/**
* Get block device udi ("/org/freedesktop/UDisks2/block_devices/loop0") using
* its backing file name.
*
* Use the Solid framework to iterate through all block devices to check if the
* backing file correspond to the given backingFile.
*
* Warning: The use of GenericInterface makes this function non portable,
* especially to non Unix-like OS.
*
* @backingFile: backing file of the device we want.
*
* @return: device udi of the found block device. If no corresponding device
* was found, return a null QString.
*/
const QString getDeviceFromBackingFile(const QString &backingFile)
{
const QList<Solid::Device> blockDevices =
Solid::Device::listFromQuery("[ IS Block AND IS GenericInterface ]");
for (const Solid::Device &device : blockDevices) {
QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties();
if (properties.contains("BackingFile")
&& backingFile == properties["BackingFile"].value<QString>()) {
return device.udi();
}
}
return QString();
}
/**
* Callback function for mounting an iso file as a loop device
*
* Uses UDisks2 Manager DBus api to mount the iso file
*
* @file: iso file path to mount
*/
void mount(const QString &file)
{
const int fd = open(file.toLocal8Bit().data(), O_RDONLY);
if (fd == -1) {
qWarning() << "Error opening " << file << ": " << strerror(errno);
return;
}
auto qtFd = QDBusUnixFileDescriptor(fd);
int res = close(fd);
if (res == -1) {
qWarning() << "Error closing " << file << ": " << strerror(errno);
return;
}
QMap<QString, QVariant> options;
options["read-only"] = QVariant::fromValue(true);
QDBusInterface manager(
"org.freedesktop.UDisks2",
"/org/freedesktop/UDisks2/Manager",
"org.freedesktop.UDisks2.Manager",
QDBusConnection::systemBus());
QDBusReply<QDBusObjectPath> reply =
manager.call("LoopSetup", QVariant::fromValue(qtFd), options);
if (!reply.isValid()) {
qWarning() << "Error mounting " << file << ":" << reply.error().name()
<< reply.error().message();
}
}
/**
* Callback function for deleting a loop device
*
* Uses UDisks2 DBus api to delete a loop device
*
* @file: iso file to mount
*/
void unmount(const QString &device)
{
// Empty argument required for Loop Delete method to work
QMap<QString, QVariant> options;
QDBusInterface manager(
"org.freedesktop.UDisks2",
device,
"org.freedesktop.UDisks2.Loop",
QDBusConnection::systemBus());
manager.call("Delete", options);
}
QList<QAction *> MountIsoAction::actions(const KFileItemListProperties &fileItemInfos,
QWidget *parentWidget)
{
if (fileItemInfos.urlList().size() != 1
|| fileItemInfos.mimeType() != QLatin1String("application/x-cd-image")
|| !fileItemInfos.isLocal()) {
return {};
};
auto file = fileItemInfos.urlList().at(0).toLocalFile();
// Check if dbus can handle file descriptor
auto connection = QDBusConnection::sessionBus();
QDBusConnection::ConnectionCapabilities capabilities = connection.connectionCapabilities();
if (!(capabilities & QDBusConnection::UnixFileDescriptorPassing)) {
return {};
}
const QString device = getDeviceFromBackingFile(file);
if (device.isEmpty()) {
const QIcon icon = QIcon::fromTheme(QStringLiteral("media-mount"));
const QString title = i18nc("@action Action to mount an iso image", "Mount this iso image");
QAction *action = new QAction(icon, title, parentWidget);
connect(action, &QAction::triggered, this, [file]() { mount(file); });
return { action };
} else {
// fileItem is mounted on device
const QIcon icon = QIcon::fromTheme(QStringLiteral("media-eject"));
const QString title =
i18nc("@action Action to unmount an iso image", "Unmount this iso image");
QAction *action = new QAction(icon, title, parentWidget);
connect(action, &QAction::triggered, this, [device]() { unmount(device); });
return { action };
}
return {};
}
#include "mountisoaction.moc"
/*
* Copyright (C) 2020 Kwon-Young Choi <kwon-young.choi@hotmail.fr>
*
* 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 MOUNTISOACTION_H
#define MOUNTISOACTION_H
#include <KAbstractFileItemActionPlugin>
#include <KFileItemListProperties>
class QAction;
class QWidget;
class KFileItemListProperties;
/**
* File item action plugin to mount and unmount an iso file.
*
* This class adds a mount or unmount entry to an iso file contextual menu. If
* the iso file is already mounted, only the unmount entry will be shown while
* if the iso file is not mounted, only the mount entry will be shown.
*
* The current implementation uses a loop device to mount the iso file.
*
*/
class MountIsoAction : public KAbstractFileItemActionPlugin
{
Q_OBJECT
public:
MountIsoAction(QObject *parent, const QVariantList &args);
/**
* Adds a mount or unmount entry to the contextual menu of an iso file.
*
* For a menu entry to be shown, only one file should be selected, the
* mimetype of the file should be "application/x-cd-image" and the file
* should be local. Then, the mount state of the iso file is checked and if
* the iso file is already mounted, we get the corresponding block device.
* If the iso file is not already mounted, we show a mount entry and create
* a callback to mount the iso file using udisksctl.
* If the iso file is already mounted, we show a unmount entry and create a
* callback to unmount the iso file using udisksctl.
*/
QList<QAction *> actions(const KFileItemListProperties &fileItemInfos,
QWidget *parentWidget) override;
};
#endif
{
"KPlugin": {
"Icon": "application-x-cd-image",
"MimeTypes": [
"application/x-cd-image"
],
"Name": "Mount and unmount iso image",
"ServiceTypes": [
"KFileItemAction/Plugin"
]
},
"MimeType": "application/x-cd-image;"
}
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<name xml:lang="uk">Додатки до Dolphin</name> <name xml:lang="uk">Додатки до Dolphin</name>
<name xml:lang="x-test">xxDolphin Pluginsxx</name> <name xml:lang="x-test">xxDolphin Pluginsxx</name>
<name xml:lang="zh-CN">Dolphin 插件</name> <name xml:lang="zh-CN">Dolphin 插件</name>
<summary>Dolphin integration for revision control systems and Dropbox</summary> <summary>Dolphin integration for revision control systems, Dropbox, and disk images.</summary>
<summary xml:lang="ca">Integració al Dolphin per a sistemes de control de revisions i Dropbox</summary> <summary xml:lang="ca">Integració al Dolphin per a sistemes de control de revisions i Dropbox</summary>
<summary xml:lang="ca-valencia">Integració al Dolphin per a sistemes de control de revisions i Dropbox</summary> <summary xml:lang="ca-valencia">Integració al Dolphin per a sistemes de control de revisions i Dropbox</summary>
<summary xml:lang="de">Integration von Versionsverwaltungssysteme und Dropbox in Dolphin</summary> <summary xml:lang="de">Integration von Versionsverwaltungssysteme und Dropbox in Dolphin</summary>
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<summary xml:lang="x-test">xxDolphin integration for revision control systems and Dropboxxx</summary> <summary xml:lang="x-test">xxDolphin integration for revision control systems and Dropboxxx</summary>
<summary xml:lang="zh-CN">Dolphin 文件版本控制系统和 Dropbox 集成</summary> <summary xml:lang="zh-CN">Dolphin 文件版本控制系统和 Dropbox 集成</summary>
<description> <description>
<p>These plugins integrate Dolphin with the revision control systems Bazaar, Mercurial and Git. A Dropbox plugin gives action items to keep your files synced to the Dropbox service.</p> <p>These plugins integrate Dolphin with the revision control systems Bazaar, Mercurial and Git. A Dropbox plugin gives action items to keep your files synced to the Dropbox service. A disk image integration plugin adds a mount or unmount action.</p>
<p xml:lang="ca">Aquests connectors integren el Dolphin amb els sistemes de control de revisions Bazaar, Mercurial i Git. I un connector de Dropbox proporciona elements d'acció per a mantenir els vostres fitxers sincronitzats amb el servei de Dropbox.</p> <p xml:lang="ca">Aquests connectors integren el Dolphin amb els sistemes de control de revisions Bazaar, Mercurial i Git. I un connector de Dropbox proporciona elements d'acció per a mantenir els vostres fitxers sincronitzats amb el servei de Dropbox.</p>
<p xml:lang="ca-valencia">Aquests connectors integren el Dolphin amb els sistemes de control de revisions Bazaar, Mercurial i Git. I un connector de Dropbox proporciona elements d'acció per a mantindre els vostres fitxers sincronitzats amb el servei de Dropbox.</p> <p xml:lang="ca-valencia">Aquests connectors integren el Dolphin amb els sistemes de control de revisions Bazaar, Mercurial i Git. I un connector de Dropbox proporciona elements d'acció per a mantindre els vostres fitxers sincronitzats amb el servei de Dropbox.</p>
<p xml:lang="de">Diese Module integrieren die Versionsverwaltungssysteme Bazaar, Mercurial und Git. Ein Dropbox-Modul stellt Aktionen zum Abgleichen von Dateien mit dem Dropbox-Dienst bereit.</p> <p xml:lang="de">Diese Module integrieren die Versionsverwaltungssysteme Bazaar, Mercurial und Git. Ein Dropbox-Modul stellt Aktionen zum Abgleichen von Dateien mit dem Dropbox-Dienst bereit.</p>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment