ohmu
·
2026-10-15
1#include "disk.h"
2#include "common/debug.h"
3#include "common/io.h"
4#include "common/time.h"
5#include "common/windows/unicode.h"
6#include "common/windows/nt.h"
7
8#include <windows.h>
9#include <winioctl.h>
10#include <ntstatus.h>
11#include <stdalign.h>
12
13const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) {
14 PROCESS_DEVICEMAP_INFORMATION_EX info = {};
15 ULONG size = 0;
16 NTSTATUS status = NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, &info, sizeof(info), &size);
17 if (!NT_SUCCESS(status)) {
18 FF_DEBUG("NtQueryInformationProcess(ProcessDeviceMap) failed: %s", ffDebugNtStatus(status));
19 return "NtQueryInformationProcess(ProcessDeviceMap) failed";
20 }
21
22 // For cross-platform portability; used by `presets/examples/13.jsonc`
23 if (options->folders.length == 1 && options->folders.chars[0] == '/') {
24 options->folders.chars[0] = (char) SharedUserData->NtSystemRoot[0];
25 ffStrbufAppendS(&options->folders, ":\\");
26 }
27
28 wchar_t mountpointW[] = L"X:\\";
29 char mountpointA[] = "X:\\";
30
31 for (wchar_t i = L'A'; i <= L'Z'; i++) {
32 if (!(info.Query.DriveMap & (1 << (i - L'A')))) {
33 continue;
34 }
35 mountpointW[0] = i;
36 mountpointA[0] = (char) i;
37
38 UINT driveType = info.Query.DriveType[i - L'A'];
39
40 if (__builtin_expect((long) options->folders.length, 0)) {
41 if (!ffStrbufSeparatedContainNS(&options->folders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR)) {
42 continue;
43 }
44 } else if (driveType == DRIVE_NO_ROOT_DIR) {
45 continue;
46 }
47
48 if (options->hideFolders.length && ffStrbufSeparatedContainNS(&options->hideFolders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR)) {
49 continue;
50 }
51
52 FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(mountpointW, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
53 if (handle == INVALID_HANDLE_VALUE) {
54 continue;
55 }
56
57 IO_STATUS_BLOCK iosb;
58
59 alignas(FILE_FS_ATTRIBUTE_INFORMATION) uint8_t bufFsAttr[1024];
60 FILE_FS_ATTRIBUTE_INFORMATION* fsAttr = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsAttr, sizeof(bufFsAttr), FileFsAttributeInformation))
61 ? (FILE_FS_ATTRIBUTE_INFORMATION*) bufFsAttr
62 : nullptr;
63
64 FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
65 if (fsAttr) {
66 ffStrbufSetNWS(&diskFileSystemBuf, fsAttr->FileSystemNameLength / sizeof(WCHAR), fsAttr->FileSystemName);
67 if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &diskFileSystemBuf, ':')) {
68 continue;
69 }
70 }
71
72 FFDisk* disk = FF_LIST_ADD(FFDisk, *disks);
73
74 disk->filesUsed = 0;
75 disk->filesTotal = 0;
76 disk->bytesTotal = 0;
77 disk->bytesFree = 0;
78 disk->createTime = 0;
79 ffStrbufInit(&disk->filesystem);
80 ffStrbufInit(&disk->name);
81 ffStrbufInitNS(&disk->mountpoint, 3, mountpointA);
82 ffStrbufInit(&disk->mountFrom);
83 disk->type = driveType == DRIVE_REMOVABLE || driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM
84 ? FF_DISK_VOLUME_TYPE_EXTERNAL_BIT
85 : driveType == DRIVE_FIXED
86 ? FF_DISK_VOLUME_TYPE_REGULAR_BIT
87 : FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
88
89 {
90 wchar_t volumeName[MAX_PATH + 1];
91 mountpointW[2] = L'\0';
92 if (QueryDosDeviceW(mountpointW, volumeName, ARRAY_SIZE(volumeName))) {
93 ffStrbufSetWS(&disk->mountFrom, volumeName);
94 }
95 mountpointW[2] = L'\\';
96 }
97
98 alignas(FILE_FS_VOLUME_INFORMATION) uint8_t bufFsVolume[1024];
99 FILE_FS_VOLUME_INFORMATION* fsVolume = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsVolume, sizeof(bufFsVolume), FileFsVolumeInformation))
100 ? (FILE_FS_VOLUME_INFORMATION*) bufFsVolume
101 : nullptr;
102
103 if (fsVolume) {
104 if (fsVolume->VolumeLabelLength > 0) {
105 ffStrbufSetNWS(&disk->name, fsVolume->VolumeLabelLength / sizeof(WCHAR), fsVolume->VolumeLabel);
106 }
107 if (fsVolume->VolumeCreationTime.QuadPart) {
108 disk->createTime = ffFileTimeToUnixMs((uint64_t) fsVolume->VolumeCreationTime.QuadPart);
109 }
110 }
111
112 if (fsAttr) {
113 ffStrbufInitMove(&disk->filesystem, &diskFileSystemBuf);
114 if (fsAttr->FileSystemAttributes & FILE_READ_ONLY_VOLUME) {
115 disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
116 }
117 }
118
119 if (options->calcType == FF_DISK_CALC_TYPE_AVAILABLE || !ffIsFileFsFullSizeInformationExAvailable()) {
120 FILE_FS_FULL_SIZE_INFORMATION fsFullSize;
121 if (NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, &fsFullSize, sizeof(fsFullSize), FileFsFullSizeInformation))) {
122 uint64_t units = fsFullSize.BytesPerSector * fsFullSize.SectorsPerAllocationUnit;
123 disk->bytesTotal = (uint64_t) fsFullSize.TotalAllocationUnits.QuadPart * units;
124 disk->bytesFree = (uint64_t) fsFullSize.CallerAvailableAllocationUnits.QuadPart * units;
125 }
126 } else {
127 FILE_FS_FULL_SIZE_INFORMATION_EX fsFullSizeEx;
128 if (NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, &fsFullSizeEx, sizeof(fsFullSizeEx), FileFsFullSizeInformationEx))) {
129 // Space info without considering the quota setting
130 uint64_t units = fsFullSizeEx.BytesPerSector * fsFullSizeEx.SectorsPerAllocationUnit;
131 disk->bytesTotal = (uint64_t) fsFullSizeEx.ActualTotalAllocationUnits * units;
132 disk->bytesFree = (uint64_t) fsFullSizeEx.ActualAvailableAllocationUnits * units;
133 }
134 }
135 }
136
137 return nullptr;
138}