1 /*
2 SPDX-FileCopyrightText: 2013 Patrick von Reth <vonreth@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7 #include "winblock.h"
8
9 #include <QDebug>
10 #include <QSettings>
11
12 using namespace Solid::Backends::Win;
13
14 #include <ntddcdrm.h>
15 #include <ntddmmc.h>
16
17 QMap<QString, QString> WinBlock::m_driveLetters = QMap<QString, QString>();
18 QMap<QString, QSet<QString>> WinBlock::m_driveUDIS = QMap<QString, QSet<QString>>();
19 QMap<QString, QString> WinBlock::m_virtualDrives = QMap<QString, QString>();
20
WinBlock(WinDevice * device)21 WinBlock::WinBlock(WinDevice *device)
22 : WinInterface(device)
23 , m_major(-1)
24 , m_minor(-1)
25 {
26 if (m_device->type() == Solid::DeviceInterface::StorageVolume) {
27 STORAGE_DEVICE_NUMBER info =
28 WinDeviceManager::getDeviceInfo<STORAGE_DEVICE_NUMBER>(driveLetterFromUdi(m_device->udi()), IOCTL_STORAGE_GET_DEVICE_NUMBER);
29 m_major = info.DeviceNumber;
30 m_minor = info.PartitionNumber;
31 } else if (m_device->type() == Solid::DeviceInterface::StorageDrive //
32 || m_device->type() == Solid::DeviceInterface::OpticalDrive //
33 || m_device->type() == Solid::DeviceInterface::OpticalDisc) {
34 m_major = m_device->udi().mid(m_device->udi().length() - 1).toInt();
35 } else {
36 qFatal("Not implemented device type %i", m_device->type());
37 }
38 }
39
~WinBlock()40 WinBlock::~WinBlock()
41 {
42 }
43
deviceMajor() const44 int WinBlock::deviceMajor() const
45 {
46 Q_ASSERT(m_major != -1);
47 return m_major;
48 }
49
deviceMinor() const50 int WinBlock::deviceMinor() const
51 {
52 return m_minor;
53 }
54
device() const55 QString WinBlock::device() const
56 {
57 return driveLetterFromUdi(m_device->udi());
58 }
59
drivesFromMask(const DWORD unitmask)60 QStringList WinBlock::drivesFromMask(const DWORD unitmask)
61 {
62 QStringList result;
63 DWORD localUnitmask(unitmask);
64 for (int i = 0; i <= 25; ++i) {
65 if (0x01 == (localUnitmask & 0x1)) {
66 result << QString("%1:").arg((char)(i + 'A'));
67 }
68 localUnitmask >>= 1;
69 }
70 return result;
71 }
72
getUdis()73 QSet<QString> WinBlock::getUdis()
74 {
75 return updateUdiFromBitMask(GetLogicalDrives());
76 }
77
driveLetterFromUdi(const QString & udi)78 QString WinBlock::driveLetterFromUdi(const QString &udi)
79 {
80 if (!m_driveLetters.contains(udi)) {
81 qWarning() << udi << "is not connected to a drive";
82 }
83 return m_driveLetters[udi];
84 }
85
udiFromDriveLetter(const QString & drive)86 QString WinBlock::udiFromDriveLetter(const QString &drive)
87 {
88 QString out;
89 for (QMap<QString, QString>::const_iterator it = m_driveLetters.cbegin(); it != m_driveLetters.cend(); ++it) {
90 if (it.value() == drive) {
91 out = it.key();
92 break;
93 }
94 }
95 return out;
96 }
97
resolveVirtualDrive(const QString & drive)98 QString WinBlock::resolveVirtualDrive(const QString &drive)
99 {
100 return m_virtualDrives[drive];
101 }
102
updateUdiFromBitMask(const DWORD unitmask)103 QSet<QString> WinBlock::updateUdiFromBitMask(const DWORD unitmask)
104 {
105 const QStringList drives = drivesFromMask(unitmask);
106 QSet<QString> list;
107 wchar_t driveWCHAR[MAX_PATH];
108 wchar_t bufferOut[MAX_PATH];
109 QString dosPath;
110 for (const QString &drive : drives) {
111 QSet<QString> udis;
112 driveWCHAR[drive.toWCharArray(driveWCHAR)] = 0;
113 if (GetDriveType(driveWCHAR) == DRIVE_REMOTE) { // network drive
114 QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Network\\") + drive.at(0), QSettings::NativeFormat);
115 QString path = settings.value("RemotePath").toString();
116 if (!path.isEmpty()) {
117 QString key = QLatin1String("/org/kde/solid/win/volume.virtual/") + drive.at(0);
118 m_virtualDrives[key] = path;
119 udis << key;
120 }
121
122 } else {
123 QueryDosDeviceW(driveWCHAR, bufferOut, MAX_PATH);
124 dosPath = QString::fromWCharArray(bufferOut);
125 if (dosPath.startsWith("\\??\\")) { // subst junction
126 dosPath = dosPath.mid(4);
127 QString key = QLatin1String("/org/kde/solid/win/volume.virtual/") + drive.at(0);
128 m_virtualDrives[key] = dosPath;
129 udis << key;
130 } else {
131 STORAGE_DEVICE_NUMBER info = WinDeviceManager::getDeviceInfo<STORAGE_DEVICE_NUMBER>(drive, IOCTL_STORAGE_GET_DEVICE_NUMBER);
132
133 switch (info.DeviceType) {
134 case FILE_DEVICE_DISK: {
135 udis << QString("/org/kde/solid/win/volume/disk#%1,partition#%2").arg(info.DeviceNumber).arg(info.PartitionNumber);
136 udis << QString("/org/kde/solid/win/storage/disk#%1").arg(info.DeviceNumber);
137 break;
138 }
139 case FILE_DEVICE_CD_ROM:
140 case FILE_DEVICE_DVD: {
141 udis << QString("/org/kde/solid/win/storage.cdrom/disk#%1").arg(info.DeviceNumber);
142 DISK_GEOMETRY_EX out = WinDeviceManager::getDeviceInfo<DISK_GEOMETRY_EX>(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX);
143 if (out.DiskSize.QuadPart != 0) {
144 udis << QString("/org/kde/solid/win/volume.cdrom/disk#%1").arg(info.DeviceNumber);
145 }
146 break;
147 }
148 default:
149 qDebug() << "unknown device" << drive << info.DeviceType << info.DeviceNumber << info.PartitionNumber;
150 }
151 }
152 }
153 m_driveUDIS[drive] = udis;
154 for (const QString &str : std::as_const(udis)) {
155 m_driveLetters[str] = drive;
156 }
157 list += udis;
158 }
159 return list;
160 }
161
getFromBitMask(const DWORD unitmask)162 QSet<QString> WinBlock::getFromBitMask(const DWORD unitmask)
163 {
164 QSet<QString> list;
165 const QStringList drives = drivesFromMask(unitmask);
166 for (const QString &drive : drives) {
167 if (m_driveUDIS.contains(drive)) {
168 list += m_driveUDIS[drive];
169 } else {
170 // we have to update the cache
171 return updateUdiFromBitMask(unitmask);
172 }
173 }
174 return list;
175 }
176