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