8#include "udisksstorageaccess.h"
10#include "udisks_debug.h"
12#include <QDBusConnection>
13#include <QDBusInterface>
14#include <QDBusMetaType>
16#include <QGuiApplication>
19#include <config-solid.h>
24struct AvailableAnswer {
28Q_DECLARE_METATYPE(AvailableAnswer)
33 argument << answer.checkResult << answer.binaryName;
41 argument >> answer.checkResult >> answer.binaryName;
46using namespace Solid::Backends::UDisks2;
48StorageAccess::StorageAccess(Device *device)
49 : DeviceInterface(device)
50 , m_setupInProgress(false)
51 , m_teardownInProgress(false)
52 , m_checkInProgress(false)
53 , m_repairInProgress(false)
54 , m_passphraseRequested(false)
56 qDBusRegisterMetaType<AvailableAnswer>();
58 connect(device, SIGNAL(changed()),
this, SLOT(checkAccessibility()));
66StorageAccess::~StorageAccess()
70void StorageAccess::connectDBusSignals()
72 m_device->registerAction(QStringLiteral(
"setup"),
this, SLOT(slotSetupRequested()), SLOT(slotSetupDone(
int, QString)));
74 m_device->registerAction(QStringLiteral(
"teardown"),
this, SLOT(slotTeardownRequested()), SLOT(slotTeardownDone(
int, QString)));
76 m_device->registerAction(QStringLiteral(
"check"),
this, SLOT(slotCheckRequested()), SLOT(slotCheckDone(
int, QString)));
78 m_device->registerAction(QStringLiteral(
"repair"),
this, SLOT(slotRepairRequested()), SLOT(slotRepairDone(
int, QString)));
81bool StorageAccess::isLuksDevice()
const
83 return m_device->isEncryptedContainer();
86bool StorageAccess::isAccessible()
const
89 const QString
path = clearTextPath();
94 Device holderDevice(path);
95 return holderDevice.isMounted();
98 return m_device->isMounted();
101bool StorageAccess::isEncrypted()
const
105 return isLuksDevice() || m_device->isEncryptedCleartext();
108bool StorageAccess::canCheck()
const
110 const auto idType = m_device->prop(QStringLiteral(
"IdType")).toString();
113 QStringLiteral(UD2_DBUS_PATH_MANAGER),
114 QStringLiteral(
"org.freedesktop.UDisks2.Manager"),
115 QStringLiteral(
"CanCheck"));
117 QDBusReply<AvailableAnswer> r = c.call(msg);
119 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType <<
"DBus error, code" << r.
error().
type();
123 const bool ret = r.value().checkResult;
124 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
128bool StorageAccess::check()
130 if (m_setupInProgress || m_teardownInProgress || m_checkInProgress || m_repairInProgress) {
133 m_checkInProgress =
true;
134 m_device->broadcastActionRequested(QStringLiteral(
"check"));
136 const auto path = dbusPath();
138 auto msg =
QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral(
"Check"));
139 msg << QVariantMap{};
141 return c.callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
144bool StorageAccess::canRepair()
const
146 const auto idType = m_device->prop(QStringLiteral(
"IdType")).toString();
149 QStringLiteral(UD2_DBUS_PATH_MANAGER),
150 QStringLiteral(
"org.freedesktop.UDisks2.Manager"),
151 QStringLiteral(
"CanRepair"));
153 QDBusReply<AvailableAnswer> r = c.call(msg);
155 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType <<
"DBus error, code" << r.
error().
type();
159 const bool ret = r.value().checkResult;
160 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
164bool StorageAccess::repair()
166 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
169 m_repairInProgress =
true;
170 m_device->broadcastActionRequested(QStringLiteral(
"repair"));
172 const auto path = dbusPath();
174 auto msg =
QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral(
"Repair"));
178 qCDebug(UDISKS2) << Q_FUNC_INFO <<
path;
179 return c.callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
182static QString baseMountPoint(
const QByteArray &dev)
189 if (
struct libmnt_table *table = mnt_new_table()) {
191 if (mnt_table_parse_mtab(table,
"/proc/self/mountinfo") == 0) {
193 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
194 struct libmnt_fs *fs;
196 const QByteArray devicePath = dev.
endsWith(
'\x00') ? dev.
chopped(1) : dev;
198 while (mnt_table_next_fs(table, itr, &fs) == 0) {
199 if (mnt_fs_get_srcpath(fs) == devicePath
200 && (qstrcmp(mnt_fs_get_root(fs),
"/") == 0)
210 mnt_free_table(table);
219QString StorageAccess::filePath()
const
221 if (isLuksDevice()) {
222 const QString
path = clearTextPath();
223 if (
path.
isEmpty() || path == QLatin1String(
"/")) {
226 Device holderDevice(path);
227 const auto mntPoints = qdbus_cast<QByteArrayList>(holderDevice.prop(QStringLiteral(
"MountPoints")));
228 if (!mntPoints.isEmpty()) {
229 QByteArray first = mntPoints.first();
239 const auto mntPoints = qdbus_cast<QByteArrayList>(m_device->prop(QStringLiteral(
"MountPoints")));
240 if (mntPoints.isEmpty()) {
244 QByteArray first = mntPoints.
first();
250 if (mntPoints.size() == 1) {
251 return potentialMountPoint;
255 const QString basePoint = baseMountPoint(m_device->prop(QStringLiteral(
"Device")).toByteArray());
257 return !basePoint.
isEmpty() ? basePoint : potentialMountPoint;
260bool StorageAccess::isIgnored()
const
262 if (m_device->prop(QStringLiteral(
"HintIgnore")).toBool()) {
266 const QStringList mountOptions = m_device->prop(QStringLiteral(
"UserspaceMountOptions")).toStringList();
267 if (mountOptions.
contains(QLatin1String(
"x-gdu.hide"))) {
271 const QString
path = filePath();
273 const bool inUserPath = (
path.
startsWith(QLatin1String(
"/media/"))
279bool StorageAccess::setup()
281 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
284 m_setupInProgress =
true;
285 m_device->broadcastActionRequested(QStringLiteral(
"setup"));
287 if (m_device->isEncryptedContainer() && clearTextPath().isEmpty()) {
288 return requestPassphrase();
294bool StorageAccess::teardown()
296 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
299 m_teardownInProgress =
true;
300 m_device->broadcastActionRequested(QStringLiteral(
"teardown"));
305void StorageAccess::updateCache()
307 m_isAccessible = isAccessible();
310void StorageAccess::checkAccessibility()
312 const bool old_isAccessible = m_isAccessible;
315 if (old_isAccessible != m_isAccessible) {
316 Q_EMIT accessibilityChanged(m_isAccessible, m_device->udi());
320void StorageAccess::slotDBusReply(
const QDBusMessage &reply)
322 if (m_setupInProgress) {
323 if (isLuksDevice() && !isAccessible()) {
326 m_setupInProgress =
false;
327 m_device->invalidateCache();
328 m_device->broadcastActionDone(QStringLiteral(
"setup"));
330 checkAccessibility();
332 }
else if (m_teardownInProgress) {
333 const QString ctPath = clearTextPath();
334 qCDebug(UDISKS2) <<
"Successfully unmounted " << m_device->udi();
335 if (isLuksDevice() && !ctPath.
isEmpty() && ctPath != QStringLiteral(
"/")) {
336 callCryptoTeardown();
337 }
else if (!ctPath.
isEmpty() && ctPath != QStringLiteral(
"/")) {
338 callCryptoTeardown(
true);
341 QString drivePath = m_device->drivePath();
342 if (!drivePath.
isEmpty() || drivePath != QStringLiteral(
"/")) {
343 Device drive(drivePath);
346 if (drive.prop(QStringLiteral(
"MediaRemovable")).toBool()
347 && drive.prop(QStringLiteral(
"MediaAvailable")).toBool()
348 && !m_device->isOpticalDisc()) {
351 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
352 QStringLiteral(
"Eject"));
353 msg << QVariantMap();
355 }
else if (drive.prop(QStringLiteral(
"CanPowerOff")).toBool()
356 && !m_device->isOpticalDisc()) {
357 qCDebug(UDISKS2) <<
"Drive can power off:" << drivePath;
360 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
361 QStringLiteral(
"PowerOff"));
362 msg << QVariantMap();
367 m_teardownInProgress =
false;
368 m_device->invalidateCache();
369 m_device->broadcastActionDone(QStringLiteral(
"teardown"));
371 checkAccessibility();
373 }
else if (m_checkInProgress) {
374 QDBusReply<bool> r = reply;
375 qCDebug(UDISKS2) <<
"Check reply received " << m_device->udi() << r;
376 m_checkInProgress =
false;
378 m_device->broadcastActionDone(QStringLiteral(
"check"), Solid::NoError,
QString::number(r.value()));
380 m_device->broadcastActionDone(QStringLiteral(
"check"), Solid::OperationFailed, reply.
errorMessage());
382 }
else if (m_repairInProgress) {
383 qCDebug(UDISKS2) <<
"Successfully repaired " << m_device->udi();
384 m_repairInProgress =
false;
385 m_device->broadcastActionDone(QStringLiteral(
"repair"));
389void StorageAccess::slotDBusError(
const QDBusError &error)
393 if (m_setupInProgress) {
394 m_setupInProgress =
false;
395 m_device->broadcastActionDone(QStringLiteral(
"setup"),
396 m_device->errorToSolidError(
error.name()),
397 m_device->errorToString(
error.name()) + QStringLiteral(
": ") +
error.message());
399 checkAccessibility();
400 }
else if (m_teardownInProgress) {
401 m_teardownInProgress =
false;
402 m_device->broadcastActionDone(QStringLiteral(
"teardown"),
403 m_device->errorToSolidError(
error.name()),
404 m_device->errorToString(
error.name()) + QStringLiteral(
": ") +
error.message());
405 checkAccessibility();
406 }
else if (m_checkInProgress) {
407 m_checkInProgress =
false;
408 m_device->broadcastActionDone(QStringLiteral(
"check"),
409 m_device->errorToSolidError(
error.name()),
410 m_device->errorToString(
error.name()) + QStringLiteral(
": ") +
error.message());
411 }
else if (m_repairInProgress) {
412 m_repairInProgress =
false;
413 m_device->broadcastActionDone(QStringLiteral(
"repair"),
414 m_device->errorToSolidError(
error.name()),
415 m_device->errorToString(
error.name()) + QStringLiteral(
": ") +
error.message());
419void StorageAccess::slotSetupRequested()
421 m_setupInProgress =
true;
423 Q_EMIT setupRequested(m_device->udi());
426void StorageAccess::slotSetupDone(
int error,
const QString &errorString)
428 m_setupInProgress =
false;
430 checkAccessibility();
431 Q_EMIT setupDone(
static_cast<Solid::ErrorType
>(error), errorString, m_device->udi());
434void StorageAccess::slotTeardownRequested()
436 m_teardownInProgress =
true;
437 Q_EMIT teardownRequested(m_device->udi());
440void StorageAccess::slotTeardownDone(
int error,
const QString &errorString)
442 m_teardownInProgress =
false;
443 checkAccessibility();
444 Q_EMIT teardownDone(
static_cast<Solid::ErrorType
>(error), errorString, m_device->udi());
447void StorageAccess::slotCheckRequested()
449 m_checkInProgress =
true;
450 Q_EMIT checkRequested(m_device->udi());
453void StorageAccess::slotCheckDone(
int error,
const QString &errorString)
455 m_checkInProgress =
false;
456 Q_EMIT checkDone(
static_cast<Solid::ErrorType
>(error), errorString, m_device->udi());
459void StorageAccess::slotRepairRequested()
461 m_repairInProgress =
true;
462 Q_EMIT repairRequested(m_device->udi());
465void StorageAccess::slotRepairDone(
int error,
const QString &errorString)
467 m_repairInProgress =
false;
468 Q_EMIT repairDone(
static_cast<Solid::ErrorType
>(error), errorString, m_device->udi());
471bool StorageAccess::mount()
473 const auto path = dbusPath();
480 if (m_device->prop(QStringLiteral(
"IdType")).
toString() == QLatin1String(
"vfat")) {
481 options.insert(QStringLiteral(
"options"), QStringLiteral(
"flush"));
486 return c.
callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
489bool StorageAccess::unmount()
491 const auto path = dbusPath();
497 msg << QVariantMap();
499 qCDebug(UDISKS2) <<
"Initiating unmount of " <<
path;
500 return c.
callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)), s_unmountTimeout);
503QString StorageAccess::generateReturnObjectPath()
505 static QAtomicInt
number = 1;
507 return QStringLiteral(
"/org/kde/solid/UDisks2StorageAccess_") +
QString::number(number++);
510QString StorageAccess::clearTextPath()
const
512 const QString
path = m_device->prop(QStringLiteral(
"CleartextDevice")).value<QDBusObjectPath>().
path();
513 if (path != QLatin1String(
"/")) {
519QString StorageAccess::dbusPath()
const
521 QString
path = m_device->udi();
522 if (isLuksDevice()) {
523 const QString ctPath = clearTextPath();
531bool StorageAccess::requestPassphrase()
533 QString udi = m_device->udi();
535 m_lastReturnObject = generateReturnObjectPath();
543 if (activeWindow !=
nullptr) {
544 wId = (uint)activeWindow->winId();
549 const auto plasmaVersionMajor = qEnvironmentVariable(
"KDE_SESSION_VERSION", QStringLiteral(
"6"));
552 QDBusInterface soliduiserver(QStringLiteral(
"org.kde.kded") + plasmaVersionMajor,
553 QStringLiteral(
"/modules/soliduiserver"),
554 QStringLiteral(
"org.kde.SolidUiServer"));
555 QDBusReply<void> reply = soliduiserver.call(QStringLiteral(
"showPassphraseDialog"), udi, returnService, m_lastReturnObject, wId, appId);
556 m_passphraseRequested = reply.
isValid();
557 if (!m_passphraseRequested) {
558 qCWarning(UDISKS2) <<
"Failed to call the SolidUiServer, D-Bus said:" << reply.
error();
561 return m_passphraseRequested;
564void StorageAccess::passphraseReply(
const QString &passphrase)
566 if (m_passphraseRequested) {
568 m_passphraseRequested =
false;
570 callCryptoSetup(passphrase);
572 m_setupInProgress =
false;
573 m_device->broadcastActionDone(QStringLiteral(
"setup"), Solid::UserCanceled);
578void StorageAccess::callCryptoSetup(
const QString &passphrase)
583 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
584 QStringLiteral(
"Unlock"));
587 msg << QVariantMap();
589 c.
callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
592bool StorageAccess::callCryptoTeardown(
bool actOnParent)
597 actOnParent ? (m_device->prop(QStringLiteral(
"CryptoBackingDevice")).value<QDBusObjectPath>().
path()) : m_device->udi(),
598 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
599 QStringLiteral(
"Lock"));
600 msg << QVariantMap();
602 return c.
callWithCallback(msg,
this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
605#include "moc_udisksstorageaccess.cpp"
char * toString(const EngineQuery &query)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QByteArray chopped(qsizetype len) const const
bool endsWith(QByteArrayView bv) const const
QByteArray first(qsizetype n) const const
QString baseService() const const
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const const
bool callWithCallback(const QDBusMessage &message, QObject *receiver, const char *returnMethod, const char *errorMethod, int timeout) const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
QDBusConnection systemBus()
void unregisterObject(const QString &path, UnregisterMode mode)
ErrorType type() const const
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
QString errorMessage() const const
const QDBusError & error()
bool isValid() const const
QString decodeName(const QByteArray &localFileName)
bool isEmpty() const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)