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
;
32 NTSTATUS
RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString
, const WCHAR
* pszFormat
, ...); // not in mingw
35 static bool get_system_root_partition(uint32_t* disk_num
, uint32_t* partition_num
) {
38 UNICODE_STRING us
, target
;
39 OBJECT_ATTRIBUTES objatt
;
41 ULONG retlen
= 0, left
;
43 static const WCHAR system_root
[] = L
"\\SystemRoot";
44 static const WCHAR arc_prefix
[] = L
"\\ArcName\\multi(0)disk(0)rdisk(";
45 static const WCHAR arc_middle
[] = L
")partition(";
47 us
.Buffer
= (WCHAR
*)system_root
;
48 us
.Length
= us
.MaximumLength
= sizeof(system_root
) - sizeof(WCHAR
);
50 InitializeObjectAttributes(&objatt
, &us
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
52 Status
= ZwOpenSymbolicLinkObject(&h
, GENERIC_READ
, &objatt
);
53 if (!NT_SUCCESS(Status
)) {
54 ERR("ZwOpenSymbolicLinkObject returned %08x\n", Status
);
58 target
.Length
= target
.MaximumLength
= 0;
60 Status
= ZwQuerySymbolicLinkObject(h
, &target
, &retlen
);
61 if (Status
!= STATUS_BUFFER_TOO_SMALL
) {
62 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status
);
72 target
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
, retlen
, ALLOC_TAG
);
74 ERR("out of memory\n");
79 target
.Length
= target
.MaximumLength
= (USHORT
)retlen
;
81 Status
= ZwQuerySymbolicLinkObject(h
, &target
, NULL
);
82 if (!NT_SUCCESS(Status
)) {
83 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status
);
85 ExFreePool(target
.Buffer
);
91 TRACE("system root is %.*S\n", target
.Length
/ sizeof(WCHAR
), target
.Buffer
);
93 if (target
.Length
<= sizeof(arc_prefix
) - sizeof(WCHAR
) ||
94 RtlCompareMemory(target
.Buffer
, arc_prefix
, sizeof(arc_prefix
) - sizeof(WCHAR
)) != sizeof(arc_prefix
) - sizeof(WCHAR
)) {
95 ExFreePool(target
.Buffer
);
99 s
= &target
.Buffer
[(sizeof(arc_prefix
) / sizeof(WCHAR
)) - 1];
100 left
= ((target
.Length
- sizeof(arc_prefix
)) / sizeof(WCHAR
)) + 1;
102 if (left
== 0 || s
[0] < '0' || s
[0] > '9') {
103 ExFreePool(target
.Buffer
);
109 while (left
> 0 && s
[0] >= '0' && s
[0] <= '9') {
111 *disk_num
+= s
[0] - '0';
116 if (left
<= (sizeof(arc_middle
) / sizeof(WCHAR
)) - 1 ||
117 RtlCompareMemory(s
, arc_middle
, sizeof(arc_middle
) - sizeof(WCHAR
)) != sizeof(arc_middle
) - sizeof(WCHAR
)) {
118 ExFreePool(target
.Buffer
);
122 s
= &s
[(sizeof(arc_middle
) / sizeof(WCHAR
)) - 1];
123 left
-= (sizeof(arc_middle
) / sizeof(WCHAR
)) - 1;
125 if (left
== 0 || s
[0] < '0' || s
[0] > '9') {
126 ExFreePool(target
.Buffer
);
132 while (left
> 0 && s
[0] >= '0' && s
[0] <= '9') {
133 *partition_num
*= 10;
134 *partition_num
+= s
[0] - '0';
139 ExFreePool(target
.Buffer
);
144 static void change_symlink(uint32_t disk_num
, uint32_t partition_num
, BTRFS_UUID
* uuid
) {
146 UNICODE_STRING us
, us2
;
147 WCHAR symlink
[60], target
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) + 36], *w
;
154 us
.MaximumLength
= sizeof(symlink
);
156 Status
= RtlUnicodeStringPrintf(&us
, L
"\\Device\\Harddisk%u\\Partition%u", disk_num
, partition_num
);
157 if (!NT_SUCCESS(Status
)) {
158 ERR("RtlUnicodeStringPrintf returned %08x\n", Status
);
162 Status
= IoDeleteSymbolicLink(&us
);
163 if (!NT_SUCCESS(Status
))
164 ERR("IoDeleteSymbolicLink returned %08x\n", Status
);
166 RtlCopyMemory(target
, BTRFS_VOLUME_PREFIX
, sizeof(BTRFS_VOLUME_PREFIX
) - sizeof(WCHAR
));
168 w
= &target
[(sizeof(BTRFS_VOLUME_PREFIX
) / sizeof(WCHAR
)) - 1];
171 for (unsigned int i
= 0; i
< 16; i
++) {
173 for (i
= 0; i
< 16; i
++) {
175 *w
= hex_digit(uuid
->uuid
[i
] >> 4); w
++;
176 *w
= hex_digit(uuid
->uuid
[i
] & 0xf); w
++;
178 if (i
== 3 || i
== 5 || i
== 7 || i
== 9) {
187 us2
.Length
= us2
.MaximumLength
= sizeof(target
);
189 Status
= IoCreateSymbolicLink(&us
, &us2
);
190 if (!NT_SUCCESS(Status
))
191 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
194 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
195 * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
196 * create so that RAID works correctly.
197 * At the time check_system_root gets called, \SystemRoot is a symlink to the ARC device,
198 * e.g. \ArcName\multi(0)disk(0)rdisk(0)partition(1)\Windows. We can't change the symlink,
199 * as it gets clobbered by IopReassignSystemRoot shortly afterwards, and we can't touch
200 * the \ArcName symlinks as they haven't been created yet. Instead, we need to change the
201 * symlink \Device\HarddiskX\PartitionY, which is what the ArcName symlink will shortly
204 void __stdcall
check_system_root(PDRIVER_OBJECT DriverObject
, PVOID Context
, ULONG Count
) {
205 uint32_t disk_num
, partition_num
;
209 TRACE("(%p, %p, %u)\n", DriverObject
, Context
, Count
);
211 if (!get_system_root_partition(&disk_num
, &partition_num
))
214 TRACE("system boot partition is disk %u, partition %u\n", disk_num
, partition_num
);
216 ExAcquireResourceSharedLite(&pdo_list_lock
, true);
219 while (le
!= &pdo_list
) {
221 pdo_device_extension
* pdode
= CONTAINING_RECORD(le
, pdo_device_extension
, list_entry
);
223 ExAcquireResourceSharedLite(&pdode
->child_lock
, true);
225 le2
= pdode
->children
.Flink
;
227 while (le2
!= &pdode
->children
) {
228 volume_child
* vc
= CONTAINING_RECORD(le2
, volume_child
, list_entry
);
230 if (vc
->disk_num
== disk_num
&& vc
->part_num
== partition_num
) {
231 change_symlink(disk_num
, partition_num
, &pdode
->uuid
);
239 ExReleaseResourceLite(&pdode
->child_lock
);
247 ExReleaseResourceLite(&pdo_list_lock
);