1 /* Copyright (c) Mark Harmstone 2019
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
22 #include <ntstrsafe.h>
25 #include <ntstrsafe.h>
28 extern ERESOURCE pdo_list_lock
;
29 extern LIST_ENTRY pdo_list
;
30 extern ERESOURCE boot_lock
;
31 extern PDRIVER_OBJECT drvobj
;
34 NTSTATUS
RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString
, const WCHAR
* pszFormat
, ...); // not in mingw
37 // Not in any headers? Windbg knows about it though.
38 #define DOE_START_PENDING 0x10
40 // Just as much as we need - the version in mingw is truncated still further
44 PDEVICE_OBJECT DeviceObject
;
50 static bool get_system_root_partition(uint32_t* disk_num
, uint32_t* partition_num
) {
53 UNICODE_STRING us
, target
;
54 OBJECT_ATTRIBUTES objatt
;
56 ULONG retlen
= 0, left
;
58 static const WCHAR system_root
[] = L
"\\SystemRoot";
59 static const WCHAR arc_prefix
[] = L
"\\ArcName\\multi(0)disk(0)rdisk(";
60 static const WCHAR arc_middle
[] = L
")partition(";
62 us
.Buffer
= (WCHAR
*)system_root
;
63 us
.Length
= us
.MaximumLength
= sizeof(system_root
) - sizeof(WCHAR
);
65 InitializeObjectAttributes(&objatt
, &us
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
67 Status
= ZwOpenSymbolicLinkObject(&h
, GENERIC_READ
, &objatt
);
68 if (!NT_SUCCESS(Status
)) {
69 ERR("ZwOpenSymbolicLinkObject returned %08x\n", Status
);
73 target
.Length
= target
.MaximumLength
= 0;
75 Status
= ZwQuerySymbolicLinkObject(h
, &target
, &retlen
);
76 if (Status
!= STATUS_BUFFER_TOO_SMALL
) {
77 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status
);
87 target
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
, retlen
, ALLOC_TAG
);
89 ERR("out of memory\n");
94 target
.Length
= target
.MaximumLength
= (USHORT
)retlen
;
96 Status
= ZwQuerySymbolicLinkObject(h
, &target
, NULL
);
97 if (!NT_SUCCESS(Status
)) {
98 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status
);
100 ExFreePool(target
.Buffer
);
106 TRACE("system root is %.*S\n", target
.Length
/ sizeof(WCHAR
), target
.Buffer
);
108 if (target
.Length
<= sizeof(arc_prefix
) - sizeof(WCHAR
) ||
109 RtlCompareMemory(target
.Buffer
, arc_prefix
, sizeof(arc_prefix
) - sizeof(WCHAR
)) != sizeof(arc_prefix
) - sizeof(WCHAR
)) {
110 ExFreePool(target
.Buffer
);
114 s
= &target
.Buffer
[(sizeof(arc_prefix
) / sizeof(WCHAR
)) - 1];
115 left
= ((target
.Length
- sizeof(arc_prefix
)) / sizeof(WCHAR
)) + 1;
117 if (left
== 0 || s
[0] < '0' || s
[0] > '9') {
118 ExFreePool(target
.Buffer
);
124 while (left
> 0 && s
[0] >= '0' && s
[0] <= '9') {
126 *disk_num
+= s
[0] - '0';
131 if (left
<= (sizeof(arc_middle
) / sizeof(WCHAR
)) - 1 ||
132 RtlCompareMemory(s
, arc_middle
, sizeof(arc_middle
) - sizeof(WCHAR
)) != sizeof(arc_middle
) - sizeof(WCHAR
)) {
133 ExFreePool(target
.Buffer
);
137 s
= &s
[(sizeof(arc_middle
) / sizeof(WCHAR
)) - 1];
138 left
-= (sizeof(arc_middle
) / sizeof(WCHAR
)) - 1;
140 if (left
== 0 || s
[0] < '0' || s
[0] > '9') {
141 ExFreePool(target
.Buffer
);
147 while (left
> 0 && s
[0] >= '0' && s
[0] <= '9') {
148 *partition_num
*= 10;
149 *partition_num
+= s
[0] - '0';
154 ExFreePool(target
.Buffer
);
159 static void change_symlink(uint32_t disk_num
, uint32_t partition_num
, BTRFS_UUID
* uuid
) {
161 UNICODE_STRING us
, us2
;
162 WCHAR symlink
[60], target
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) + 36], *w
;
169 us
.MaximumLength
= sizeof(symlink
);
171 Status
= RtlUnicodeStringPrintf(&us
, L
"\\Device\\Harddisk%u\\Partition%u", disk_num
, partition_num
);
172 if (!NT_SUCCESS(Status
)) {
173 ERR("RtlUnicodeStringPrintf returned %08x\n", Status
);
177 Status
= IoDeleteSymbolicLink(&us
);
178 if (!NT_SUCCESS(Status
))
179 ERR("IoDeleteSymbolicLink returned %08x\n", Status
);
181 RtlCopyMemory(target
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
183 w
= &target
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1];
186 for (unsigned int i
= 0; i
< 16; i
++) {
188 for (i
= 0; i
< 16; i
++) {
190 *w
= hex_digit(uuid
->uuid
[i
] >> 4); w
++;
191 *w
= hex_digit(uuid
->uuid
[i
] & 0xf); w
++;
193 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
202 us2
.Length
= us2
.MaximumLength
= sizeof(target
);
204 Status
= IoCreateSymbolicLink(&us
, &us2
);
205 if (!NT_SUCCESS(Status
))
206 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
209 static void mountmgr_notification(BTRFS_UUID
* uuid
) {
210 UNICODE_STRING mmdevpath
;
212 PFILE_OBJECT FileObject
;
213 PDEVICE_OBJECT mountmgr
;
215 MOUNTMGR_TARGET_NAME
* mmtn
;
221 RtlInitUnicodeString(&mmdevpath
, MOUNTMGR_DEVICE_NAME
);
222 Status
= IoGetDeviceObjectPointer(&mmdevpath
, FILE_READ_ATTRIBUTES
, &FileObject
, &mountmgr
);
223 if (!NT_SUCCESS(Status
)) {
224 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
228 mmtnlen
= offsetof(MOUNTMGR_TARGET_NAME
, DeviceName
[0]) + sizeof(BTRFS_VOLUME_PREFIX
) + (36 * sizeof(WCHAR
));
230 mmtn
= ExAllocatePoolWithTag(NonPagedPool
, mmtnlen
, ALLOC_TAG
);
232 ERR("out of memory\n");
236 mmtn
->DeviceNameLength
= sizeof(BTRFS_VOLUME_PREFIX
) + (36 * sizeof(WCHAR
));
238 RtlCopyMemory(mmtn
->DeviceName
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
240 w
= &mmtn
->DeviceName
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1];
243 for (unsigned int i
= 0; i
< 16; i
++) {
245 for (i
= 0; i
< 16; i
++) {
247 *w
= hex_digit(uuid
->uuid
[i
] >> 4); w
++;
248 *w
= hex_digit(uuid
->uuid
[i
] & 0xf); w
++;
250 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
258 Status
= dev_ioctl(mountmgr
, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION
, mmtn
, mmtnlen
, NULL
, 0, false, NULL
);
259 if (!NT_SUCCESS(Status
)) {
260 ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08x\n", Status
);
268 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
269 * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
270 * create so that RAID works correctly.
271 * At the time check_system_root gets called, \SystemRoot is a symlink to the ARC device,
272 * e.g. \ArcName\multi(0)disk(0)rdisk(0)partition(1)\Windows. We can't change the symlink,
273 * as it gets clobbered by IopReassignSystemRoot shortly afterwards, and we can't touch
274 * the \ArcName symlinks as they haven't been created yet. Instead, we need to change the
275 * symlink \Device\HarddiskX\PartitionY, which is what the ArcName symlink will shortly
278 void __stdcall
check_system_root(PDRIVER_OBJECT DriverObject
, PVOID Context
, ULONG Count
) {
279 uint32_t disk_num
, partition_num
;
282 PDEVICE_OBJECT pdo_to_add
= NULL
;
284 TRACE("(%p, %p, %u)\n", DriverObject
, Context
, Count
);
286 // wait for any PNP notifications in progress to finish
287 ExAcquireResourceExclusiveLite(&boot_lock
, TRUE
);
288 ExReleaseResourceLite(&boot_lock
);
290 if (!get_system_root_partition(&disk_num
, &partition_num
))
293 TRACE("system boot partition is disk %u, partition %u\n", disk_num
, partition_num
);
295 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
298 while (le
!= &pdo_list
) {
300 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
302 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
304 le2
= pdode
->children
.Flink
;
306 while (le2
!= &pdode
->children
) {
307 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
309 if (vc
->disk_num
== disk_num
&& vc
->part_num
== partition_num
) {
310 change_symlink(disk_num
, partition_num
, &pdode
->uuid
);
314 pdo_to_add
= pdode
->pdo
;
323 le2
= pdode
->children
.Flink
;
325 while (le2
!= &pdode
->children
) {
326 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
328 /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of
329 * all of our underlying partition objects - otherwise IopMountVolume
330 * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails
333 PDEVICE_OBJECT dev
= vc
->devobj
;
335 ObReferenceObject(dev
);
338 PDEVICE_OBJECT dev2
= IoGetLowerDeviceObject(dev
);
340 dev
->Flags
&= ~DO_SYSTEM_BOOT_PARTITION
;
342 ObDereferenceObject(dev
);
351 ExReleaseResourceLite(&pdode
->child_lock
);
356 ExReleaseResourceLite(&pdode
->child_lock
);
361 ExReleaseResourceLite(&pdo_list_lock
);
363 // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls
364 // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our
365 // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile
366 // will return STATUS_NO_SUCH_DEVICE.
368 pdo_device_extension
* pdode
= pdo_to_add
->DeviceExtension
;
370 AddDevice(drvobj
, pdo_to_add
);
372 // To stop Windows sneakily setting DOE_START_PENDING
373 pdode
->dont_report
= true;
375 if (pdo_to_add
->DeviceObjectExtension
) {
376 ((DEVOBJ_EXTENSION2
*)pdo_to_add
->DeviceObjectExtension
)->ExtensionFlags
&= ~DOE_START_PENDING
;
378 if (pdode
&& pdode
->vde
&& pdode
->vde
->device
)
379 ((DEVOBJ_EXTENSION2
*)pdode
->vde
->device
->DeviceObjectExtension
)->ExtensionFlags
&= ~DOE_START_PENDING
;
382 mountmgr_notification(&pdode
->uuid
);