Files
cantata/3rdparty/solid-lite/backends/hal/halstorageaccess.cpp
Craig Drummond eff0e66d39 Use AUTOMOC
2018-03-07 19:04:14 +00:00

517 lines
18 KiB
C++

/*
Copyright 2006 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "halstorageaccess.h"
#include "halfstabhandling.h"
#include "../../genericinterface.h"
#include <QLocale>
#include <QDebug>
#include <QProcess>
#include <QTimer>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDBusVariant>
#include <QApplication>
#include <QWidget>
#include <unistd.h>
#include <stdlib.h>
#ifdef Q_OS_FREEBSD
#include <langinfo.h>
#endif
using namespace Solid::Backends::Hal;
StorageAccess::StorageAccess(HalDevice *device)
: DeviceInterface(device), m_setupInProgress(false), m_teardownInProgress(false), m_ejectInProgress(false),
m_passphraseRequested(false)
{
connect(device, SIGNAL(propertyChanged(QMap<QString,int>)),
this, SLOT(slotPropertyChanged(QMap<QString,int>)));
// Delay connecting to DBus signals to avoid the related time penalty
// in hot paths such as predicate matching
QTimer::singleShot(0, this, SLOT(connectDBusSignals()));
}
StorageAccess::~StorageAccess()
{
}
void StorageAccess::connectDBusSignals()
{
m_device->registerAction("setup", this,
SLOT(slotSetupRequested()),
SLOT(slotSetupDone(int,QString)));
m_device->registerAction("teardown", this,
SLOT(slotTeardownRequested()),
SLOT(slotTeardownDone(int,QString)));
m_device->registerAction("eject", this,
SLOT(slotEjectRequested()),
SLOT(slotEjectDone(int,QString)));
}
void StorageAccess::slotSetupDone(int error, const QString &errorString)
{
m_setupInProgress = false;
emit setupDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
}
void StorageAccess::slotTeardownDone(int error, const QString &errorString)
{
m_teardownInProgress = false;
emit teardownDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
}
void StorageAccess::slotEjectDone(int error, const QString &errorString)
{
m_ejectInProgress = false;
emit ejectDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
}
bool StorageAccess::isAccessible() const
{
if (m_device->prop("info.interfaces").toStringList().contains("org.freedesktop.Hal.Device.Volume.Crypto")) {
// Might be a bit slow, but I see no cleaner way to do this with HAL...
QDBusInterface manager("org.freedesktop.Hal",
"/org/freedesktop/Hal/Manager",
"org.freedesktop.Hal.Manager",
QDBusConnection::systemBus());
QDBusReply<QStringList> reply = manager.call("FindDeviceStringMatch",
"volume.crypto_luks.clear.backing_volume",
m_device->udi());
QStringList list = reply;
return reply.isValid() && !list.isEmpty();
} else {
return m_device->prop("volume.is_mounted").toBool();
}
}
QString StorageAccess::filePath() const
{
QString result = m_device->prop("volume.mount_point").toString();
if (result.isEmpty()) {
QStringList mountpoints
= FstabHandling::possibleMountPoints(m_device->prop("block.device").toString());
if (mountpoints.size()==1) {
result = mountpoints.first();
}
}
return result;
}
bool StorageAccess::isIgnored() const
{
HalDevice lock("/org/freedesktop/Hal/devices/computer");
bool isLocked = lock.prop("info.named_locks.Global.org.freedesktop.Hal.Device.Storage.locked").toBool();
if (m_device->prop("volume.ignore").toBool() || isLocked ){
return true;
}
const QString mount_point = StorageAccess(m_device).filePath();
const bool mounted = m_device->prop("volume.is_mounted").toBool();
if (!mounted) {
return false;
} else if (mount_point.startsWith(QLatin1String("/media/")) || mount_point.startsWith(QLatin1String("/mnt/"))) {
return false;
}
/* Now be a bit more aggressive on what we want to ignore,
* the user generally need to check only what's removable or in /media
* the volumes mounted to make the system (/, /boot, /var, etc.)
* are useless to him.
*/
Solid::Device drive(m_device->prop("block.storage_device").toString());
const bool removable = drive.as<Solid::GenericInterface>()->property("storage.removable").toBool();
const bool hotpluggable = drive.as<Solid::GenericInterface>()->property("storage.hotpluggable").toBool();
return !removable && !hotpluggable;
}
bool StorageAccess::setup()
{
if (m_teardownInProgress || m_setupInProgress || isAccessible()) {
return false;
}
m_setupInProgress = true;
m_device->broadcastActionRequested("setup");
if (m_device->prop("info.interfaces").toStringList().contains("org.freedesktop.Hal.Device.Volume.Crypto")) {
return requestPassphrase();
} else if (FstabHandling::isInFstab(m_device->prop("block.device").toString())) {
return callSystemMount();
} else {
return callHalVolumeMount();
}
}
bool StorageAccess::teardown()
{
if (m_teardownInProgress || m_setupInProgress || !isAccessible()) {
return false;
}
m_teardownInProgress = true;
m_device->broadcastActionRequested("teardown");
if (m_device->prop("info.interfaces").toStringList().contains("org.freedesktop.Hal.Device.Volume.Crypto")) {
return callCryptoTeardown();
} else if (FstabHandling::isInFstab(m_device->prop("block.device").toString())) {
return callSystemUnmount();
} else {
return callHalVolumeUnmount();
}
}
void StorageAccess::slotPropertyChanged(const QMap<QString,int> &changes)
{
if (changes.contains("volume.is_mounted"))
{
emit accessibilityChanged(isAccessible(), m_device->udi());
}
}
void StorageAccess::slotDBusReply(const QDBusMessage &/*reply*/)
{
if (m_setupInProgress) {
m_setupInProgress = false;
m_device->broadcastActionDone("setup");
} else if (m_teardownInProgress) {
m_teardownInProgress = false;
m_device->broadcastActionDone("teardown");
HalDevice drive(m_device->prop("block.storage_device").toString());
if (drive.prop("storage.drive_type").toString()!="cdrom"
&& drive.prop("storage.requires_eject").toBool()) {
QString devnode = m_device->prop("block.device").toString();
#if defined(Q_OS_OPENBSD)
QString program = "cdio";
QStringList args;
args << "-f" << devnode << "eject";
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
devnode.remove("/dev/").replace("([0-9]).", "\\1");
QString program = "cdcontrol";
QStringList args;
args << "-f" << devnode << "eject";
#else
QString program = "eject";
QStringList args;
args << devnode;
#endif
m_ejectInProgress = true;
m_device->broadcastActionRequested("eject");
m_process = FstabHandling::callSystemCommand("eject", args,
this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
}
} else if (m_ejectInProgress) {
m_ejectInProgress = false;
m_device->broadcastActionDone("eject");
}
}
void StorageAccess::slotDBusError(const QDBusError &error)
{
// TODO: Better error reporting here
if (m_setupInProgress) {
m_setupInProgress = false;
m_device->broadcastActionDone("setup", Solid::UnauthorizedOperation,
QString(error.name()+": "+error.message()));
} else if (m_teardownInProgress) {
m_teardownInProgress = false;
m_device->broadcastActionDone("teardown", Solid::UnauthorizedOperation,
QString(error.name()+": "+error.message()));
} else if (m_ejectInProgress) {
m_ejectInProgress = false;
m_device->broadcastActionDone("eject", Solid::UnauthorizedOperation,
QString(error.name()+": "+error.message()));
}
}
void Solid::Backends::Hal::StorageAccess::slotProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitStatus);
if (m_setupInProgress) {
m_setupInProgress = false;
if (exitCode==0) {
m_device->broadcastActionDone("setup");
} else {
m_device->broadcastActionDone("setup", Solid::UnauthorizedOperation,
m_process->readAllStandardError());
}
} else if (m_teardownInProgress) {
m_teardownInProgress = false;
if (exitCode==0) {
m_device->broadcastActionDone("teardown");
} else {
m_device->broadcastActionDone("teardown", Solid::UnauthorizedOperation,
m_process->readAllStandardError());
}
} else if (m_ejectInProgress) {
if (exitCode==0) {
m_ejectInProgress = false;
m_device->broadcastActionDone("eject");
} else {
callHalVolumeEject();
}
}
delete m_process;
}
void StorageAccess::slotSetupRequested()
{
m_setupInProgress = true;
emit setupRequested(m_device->udi());
}
void StorageAccess::slotTeardownRequested()
{
m_teardownInProgress = true;
emit teardownRequested(m_device->udi());
}
void StorageAccess::slotEjectRequested()
{
m_ejectInProgress = true;
}
QString generateReturnObjectPath()
{
static int number = 1;
return "/org/kde/solid/HalStorageAccess_"+QString::number(number++);
}
bool StorageAccess::requestPassphrase()
{
QString udi = m_device->udi();
QString returnService = QDBusConnection::sessionBus().baseService();
m_lastReturnObject = generateReturnObjectPath();
QDBusConnection::sessionBus().registerObject(m_lastReturnObject, this,
QDBusConnection::ExportScriptableSlots);
QWidget *activeWindow = QApplication::activeWindow();
uint wId = 0;
if (activeWindow!=nullptr) {
wId = (uint)activeWindow->winId();
}
QString appId = QCoreApplication::applicationName();
QDBusInterface soliduiserver("org.kde.kded", "/modules/soliduiserver", "org.kde.SolidUiServer");
QDBusReply<void> reply = soliduiserver.call("showPassphraseDialog", udi,
returnService, m_lastReturnObject,
wId, appId);
m_passphraseRequested = reply.isValid();
if (!m_passphraseRequested) {
qWarning() << "Failed to call the SolidUiServer, D-Bus said:" << reply.error();
}
return m_passphraseRequested;
}
void StorageAccess::passphraseReply(const QString &passphrase)
{
if (m_passphraseRequested) {
QDBusConnection::sessionBus().unregisterObject(m_lastReturnObject);
m_passphraseRequested = false;
if (!passphrase.isEmpty()) {
callCryptoSetup(passphrase);
} else {
m_setupInProgress = false;
m_device->broadcastActionDone("setup");
}
}
}
bool StorageAccess::callHalVolumeMount()
{
QDBusConnection c = QDBusConnection::systemBus();
QString udi = m_device->udi();
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Hal", udi,
"org.freedesktop.Hal.Device.Volume",
"Mount");
// HAL 0.5.12 supports using alternative drivers for the same filesystem.
// This is mainly used to integrate the ntfs-3g driver.
// Unfortunately, the primary driver gets used unless we
// specify some other driver (fstype) to the Mount method.
// TODO: Allow the user to choose the driver.
QString fstype = m_device->prop("volume.fstype").toString();
QStringList halOptions = m_device->prop("volume.mount.valid_options").toStringList();
QString alternativePreferred = m_device->prop("volume.fstype.alternative.preferred").toString();
if (!alternativePreferred.isEmpty()) {
QStringList alternativeFstypes = m_device->prop("volume.fstype.alternative").toStringList();
if (alternativeFstypes.contains(alternativePreferred)) {
fstype = alternativePreferred;
halOptions = m_device->prop("volume.mount."+fstype+".valid_options").toStringList();
}
}
QStringList options;
#ifdef Q_OS_FREEBSD
QString uid="-u=";
#else
QString uid="uid=";
#endif
if (halOptions.contains(uid)) {
options << uid+QString::number(::getuid());
}
#ifdef Q_OS_FREEBSD
char *cType;
if ( fstype=="vfat" && halOptions.contains("-L=")) {
if ( (cType = getenv("LC_ALL")) || (cType = getenv("LC_CTYPE")) || (cType = getenv("LANG")) )
options << "-L="+QString(cType);
}
else if ( (fstype.startsWith(QLatin1String("ntfs")) || fstype=="iso9660" || fstype=="udf") && halOptions.contains("-C=") ) {
if ((cType = getenv("LC_ALL")) || (cType = getenv("LC_CTYPE")) || (cType = getenv("LANG")) )
options << "-C="+QString(nl_langinfo(CODESET));
}
#else
if (fstype=="vfat" || fstype=="ntfs" || fstype=="iso9660" || fstype=="udf" ) {
if (halOptions.contains("utf8"))
options<<"utf8";
else if (halOptions.contains("iocharset="))
options<<"iocharset=utf8";
if (halOptions.contains("shortname="))
options<<"shortname=mixed";
if (halOptions.contains("flush"))
options<<"flush";
}
// pass our locale to the ntfs-3g driver so it can translate local characters
else if ( halOptions.contains("locale=") ) {
// have to obtain LC_CTYPE as returned by the `locale` command
// check in the same order as `locale` does
char *cType;
if ( (cType = getenv("LC_ALL")) || (cType = getenv("LC_CTYPE")) || (cType = getenv("LANG")) ) {
options << "locale="+QString(cType);
}
}
#endif
msg << "" << fstype << options;
return c.callWithCallback(msg, this,
SLOT(slotDBusReply(QDBusMessage)),
SLOT(slotDBusError(QDBusError)));
}
bool StorageAccess::callHalVolumeUnmount()
{
QDBusConnection c = QDBusConnection::systemBus();
QString udi = m_device->udi();
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Hal", udi,
"org.freedesktop.Hal.Device.Volume",
"Unmount");
msg << QStringList();
return c.callWithCallback(msg, this,
SLOT(slotDBusReply(QDBusMessage)),
SLOT(slotDBusError(QDBusError)));
}
bool StorageAccess::callHalVolumeEject()
{
QString udi = m_device->udi();
QString interface = "org.freedesktop.Hal.Device.Volume";
QDBusConnection c = QDBusConnection::systemBus();
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Hal", udi,
interface, "Eject");
msg << QStringList();
return c.callWithCallback(msg, this,
SLOT(slotDBusReply(QDBusMessage)),
SLOT(slotDBusError(QDBusError)));
}
bool Solid::Backends::Hal::StorageAccess::callSystemMount()
{
const QString device = m_device->prop("block.device").toString();
m_process = FstabHandling::callSystemCommand("mount", device,
this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
return m_process!=nullptr;
}
bool Solid::Backends::Hal::StorageAccess::callSystemUnmount()
{
const QString device = m_device->prop("block.device").toString();
m_process = FstabHandling::callSystemCommand("umount", device,
this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
return m_process!=nullptr;
}
void StorageAccess::callCryptoSetup(const QString &passphrase)
{
QDBusConnection c = QDBusConnection::systemBus();
QString udi = m_device->udi();
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Hal", udi,
"org.freedesktop.Hal.Device.Volume.Crypto",
"Setup");
msg << passphrase;
c.callWithCallback(msg, this,
SLOT(slotDBusReply(QDBusMessage)),
SLOT(slotDBusError(QDBusError)));
}
bool StorageAccess::callCryptoTeardown()
{
QDBusConnection c = QDBusConnection::systemBus();
QString udi = m_device->udi();
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Hal", udi,
"org.freedesktop.Hal.Device.Volume.Crypto",
"Teardown");
return c.callWithCallback(msg, this,
SLOT(slotDBusReply(QDBusMessage)),
SLOT(slotDBusError(QDBusError)));
}
#include "backends/hal/moc_halstorageaccess.cpp"