File indexing completed on 2024-04-21 05:48:34

0001 /*
0002     SPDX-FileCopyrightText: 2016 ROSA
0003     SPDX-License-Identifier: GPL-3.0-or-later
0004 */
0005 
0006 ////////////////////////////////////////////////////////////////////////////////
0007 // Implementation of PhysicalDevice
0008 
0009 #include <KLocalizedString>
0010 
0011 #include "physicaldevice.h"
0012 
0013 #include <QtDBus/QDBusArgument>
0014 #include <QtDBus/QDBusReply>
0015 #include <QtDBus/QDBusInterface>
0016 #include <QtDBus/QDBusUnixFileDescriptor>
0017 #include <fcntl.h>
0018 
0019 typedef QHash<QString, QVariant> Properties;
0020 typedef QHash<QString, Properties> InterfacesAndProperties;
0021 typedef QHash<QDBusObjectPath, InterfacesAndProperties> DBusIntrospection;
0022 Q_DECLARE_METATYPE(Properties)
0023 Q_DECLARE_METATYPE(InterfacesAndProperties)
0024 Q_DECLARE_METATYPE(DBusIntrospection)
0025 
0026 PhysicalDevice::PhysicalDevice(const QString& name) :
0027     QFile(name)
0028 {
0029 }
0030 
0031 // Opens the selected device in WriteOnly mode
0032 bool PhysicalDevice::open()
0033 {
0034 #if defined(Q_OS_WIN32)
0035     DWORD bret;
0036 
0037     // In Windows QFile with write mode uses disposition OPEN_ALWAYS, but WinAPI
0038     // requires OPEN_EXISTING for physical devices. Therefore we have to use native API.
0039     m_fileHandle = CreateFile(
0040         reinterpret_cast<const wchar_t*>(fileName().utf16()),
0041         GENERIC_WRITE,
0042         FILE_SHARE_READ | FILE_SHARE_WRITE,
0043         NULL,
0044         OPEN_EXISTING,
0045         FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
0046         NULL
0047     );
0048     if (m_fileHandle == INVALID_HANDLE_VALUE)
0049     {
0050         setErrorString(errorMessageFromCode());
0051         return false;
0052     }
0053     // Lock the opened device
0054     if (!DeviceIoControl(m_fileHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bret, NULL))
0055     {
0056         setErrorString(formatErrorMessageFromCode(i18n("Could not acquire lock:")));
0057         return false;
0058     }
0059     // Construct QFile around the device handle; close() will now close the handle automatically
0060     if (QFile::open(_open_osfhandle(reinterpret_cast<intptr_t>(m_fileHandle), 0), QIODevice::WriteOnly | QIODevice::Unbuffered, AutoCloseHandle))
0061         return true;
0062     else
0063     {
0064         CloseHandle(m_fileHandle);
0065         return false;
0066     }
0067 #elif defined(Q_OS_MAC)
0068     return QFile::open(QIODevice::WriteOnly);
0069 #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
0070     int fd = getDescriptor();
0071     return QFile::open(fd, QIODevice::WriteOnly);
0072 #else
0073     return false;
0074 #endif
0075 }
0076 
0077 #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
0078 int PhysicalDevice::getDescriptor() {
0079     // fileName == e.g. /org/freedesktop/UDisks2/block_devices/sda
0080     // drivePath == e.g. /org/freedesktop/UDisks2/drives/JetFlash_Transcend_8GB_2H1NKR5V
0081     QDBusInterface device("org.freedesktop.UDisks2", fileName(), "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus(), this);
0082     QString drivePath = qvariant_cast<QDBusObjectPath>(device.property("Drive")).path();
0083     QDBusInterface manager("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2", "org.freedesktop.DBus.ObjectManager", QDBusConnection::systemBus());
0084     QDBusMessage message = manager.call("GetManagedObjects");
0085 
0086     if (message.arguments().length() == 1) {
0087         QDBusArgument arg = qvariant_cast<QDBusArgument>(message.arguments().first());
0088         DBusIntrospection objects;
0089         arg >> objects;
0090         for (auto i : objects.keys()) {
0091             if (objects[i].contains("org.freedesktop.UDisks2.Filesystem")) {
0092                 QString currentDrivePath = qvariant_cast<QDBusObjectPath>(objects[i]["org.freedesktop.UDisks2.Block"]["Drive"]).path();
0093                 if (currentDrivePath == drivePath) {
0094                     QDBusInterface partition("org.freedesktop.UDisks2", i.path(), "org.freedesktop.UDisks2.Filesystem", QDBusConnection::systemBus());
0095                     message = partition.call("Unmount", Properties { {"force", true} });
0096                 }
0097             }
0098         }
0099     } else {
0100         setErrorString(message.errorMessage());
0101         return -1;
0102     }
0103 
0104     QDBusReply<QDBusUnixFileDescriptor> reply = device.call(QDBus::Block, "OpenDevice", "rw", Properties{{"flags", O_DIRECT | O_SYNC | O_CLOEXEC}} );
0105     QDBusUnixFileDescriptor fd = reply.value();
0106 
0107     if (!fd.isValid()) {
0108         setErrorString(reply.error().message());
0109         return -1;
0110     }
0111 
0112     return fd.fileDescriptor();
0113 }
0114 #endif
0115 
0116 #include "moc_physicaldevice.cpp"