1/*
2 SPDX-FileCopyrightText: 2009 Pino Toscano <pino@kde.org>
3 SPDX-FileCopyrightText: 2009-2012 Lukáš Tinkl <ltinkl@redhat.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "udisksstorageaccess.h"
9#include "udisks2.h"
10#include "udisks_debug.h"
11
12#include <QDBusConnection>
13#include <QDBusInterface>
14#include <QDBusMetaType>
15#include <QDir>
16#include <QGuiApplication>
17#include <QWindow>
18
19#include <config-solid.h>
20#if HAVE_LIBMOUNT
21#include <libmount.h>
22#endif
23
24struct AvailableAnswer {
25 bool checkResult;
26 QString binaryName;
27};
28Q_DECLARE_METATYPE(AvailableAnswer)
29
30QDBusArgument &operator<<(QDBusArgument &argument, const AvailableAnswer &answer)
31{
32 argument.beginStructure();
33 argument << answer.checkResult << answer.binaryName;
34 argument.endStructure();
35 return argument;
36}
37
38const QDBusArgument &operator>>(const QDBusArgument &argument, AvailableAnswer &answer)
39{
40 argument.beginStructure();
41 argument >> answer.checkResult >> answer.binaryName;
42 argument.endStructure();
43 return argument;
44}
45
46using namespace Solid::Backends::UDisks2;
47
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)
55{
56 qDBusRegisterMetaType<AvailableAnswer>();
57
58 connect(sender: device, SIGNAL(changed()), receiver: this, SLOT(checkAccessibility()));
59 updateCache();
60
61 // Delay connecting to DBus signals to avoid the related time penalty
62 // in hot paths such as predicate matching
63 QTimer::singleShot(msec: 0, receiver: this, SLOT(connectDBusSignals()));
64}
65
66StorageAccess::~StorageAccess()
67{
68}
69
70void StorageAccess::connectDBusSignals()
71{
72 m_device->registerAction(QStringLiteral("setup"), dest: this, SLOT(slotSetupRequested()), SLOT(slotSetupDone(int, QString)));
73
74 m_device->registerAction(QStringLiteral("teardown"), dest: this, SLOT(slotTeardownRequested()), SLOT(slotTeardownDone(int, QString)));
75
76 m_device->registerAction(QStringLiteral("check"), dest: this, SLOT(slotCheckRequested()), SLOT(slotCheckDone(int, QString)));
77
78 m_device->registerAction(QStringLiteral("repair"), dest: this, SLOT(slotRepairRequested()), SLOT(slotRepairDone(int, QString)));
79}
80
81bool StorageAccess::isLuksDevice() const
82{
83 return m_device->isEncryptedContainer(); // encrypted device
84}
85
86bool StorageAccess::isAccessible() const
87{
88 if (isLuksDevice()) { // check if the cleartext slave is mounted
89 const QString path = clearTextPath();
90 // qDebug() << Q_FUNC_INFO << "CLEARTEXT device path: " << path;
91 if (path.isEmpty() || path == QLatin1String("/")) {
92 return false;
93 }
94 Device holderDevice(path);
95 return holderDevice.isMounted();
96 }
97
98 return m_device->isMounted();
99}
100
101bool StorageAccess::isEncrypted() const
102{
103 // FIXME We should also check if physical device is encrypted
104 // FIXME Gocryptfs is not supported
105 return isLuksDevice() || m_device->isEncryptedCleartext();
106}
107
108bool StorageAccess::canCheck() const
109{
110 const auto idType = m_device->prop(QStringLiteral("IdType")).toString();
111 auto c = QDBusConnection::systemBus();
112 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
113 QStringLiteral(UD2_DBUS_PATH_MANAGER),
114 QStringLiteral("org.freedesktop.UDisks2.Manager"),
115 QStringLiteral("CanCheck"));
116 msg << idType;
117 QDBusReply<AvailableAnswer> r = c.call(message: msg);
118 if (!r.isValid()) {
119 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << "DBus error, code" << r.error().type();
120 return false;
121 }
122
123 const bool ret = r.value().checkResult;
124 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
125 return ret;
126}
127
128bool StorageAccess::check()
129{
130 if (m_setupInProgress || m_teardownInProgress || m_checkInProgress || m_repairInProgress) {
131 return false;
132 }
133 m_checkInProgress = true;
134 m_device->broadcastActionRequested(QStringLiteral("check"));
135
136 const auto path = dbusPath();
137 auto c = QDBusConnection::systemBus();
138 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Check"));
139 msg << QVariantMap{};
140
141 return c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
142}
143
144bool StorageAccess::canRepair() const
145{
146 const auto idType = m_device->prop(QStringLiteral("IdType")).toString();
147 auto c = QDBusConnection::systemBus();
148 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
149 QStringLiteral(UD2_DBUS_PATH_MANAGER),
150 QStringLiteral("org.freedesktop.UDisks2.Manager"),
151 QStringLiteral("CanRepair"));
152 msg << idType;
153 QDBusReply<AvailableAnswer> r = c.call(message: msg);
154 if (!r.isValid()) {
155 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << "DBus error, code" << r.error().type();
156 return false;
157 }
158
159 const bool ret = r.value().checkResult;
160 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
161 return ret;
162}
163
164bool StorageAccess::repair()
165{
166 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
167 return false;
168 }
169 m_repairInProgress = true;
170 m_device->broadcastActionRequested(QStringLiteral("repair"));
171
172 const auto path = dbusPath();
173 auto c = QDBusConnection::systemBus();
174 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Repair"));
175 QVariantMap options;
176 msg << options;
177
178 qCDebug(UDISKS2) << Q_FUNC_INFO << path;
179 return c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
180}
181
182static QString baseMountPoint(const QByteArray &dev)
183{
184 QString mountPoint;
185
186#if HAVE_LIBMOUNT
187 // UDisks "MountPoints" property contains multiple paths, this happens with
188 // devices with bind mounts; try finding the "base" mount point
189 if (struct libmnt_table *table = mnt_new_table()) {
190 // This parses "/etc/mtab" if present or "/proc/self/mountinfo" by default
191 if (mnt_table_parse_mtab(tb: table, filename: "/proc/self/mountinfo") == 0) {
192 // BACKWARD because the fs's we're interested in, /dev/sdXY, are typically at the end
193 struct libmnt_iter *itr = mnt_new_iter(direction: MNT_ITER_BACKWARD);
194 struct libmnt_fs *fs;
195
196 const QByteArray devicePath = dev.endsWith(c: '\x00') ? dev.chopped(len: 1) : dev;
197
198 while (mnt_table_next_fs(tb: table, itr, fs: &fs) == 0) {
199 if (mnt_fs_get_srcpath(fs) == devicePath //
200 && (qstrcmp(str1: mnt_fs_get_root(fs), str2: "/") == 0) // Base mount point will have "/" as root fs
201 ) {
202 mountPoint = QFile::decodeName(localFileName: mnt_fs_get_target(fs));
203 break;
204 }
205 }
206
207 mnt_free_iter(itr);
208 }
209
210 mnt_free_table(tb: table);
211 }
212#else
213 Q_UNUSED(dev);
214#endif
215
216 return mountPoint;
217}
218
219QString StorageAccess::filePath() const
220{
221 if (isLuksDevice()) { // encrypted (and unlocked) device
222 const QString path = clearTextPath();
223 if (path.isEmpty() || path == QLatin1String("/")) {
224 return QString();
225 }
226 Device holderDevice(path);
227 const auto mntPoints = qdbus_cast<QByteArrayList>(v: holderDevice.prop(QStringLiteral("MountPoints")));
228 if (!mntPoints.isEmpty()) {
229 QByteArray first = mntPoints.first();
230 if (first.endsWith(c: '\x00')) {
231 first.chop(n: 1);
232 }
233 return QFile::decodeName(localFileName: first); // FIXME Solid doesn't support multiple mount points
234 } else {
235 return QString();
236 }
237 }
238
239 const auto mntPoints = qdbus_cast<QByteArrayList>(v: m_device->prop(QStringLiteral("MountPoints")));
240 if (mntPoints.isEmpty()) {
241 return {};
242 }
243
244 QByteArray first = mntPoints.first();
245 if (first.endsWith(c: '\x00')) {
246 first.chop(n: 1);
247 }
248 const QString potentialMountPoint = QFile::decodeName(localFileName: first);
249
250 if (mntPoints.size() == 1) {
251 return potentialMountPoint;
252 }
253
254 // Device has bind mounts?
255 const QString basePoint = baseMountPoint(dev: m_device->prop(QStringLiteral("Device")).toByteArray());
256
257 return !basePoint.isEmpty() ? basePoint : potentialMountPoint;
258}
259
260bool StorageAccess::isIgnored() const
261{
262 if (m_device->prop(QStringLiteral("HintIgnore")).toBool()) {
263 return true;
264 }
265
266 const QStringList mountOptions = m_device->prop(QStringLiteral("UserspaceMountOptions")).toStringList();
267 if (mountOptions.contains(str: QLatin1String("x-gdu.hide"))) {
268 return true;
269 }
270
271 const QString path = filePath();
272
273 const bool inUserPath = (path.startsWith(s: QLatin1String("/media/")) //
274 || path.startsWith(s: QLatin1String("/run/media/")) //
275 || path.startsWith(s: QDir::homePath()));
276 return !inUserPath;
277}
278
279bool StorageAccess::setup()
280{
281 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
282 return false;
283 }
284 m_setupInProgress = true;
285 m_device->broadcastActionRequested(QStringLiteral("setup"));
286
287 if (m_device->isEncryptedContainer() && clearTextPath().isEmpty()) {
288 return requestPassphrase();
289 } else {
290 return mount();
291 }
292}
293
294bool StorageAccess::teardown()
295{
296 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
297 return false;
298 }
299 m_teardownInProgress = true;
300 m_device->broadcastActionRequested(QStringLiteral("teardown"));
301
302 return unmount();
303}
304
305void StorageAccess::updateCache()
306{
307 m_isAccessible = isAccessible();
308}
309
310void StorageAccess::checkAccessibility()
311{
312 const bool old_isAccessible = m_isAccessible;
313 updateCache();
314
315 if (old_isAccessible != m_isAccessible) {
316 Q_EMIT accessibilityChanged(accessible: m_isAccessible, udi: m_device->udi());
317 }
318}
319
320void StorageAccess::slotDBusReply(const QDBusMessage &reply)
321{
322 if (m_setupInProgress) {
323 if (isLuksDevice() && !isAccessible()) { // unlocked device, now mount it
324 mount();
325 } else { // Don't broadcast setupDone unless the setup is really done. (Fix kde#271156)
326 m_setupInProgress = false;
327 m_device->invalidateCache();
328 m_device->broadcastActionDone(QStringLiteral("setup"));
329
330 checkAccessibility();
331 }
332 } else if (m_teardownInProgress) { // FIXME
333 const QString ctPath = clearTextPath();
334 qCDebug(UDISKS2) << "Successfully unmounted " << m_device->udi();
335 if (isLuksDevice() && !ctPath.isEmpty() && ctPath != QStringLiteral("/")) { // unlocked device, lock it
336 callCryptoTeardown();
337 } else if (!ctPath.isEmpty() && ctPath != QStringLiteral("/")) {
338 callCryptoTeardown(actOnParent: true); // Lock encrypted parent
339 } else {
340 // try to "eject" (aka safely remove) from the (parent) drive, e.g. SD card from a reader
341 QString drivePath = m_device->drivePath();
342 if (!drivePath.isEmpty() || drivePath != QStringLiteral("/")) {
343 Device drive(drivePath);
344 QDBusConnection c = QDBusConnection::systemBus();
345
346 if (drive.prop(QStringLiteral("MediaRemovable")).toBool() //
347 && drive.prop(QStringLiteral("MediaAvailable")).toBool() //
348 && !m_device->isOpticalDisc()) { // optical drives have their Eject method
349 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
350 path: drivePath,
351 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
352 QStringLiteral("Eject"));
353 msg << QVariantMap(); // options, unused now
354 c.call(message: msg, mode: QDBus::NoBlock);
355 } else if (drive.prop(QStringLiteral("CanPowerOff")).toBool() //
356 && !m_device->isOpticalDisc()) { // avoid disconnecting optical drives from the bus
357 qCDebug(UDISKS2) << "Drive can power off:" << drivePath;
358 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
359 path: drivePath,
360 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
361 QStringLiteral("PowerOff"));
362 msg << QVariantMap(); // options, unused now
363 c.call(message: msg, mode: QDBus::NoBlock);
364 }
365 }
366
367 m_teardownInProgress = false;
368 m_device->invalidateCache();
369 m_device->broadcastActionDone(QStringLiteral("teardown"));
370
371 checkAccessibility();
372 }
373 } else if (m_checkInProgress) {
374 QDBusReply<bool> r = reply;
375 qCDebug(UDISKS2) << "Check reply received " << m_device->udi() << r;
376 m_checkInProgress = false;
377 if (r.isValid()) {
378 m_device->broadcastActionDone(QStringLiteral("check"), error: Solid::NoError, errorString: QString::number(r.value()));
379 } else {
380 m_device->broadcastActionDone(QStringLiteral("check"), error: Solid::OperationFailed, errorString: reply.errorMessage());
381 }
382 } else if (m_repairInProgress) {
383 qCDebug(UDISKS2) << "Successfully repaired " << m_device->udi();
384 m_repairInProgress = false;
385 m_device->broadcastActionDone(QStringLiteral("repair"));
386 }
387}
388
389void StorageAccess::slotDBusError(const QDBusError &error)
390{
391 // qDebug() << Q_FUNC_INFO << "DBUS ERROR:" << error.name() << error.message();
392
393 if (m_setupInProgress) {
394 m_setupInProgress = false;
395 m_device->broadcastActionDone(QStringLiteral("setup"), //
396 error: m_device->errorToSolidError(error: error.name()),
397 errorString: m_device->errorToString(error: error.name()) + QStringLiteral(": ") + error.message());
398
399 checkAccessibility();
400 } else if (m_teardownInProgress) {
401 m_teardownInProgress = false;
402 m_device->broadcastActionDone(QStringLiteral("teardown"), //
403 error: m_device->errorToSolidError(error: error.name()),
404 errorString: m_device->errorToString(error: error.name()) + QStringLiteral(": ") + error.message());
405 checkAccessibility();
406 } else if (m_checkInProgress) {
407 m_checkInProgress = false;
408 m_device->broadcastActionDone(QStringLiteral("check"),
409 error: m_device->errorToSolidError(error: error.name()),
410 errorString: m_device->errorToString(error: error.name()) + QStringLiteral(": ") + error.message());
411 } else if (m_repairInProgress) {
412 m_repairInProgress = false;
413 m_device->broadcastActionDone(QStringLiteral("repair"),
414 error: m_device->errorToSolidError(error: error.name()),
415 errorString: m_device->errorToString(error: error.name()) + QStringLiteral(": ") + error.message());
416 }
417}
418
419void StorageAccess::slotSetupRequested()
420{
421 m_setupInProgress = true;
422 // qDebug() << "SETUP REQUESTED:" << m_device->udi();
423 Q_EMIT setupRequested(m_device->udi());
424}
425
426void StorageAccess::slotSetupDone(int error, const QString &errorString)
427{
428 m_setupInProgress = false;
429 // qDebug() << "SETUP DONE:" << m_device->udi();
430 checkAccessibility();
431 Q_EMIT setupDone(error: static_cast<Solid::ErrorType>(error), errorData: errorString, udi: m_device->udi());
432}
433
434void StorageAccess::slotTeardownRequested()
435{
436 m_teardownInProgress = true;
437 Q_EMIT teardownRequested(udi: m_device->udi());
438}
439
440void StorageAccess::slotTeardownDone(int error, const QString &errorString)
441{
442 m_teardownInProgress = false;
443 checkAccessibility();
444 Q_EMIT teardownDone(error: static_cast<Solid::ErrorType>(error), errorData: errorString, udi: m_device->udi());
445}
446
447void StorageAccess::slotCheckRequested()
448{
449 m_checkInProgress = true;
450 Q_EMIT checkRequested(udi: m_device->udi());
451}
452
453void StorageAccess::slotCheckDone(int error, const QString &errorString)
454{
455 m_checkInProgress = false;
456 Q_EMIT checkDone(error: static_cast<Solid::ErrorType>(error), errorData: errorString, udi: m_device->udi());
457}
458
459void StorageAccess::slotRepairRequested()
460{
461 m_repairInProgress = true;
462 Q_EMIT repairRequested(udi: m_device->udi());
463}
464
465void StorageAccess::slotRepairDone(int error, const QString &errorString)
466{
467 m_repairInProgress = false;
468 Q_EMIT repairDone(error: static_cast<Solid::ErrorType>(error), errorData: errorString, udi: m_device->udi());
469}
470
471bool StorageAccess::mount()
472{
473 const auto path = dbusPath();
474
475 QDBusConnection c = QDBusConnection::systemBus();
476 QDBusMessage msg =
477 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Mount"));
478 QVariantMap options;
479
480 if (m_device->prop(QStringLiteral("IdType")).toString() == QLatin1String("vfat")) {
481 options.insert(QStringLiteral("options"), QStringLiteral("flush"));
482 }
483
484 msg << options;
485
486 return c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
487}
488
489bool StorageAccess::unmount()
490{
491 const auto path = dbusPath();
492
493 QDBusConnection c = QDBusConnection::systemBus();
494 QDBusMessage msg =
495 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Unmount"));
496
497 msg << QVariantMap(); // options, unused now
498
499 qCDebug(UDISKS2) << "Initiating unmount of " << path;
500 return c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)), timeout: s_unmountTimeout);
501}
502
503QString StorageAccess::generateReturnObjectPath()
504{
505 static QAtomicInt number = 1;
506
507 return QStringLiteral("/org/kde/solid/UDisks2StorageAccess_") + QString::number(number++);
508}
509
510QString StorageAccess::clearTextPath() const
511{
512 const QString path = m_device->prop(QStringLiteral("CleartextDevice")).value<QDBusObjectPath>().path();
513 if (path != QLatin1String("/")) {
514 return path;
515 }
516 return QString();
517}
518
519QString StorageAccess::dbusPath() const
520{
521 QString path = m_device->udi();
522 if (isLuksDevice()) { // mount options for the cleartext volume
523 const QString ctPath = clearTextPath();
524 if (!ctPath.isEmpty()) {
525 path = ctPath;
526 }
527 }
528 return path;
529}
530
531bool StorageAccess::requestPassphrase()
532{
533 QString udi = m_device->udi();
534 QString returnService = QDBusConnection::sessionBus().baseService();
535 m_lastReturnObject = generateReturnObjectPath();
536
537 QDBusConnection::sessionBus().registerObject(path: m_lastReturnObject, object: this, options: QDBusConnection::ExportScriptableSlots);
538
539 // TODO: this only works on X11, Wayland doesn't have global window ids.
540 // Passing ids to other processes doesn't make any sense
541 auto activeWindow = QGuiApplication::focusWindow();
542 uint wId = 0;
543 if (activeWindow != nullptr) {
544 wId = (uint)activeWindow->winId();
545 }
546
547 QString appId = QCoreApplication::applicationName();
548
549 const auto plasmaVersionMajor = qEnvironmentVariable(varName: "KDE_SESSION_VERSION", QStringLiteral("6"));
550
551 // TODO KF6: remove hard dep on Plasma here which provides the SolidUiServer kded plugin
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"), args&: udi, args&: returnService, args&: m_lastReturnObject, args&: wId, args&: appId);
556 m_passphraseRequested = reply.isValid();
557 if (!m_passphraseRequested) {
558 qCWarning(UDISKS2) << "Failed to call the SolidUiServer, D-Bus said:" << reply.error();
559 }
560
561 return m_passphraseRequested;
562}
563
564void StorageAccess::passphraseReply(const QString &passphrase)
565{
566 if (m_passphraseRequested) {
567 QDBusConnection::sessionBus().unregisterObject(path: m_lastReturnObject);
568 m_passphraseRequested = false;
569 if (!passphrase.isEmpty()) {
570 callCryptoSetup(passphrase);
571 } else {
572 m_setupInProgress = false;
573 m_device->broadcastActionDone(QStringLiteral("setup"), error: Solid::UserCanceled);
574 }
575 }
576}
577
578void StorageAccess::callCryptoSetup(const QString &passphrase)
579{
580 QDBusConnection c = QDBusConnection::systemBus();
581 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
582 path: m_device->udi(),
583 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
584 QStringLiteral("Unlock"));
585
586 msg << passphrase;
587 msg << QVariantMap(); // options, unused now
588
589 c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
590}
591
592bool StorageAccess::callCryptoTeardown(bool actOnParent)
593{
594 QDBusConnection c = QDBusConnection::systemBus();
595 QDBusMessage msg =
596 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
597 path: actOnParent ? (m_device->prop(QStringLiteral("CryptoBackingDevice")).value<QDBusObjectPath>().path()) : m_device->udi(),
598 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
599 QStringLiteral("Lock"));
600 msg << QVariantMap(); // options, unused now
601
602 return c.callWithCallback(message: msg, receiver: this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
603}
604
605#include "moc_udisksstorageaccess.cpp"
606

source code of solid/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp