[BTRFS] Upgrade to 1.5
[reactos.git] / drivers / filesystems / btrfs / boot.c
1 /* Copyright (c) Mark Harmstone 2019
2 *
3 * This file is part of WinBtrfs.
4 *
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.
9 *
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.
14 *
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/>. */
17
18 #include "btrfs_drv.h"
19
20 #ifndef __REACTOS__
21 #ifdef _MSC_VER
22 #include <ntstrsafe.h>
23 #endif
24 #else
25 #include <ntstrsafe.h>
26 #endif
27
28 extern ERESOURCE pdo_list_lock;
29 extern LIST_ENTRY pdo_list;
30 extern ERESOURCE boot_lock;
31 extern PDRIVER_OBJECT drvobj;
32
33 #ifndef _MSC_VER
34 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw
35 #endif
36
37 // Not in any headers? Windbg knows about it though.
38 #define DOE_START_PENDING 0x10
39
40 // Just as much as we need - the version in mingw is truncated still further
41 typedef struct {
42 CSHORT Type;
43 USHORT Size;
44 PDEVICE_OBJECT DeviceObject;
45 ULONG PowerFlags;
46 void* Dope;
47 ULONG ExtensionFlags;
48 } DEVOBJ_EXTENSION2;
49
50 static bool get_system_root_partition(uint32_t* disk_num, uint32_t* partition_num) {
51 NTSTATUS Status;
52 HANDLE h;
53 UNICODE_STRING us, target;
54 OBJECT_ATTRIBUTES objatt;
55 WCHAR* s;
56 ULONG retlen = 0, left;
57
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(";
61
62 us.Buffer = (WCHAR*)system_root;
63 us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR);
64
65 InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
66
67 Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt);
68 if (!NT_SUCCESS(Status)) {
69 ERR("ZwOpenSymbolicLinkObject returned %08x\n", Status);
70 return false;
71 }
72
73 target.Length = target.MaximumLength = 0;
74
75 Status = ZwQuerySymbolicLinkObject(h, &target, &retlen);
76 if (Status != STATUS_BUFFER_TOO_SMALL) {
77 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
78 NtClose(h);
79 return false;
80 }
81
82 if (retlen == 0) {
83 NtClose(h);
84 return false;
85 }
86
87 target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG);
88 if (!target.Buffer) {
89 ERR("out of memory\n");
90 NtClose(h);
91 return false;
92 }
93
94 target.Length = target.MaximumLength = (USHORT)retlen;
95
96 Status = ZwQuerySymbolicLinkObject(h, &target, NULL);
97 if (!NT_SUCCESS(Status)) {
98 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
99 NtClose(h);
100 ExFreePool(target.Buffer);
101 return false;
102 }
103
104 NtClose(h);
105
106 TRACE("system root is %.*S\n", target.Length / sizeof(WCHAR), target.Buffer);
107
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);
111 return false;
112 }
113
114 s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1];
115 left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1;
116
117 if (left == 0 || s[0] < '0' || s[0] > '9') {
118 ExFreePool(target.Buffer);
119 return false;
120 }
121
122 *disk_num = 0;
123
124 while (left > 0 && s[0] >= '0' && s[0] <= '9') {
125 *disk_num *= 10;
126 *disk_num += s[0] - '0';
127 s++;
128 left--;
129 }
130
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);
134 return false;
135 }
136
137 s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1];
138 left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1;
139
140 if (left == 0 || s[0] < '0' || s[0] > '9') {
141 ExFreePool(target.Buffer);
142 return false;
143 }
144
145 *partition_num = 0;
146
147 while (left > 0 && s[0] >= '0' && s[0] <= '9') {
148 *partition_num *= 10;
149 *partition_num += s[0] - '0';
150 s++;
151 left--;
152 }
153
154 ExFreePool(target.Buffer);
155
156 return true;
157 }
158
159 static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID* uuid) {
160 NTSTATUS Status;
161 UNICODE_STRING us, us2;
162 WCHAR symlink[60], target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) + 36], *w;
163 #ifdef __REACTOS__
164 unsigned int i;
165 #endif
166
167 us.Buffer = symlink;
168 us.Length = 0;
169 us.MaximumLength = sizeof(symlink);
170
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);
174 return;
175 }
176
177 Status = IoDeleteSymbolicLink(&us);
178 if (!NT_SUCCESS(Status))
179 ERR("IoDeleteSymbolicLink returned %08x\n", Status);
180
181 RtlCopyMemory(target, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
182
183 w = &target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
184
185 #ifndef __REACTOS__
186 for (unsigned int i = 0; i < 16; i++) {
187 #else
188 for (i = 0; i < 16; i++) {
189 #endif
190 *w = hex_digit(uuid->uuid[i] >> 4); w++;
191 *w = hex_digit(uuid->uuid[i] & 0xf); w++;
192
193 if (i == 3 || i == 5 || i == 7 || i == 9) {
194 *w = L'-';
195 w++;
196 }
197 }
198
199 *w = L'}';
200
201 us2.Buffer = target;
202 us2.Length = us2.MaximumLength = sizeof(target);
203
204 Status = IoCreateSymbolicLink(&us, &us2);
205 if (!NT_SUCCESS(Status))
206 ERR("IoCreateSymbolicLink returned %08x\n", Status);
207 }
208
209 static void mountmgr_notification(BTRFS_UUID* uuid) {
210 UNICODE_STRING mmdevpath;
211 NTSTATUS Status;
212 PFILE_OBJECT FileObject;
213 PDEVICE_OBJECT mountmgr;
214 ULONG mmtnlen;
215 MOUNTMGR_TARGET_NAME* mmtn;
216 WCHAR* w;
217 #ifdef __REACTOS__
218 unsigned int i;
219 #endif
220
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);
225 return;
226 }
227
228 mmtnlen = offsetof(MOUNTMGR_TARGET_NAME, DeviceName[0]) + sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
229
230 mmtn = ExAllocatePoolWithTag(NonPagedPool, mmtnlen, ALLOC_TAG);
231 if (!mmtn) {
232 ERR("out of memory\n");
233 return;
234 }
235
236 mmtn->DeviceNameLength = sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
237
238 RtlCopyMemory(mmtn->DeviceName, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
239
240 w = &mmtn->DeviceName[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
241
242 #ifndef __REACTOS__
243 for (unsigned int i = 0; i < 16; i++) {
244 #else
245 for (i = 0; i < 16; i++) {
246 #endif
247 *w = hex_digit(uuid->uuid[i] >> 4); w++;
248 *w = hex_digit(uuid->uuid[i] & 0xf); w++;
249
250 if (i == 3 || i == 5 || i == 7 || i == 9) {
251 *w = L'-';
252 w++;
253 }
254 }
255
256 *w = L'}';
257
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);
261 ExFreePool(mmtn);
262 return;
263 }
264
265 ExFreePool(mmtn);
266 }
267
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
276 * point to.
277 */
278 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count) {
279 uint32_t disk_num, partition_num;
280 LIST_ENTRY* le;
281 bool done = false;
282 PDEVICE_OBJECT pdo_to_add = NULL;
283
284 TRACE("(%p, %p, %u)\n", DriverObject, Context, Count);
285
286 // wait for any PNP notifications in progress to finish
287 ExAcquireResourceExclusiveLite(&boot_lock, TRUE);
288 ExReleaseResourceLite(&boot_lock);
289
290 if (!get_system_root_partition(&disk_num, &partition_num))
291 return;
292
293 TRACE("system boot partition is disk %u, partition %u\n", disk_num, partition_num);
294
295 ExAcquireResourceSharedLite(&pdo_list_lock, true);
296
297 le = pdo_list.Flink;
298 while (le != &pdo_list) {
299 LIST_ENTRY* le2;
300 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
301
302 ExAcquireResourceSharedLite(&pdode->child_lock, true);
303
304 le2 = pdode->children.Flink;
305
306 while (le2 != &pdode->children) {
307 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
308
309 if (vc->disk_num == disk_num && vc->part_num == partition_num) {
310 change_symlink(disk_num, partition_num, &pdode->uuid);
311 done = true;
312
313 if (!pdode->vde)
314 pdo_to_add = pdode->pdo;
315
316 break;
317 }
318
319 le2 = le2->Flink;
320 }
321
322 if (done) {
323 le2 = pdode->children.Flink;
324
325 while (le2 != &pdode->children) {
326 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
327
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
331 * to mount one. */
332 if (vc->devobj) {
333 PDEVICE_OBJECT dev = vc->devobj;
334
335 ObReferenceObject(dev);
336
337 while (dev) {
338 PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev);
339
340 dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION;
341
342 ObDereferenceObject(dev);
343
344 dev = dev2;
345 }
346 }
347
348 le2 = le2->Flink;
349 }
350
351 ExReleaseResourceLite(&pdode->child_lock);
352
353 break;
354 }
355
356 ExReleaseResourceLite(&pdode->child_lock);
357
358 le = le->Flink;
359 }
360
361 ExReleaseResourceLite(&pdo_list_lock);
362
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.
367 if (pdo_to_add) {
368 pdo_device_extension* pdode = pdo_to_add->DeviceExtension;
369
370 AddDevice(drvobj, pdo_to_add);
371
372 // To stop Windows sneakily setting DOE_START_PENDING
373 pdode->dont_report = true;
374
375 if (pdo_to_add->DeviceObjectExtension) {
376 ((DEVOBJ_EXTENSION2*)pdo_to_add->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
377
378 if (pdode && pdode->vde && pdode->vde->device)
379 ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
380 }
381
382 mountmgr_notification(&pdode->uuid);
383 }
384 }