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