xref: /reactos/drivers/filesystems/btrfs/boot.c (revision cc7cf826)
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 BTRFS_UUID boot_uuid; // initialized to 0
34 uint64_t boot_subvol = 0;
35 
36 #ifndef _MSC_VER
37 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw
38 #endif
39 
40 // Not in any headers? Windbg knows about it though.
41 #define DOE_START_PENDING 0x10
42 
43 // Just as much as we need - the version in mingw is truncated still further
44 typedef struct {
45     CSHORT Type;
46     USHORT Size;
47     PDEVICE_OBJECT DeviceObject;
48     ULONG PowerFlags;
49     void* Dope;
50     ULONG ExtensionFlags;
51 } DEVOBJ_EXTENSION2;
52 
53 typedef enum {
54     system_root_unknown,
55     system_root_partition,
56     system_root_btrfs
57 } system_root_type;
58 
59 typedef struct {
60     uint32_t disk_num;
61     uint32_t partition_num;
62     BTRFS_UUID uuid;
63     system_root_type type;
64 } system_root;
65 
66 static void get_system_root(system_root* sr) {
67     NTSTATUS Status;
68     HANDLE h;
69     UNICODE_STRING us, target;
70     OBJECT_ATTRIBUTES objatt;
71     ULONG retlen = 0;
72     bool second_time = false;
73 
74     static const WCHAR system_root[] = L"\\SystemRoot";
75     static const WCHAR boot_device[] = L"\\Device\\BootDevice";
76     static const WCHAR arc_prefix[] = L"\\ArcName\\multi(0)disk(0)rdisk(";
77     static const WCHAR arc_middle[] = L")partition(";
78     static const WCHAR arc_btrfs_prefix[] = L"\\ArcName\\btrfs(";
79 
80     us.Buffer = (WCHAR*)system_root;
81     us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR);
82 
83     InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
84 
85     while (true) {
86         Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt);
87         if (!NT_SUCCESS(Status)) {
88             ERR("ZwOpenSymbolicLinkObject returned %08lx\n", Status);
89             return;
90         }
91 
92         target.Length = target.MaximumLength = 0;
93 
94         Status = ZwQuerySymbolicLinkObject(h, &target, &retlen);
95         if (Status != STATUS_BUFFER_TOO_SMALL) {
96             ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status);
97             NtClose(h);
98             return;
99         }
100 
101         if (retlen == 0) {
102             NtClose(h);
103             return;
104         }
105 
106         target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG);
107         if (!target.Buffer) {
108             ERR("out of memory\n");
109             NtClose(h);
110             return;
111         }
112 
113         target.Length = target.MaximumLength = (USHORT)retlen;
114 
115         Status = ZwQuerySymbolicLinkObject(h, &target, NULL);
116         if (!NT_SUCCESS(Status)) {
117             ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status);
118             NtClose(h);
119             ExFreePool(target.Buffer);
120             return;
121         }
122 
123         NtClose(h);
124 
125         if (second_time) {
126             TRACE("boot device is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer);
127         } else {
128             TRACE("system root is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer);
129         }
130 
131         if (!second_time && target.Length >= sizeof(boot_device) - sizeof(WCHAR) &&
132             RtlCompareMemory(target.Buffer, boot_device, sizeof(boot_device) - sizeof(WCHAR)) == sizeof(boot_device) - sizeof(WCHAR)) {
133             ExFreePool(target.Buffer);
134 
135             us.Buffer = (WCHAR*)boot_device;
136             us.Length = us.MaximumLength = sizeof(boot_device) - sizeof(WCHAR);
137 
138             second_time = true;
139         } else
140             break;
141     }
142 
143     sr->type = system_root_unknown;
144 
145     if (target.Length >= sizeof(arc_prefix) - sizeof(WCHAR) &&
146         RtlCompareMemory(target.Buffer, arc_prefix, sizeof(arc_prefix) - sizeof(WCHAR)) == sizeof(arc_prefix) - sizeof(WCHAR)) {
147         WCHAR* s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1];
148         ULONG left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1;
149 
150         if (left == 0 || s[0] < '0' || s[0] > '9') {
151             ExFreePool(target.Buffer);
152             return;
153         }
154 
155         sr->disk_num = 0;
156 
157         while (left > 0 && s[0] >= '0' && s[0] <= '9') {
158             sr->disk_num *= 10;
159             sr->disk_num += s[0] - '0';
160             s++;
161             left--;
162         }
163 
164         if (left <= (sizeof(arc_middle) / sizeof(WCHAR)) - 1 ||
165             RtlCompareMemory(s, arc_middle, sizeof(arc_middle) - sizeof(WCHAR)) != sizeof(arc_middle) - sizeof(WCHAR)) {
166             ExFreePool(target.Buffer);
167             return;
168         }
169 
170         s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1];
171         left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1;
172 
173         if (left == 0 || s[0] < '0' || s[0] > '9') {
174             ExFreePool(target.Buffer);
175             return;
176         }
177 
178         sr->partition_num = 0;
179 
180         while (left > 0 && s[0] >= '0' && s[0] <= '9') {
181             sr->partition_num *= 10;
182             sr->partition_num += s[0] - '0';
183             s++;
184             left--;
185         }
186 
187         sr->type = system_root_partition;
188     } else if (target.Length >= sizeof(arc_btrfs_prefix) - sizeof(WCHAR) &&
189         RtlCompareMemory(target.Buffer, arc_btrfs_prefix, sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) == sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) {
190         WCHAR* s = &target.Buffer[(sizeof(arc_btrfs_prefix) / sizeof(WCHAR)) - 1];
191 #ifdef __REACTOS__
192         unsigned int i;
193 #endif // __REACTOS__
194 
195 #ifndef __REACTOS__
196         for (unsigned int i = 0; i < 16; i++) {
197 #else
198         for (i = 0; i < 16; i++) {
199 #endif // __REACTOS__
200             if (*s >= '0' && *s <= '9')
201                 sr->uuid.uuid[i] = (*s - '0') << 4;
202             else if (*s >= 'a' && *s <= 'f')
203                 sr->uuid.uuid[i] = (*s - 'a' + 0xa) << 4;
204             else if (*s >= 'A' && *s <= 'F')
205                 sr->uuid.uuid[i] = (*s - 'A' + 0xa) << 4;
206             else {
207                 ExFreePool(target.Buffer);
208                 return;
209             }
210 
211             s++;
212 
213             if (*s >= '0' && *s <= '9')
214                 sr->uuid.uuid[i] |= *s - '0';
215             else if (*s >= 'a' && *s <= 'f')
216                 sr->uuid.uuid[i] |= *s - 'a' + 0xa;
217             else if (*s >= 'A' && *s <= 'F')
218                 sr->uuid.uuid[i] |= *s - 'A' + 0xa;
219             else {
220                 ExFreePool(target.Buffer);
221                 return;
222             }
223 
224             s++;
225 
226             if (i == 3 || i == 5 || i == 7 || i == 9) {
227                 if (*s != '-') {
228                     ExFreePool(target.Buffer);
229                     return;
230                 }
231 
232                 s++;
233             }
234         }
235 
236         if (*s != ')') {
237             ExFreePool(target.Buffer);
238             return;
239         }
240 
241         sr->type = system_root_btrfs;
242     }
243 
244     ExFreePool(target.Buffer);
245 }
246 
247 static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID* uuid) {
248     NTSTATUS Status;
249     UNICODE_STRING us, us2;
250     WCHAR symlink[60], target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) + 36], *w;
251 #ifdef __REACTOS__
252     unsigned int i;
253 #endif
254 
255     us.Buffer = symlink;
256     us.Length = 0;
257     us.MaximumLength = sizeof(symlink);
258 
259     Status = RtlUnicodeStringPrintf(&us, L"\\Device\\Harddisk%u\\Partition%u", disk_num, partition_num);
260     if (!NT_SUCCESS(Status)) {
261         ERR("RtlUnicodeStringPrintf returned %08lx\n", Status);
262         return;
263     }
264 
265     Status = IoDeleteSymbolicLink(&us);
266     if (!NT_SUCCESS(Status))
267         ERR("IoDeleteSymbolicLink returned %08lx\n", Status);
268 
269     RtlCopyMemory(target, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
270 
271     w = &target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
272 
273 #ifndef __REACTOS__
274     for (unsigned int i = 0; i < 16; i++) {
275 #else
276     for (i = 0; i < 16; i++) {
277 #endif
278         *w = hex_digit(uuid->uuid[i] >> 4); w++;
279         *w = hex_digit(uuid->uuid[i] & 0xf); w++;
280 
281         if (i == 3 || i == 5 || i == 7 || i == 9) {
282             *w = L'-';
283             w++;
284         }
285     }
286 
287     *w = L'}';
288 
289     us2.Buffer = target;
290     us2.Length = us2.MaximumLength = sizeof(target);
291 
292     Status = IoCreateSymbolicLink(&us, &us2);
293     if (!NT_SUCCESS(Status))
294         ERR("IoCreateSymbolicLink returned %08lx\n", Status);
295 }
296 
297 static void mountmgr_notification(BTRFS_UUID* uuid) {
298     UNICODE_STRING mmdevpath;
299     NTSTATUS Status;
300     PFILE_OBJECT FileObject;
301     PDEVICE_OBJECT mountmgr;
302     ULONG mmtnlen;
303     MOUNTMGR_TARGET_NAME* mmtn;
304     WCHAR* w;
305 #ifdef __REACTOS__
306     unsigned int i;
307 #endif
308 
309     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
310     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
311     if (!NT_SUCCESS(Status)) {
312         ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
313         return;
314     }
315 
316     mmtnlen = offsetof(MOUNTMGR_TARGET_NAME, DeviceName[0]) + sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
317 
318     mmtn = ExAllocatePoolWithTag(NonPagedPool, mmtnlen, ALLOC_TAG);
319     if (!mmtn) {
320         ERR("out of memory\n");
321         return;
322     }
323 
324     mmtn->DeviceNameLength = sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
325 
326     RtlCopyMemory(mmtn->DeviceName, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
327 
328     w = &mmtn->DeviceName[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
329 
330 #ifndef __REACTOS__
331     for (unsigned int i = 0; i < 16; i++) {
332 #else
333     for (i = 0; i < 16; i++) {
334 #endif
335         *w = hex_digit(uuid->uuid[i] >> 4); w++;
336         *w = hex_digit(uuid->uuid[i] & 0xf); w++;
337 
338         if (i == 3 || i == 5 || i == 7 || i == 9) {
339             *w = L'-';
340             w++;
341         }
342     }
343 
344     *w = L'}';
345 
346     Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, mmtn, mmtnlen, NULL, 0, false, NULL);
347     if (!NT_SUCCESS(Status)) {
348         ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08lx\n", Status);
349         ExFreePool(mmtn);
350         return;
351     }
352 
353     ExFreePool(mmtn);
354 }
355 
356 static void check_boot_options() {
357     NTSTATUS Status;
358     WCHAR* s;
359 
360     static const WCHAR pathw[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control";
361     static const WCHAR namew[] = L"SystemStartOptions";
362     static const WCHAR subvol[] = L"SUBVOL=";
363 
364     _SEH2_TRY {
365         HANDLE control;
366         OBJECT_ATTRIBUTES oa;
367         UNICODE_STRING path;
368         ULONG kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR));
369         KEY_VALUE_FULL_INFORMATION* kvfi;
370         UNICODE_STRING name;
371         WCHAR* options;
372 
373         path.Buffer = (WCHAR*)pathw;
374         path.Length = path.MaximumLength = sizeof(pathw) - sizeof(WCHAR);
375 
376         InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
377 
378         Status = ZwOpenKey(&control, KEY_QUERY_VALUE, &oa);
379         if (!NT_SUCCESS(Status)) {
380             ERR("ZwOpenKey returned %08lx\n", Status);
381             return;
382         }
383 
384         // FIXME - don't fail if value too long (can we query for the length?)
385 
386         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
387         if (!kvfi) {
388             ERR("out of memory\n");
389             NtClose(control);
390             return;
391         }
392 
393         name.Buffer = (WCHAR*)namew;
394         name.Length = name.MaximumLength = sizeof(namew) - sizeof(WCHAR);
395 
396         Status = ZwQueryValueKey(control, &name, KeyValueFullInformation, kvfi,
397                                  kvfilen, &kvfilen);
398         if (!NT_SUCCESS(Status)) {
399             ERR("ZwQueryValueKey returned %08lx\n", Status);
400             NtClose(control);
401             return;
402         }
403 
404         NtClose(control);
405 
406         options = (WCHAR*)((uint8_t*)kvfi + kvfi->DataOffset);
407         options[kvfi->DataLength / sizeof(WCHAR)] = 0; // FIXME - make sure buffer long enough to allow this
408 
409         s = wcsstr(options, subvol);
410 
411         if (!s)
412             return;
413 
414         s += (sizeof(subvol) / sizeof(WCHAR)) - 1;
415 
416         boot_subvol = 0;
417 
418         while (true) {
419             if (*s >= '0' && *s <= '9') {
420                 boot_subvol <<= 4;
421                 boot_subvol |= *s - '0';
422             } else if (*s >= 'a' && *s <= 'f') {
423                 boot_subvol <<= 4;
424                 boot_subvol |= *s - 'a' + 0xa;
425             } else if (*s >= 'A' && *s <= 'F') {
426                 boot_subvol <<= 4;
427                 boot_subvol |= *s - 'A' + 0xa;
428             } else
429                 break;
430 
431             s++;
432         }
433     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
434         return;
435     } _SEH2_END;
436 
437     if (boot_subvol != 0) {
438         TRACE("passed subvol %I64x in boot options\n", boot_subvol);
439     }
440 }
441 
442 void boot_add_device(DEVICE_OBJECT* pdo) {
443     pdo_device_extension* pdode = pdo->DeviceExtension;
444 
445     AddDevice(drvobj, pdo);
446 
447     // To stop Windows sneakily setting DOE_START_PENDING
448     pdode->dont_report = true;
449 
450     if (pdo->DeviceObjectExtension) {
451         ((DEVOBJ_EXTENSION2*)pdo->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
452 
453         if (pdode && pdode->vde && pdode->vde->device)
454             ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
455     }
456 
457     mountmgr_notification(&pdode->uuid);
458 }
459 
460 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
461  * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
462  * create so that RAID works correctly.
463  * At the time check_system_root gets called, \SystemRoot is a symlink to the ARC device,
464  * e.g. \ArcName\multi(0)disk(0)rdisk(0)partition(1)\Windows. We can't change the symlink,
465  * as it gets clobbered by IopReassignSystemRoot shortly afterwards, and we can't touch
466  * the \ArcName symlinks as they haven't been created yet. Instead, we need to change the
467  * symlink \Device\HarddiskX\PartitionY, which is what the ArcName symlink will shortly
468  * point to.
469  */
470 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count) {
471     system_root sr;
472     LIST_ENTRY* le;
473     bool done = false;
474     PDEVICE_OBJECT pdo_to_add = NULL;
475     volume_child* boot_vc = NULL;
476 
477     TRACE("(%p, %p, %lu)\n", DriverObject, Context, Count);
478 
479     // wait for any PNP notifications in progress to finish
480     ExAcquireResourceExclusiveLite(&boot_lock, TRUE);
481     ExReleaseResourceLite(&boot_lock);
482 
483     get_system_root(&sr);
484 
485     if (sr.type == system_root_partition) {
486         TRACE("system boot partition is disk %u, partition %u\n", sr.disk_num, sr.partition_num);
487 
488         ExAcquireResourceSharedLite(&pdo_list_lock, true);
489 
490         le = pdo_list.Flink;
491         while (le != &pdo_list) {
492             LIST_ENTRY* le2;
493             pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
494 
495             ExAcquireResourceSharedLite(&pdode->child_lock, true);
496 
497             le2 = pdode->children.Flink;
498 
499             while (le2 != &pdode->children) {
500                 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
501 
502                 if (vc->disk_num == sr.disk_num && vc->part_num == sr.partition_num) {
503                     change_symlink(sr.disk_num, sr.partition_num, &pdode->uuid);
504                     done = true;
505 
506                     vc->boot_volume = true;
507                     boot_uuid = pdode->uuid;
508 
509                     if (!pdode->vde)
510                         pdo_to_add = pdode->pdo;
511 
512                     boot_vc = vc;
513 
514                     break;
515                 }
516 
517                 le2 = le2->Flink;
518             }
519 
520             if (done) {
521                 le2 = pdode->children.Flink;
522 
523                 while (le2 != &pdode->children) {
524                     volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
525 
526                     /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of
527                     * all of our underlying partition objects - otherwise IopMountVolume
528                     * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails
529                     * to mount one. */
530                     if (vc->devobj) {
531                         PDEVICE_OBJECT dev = vc->devobj;
532 
533                         ObReferenceObject(dev);
534 
535                         while (dev) {
536                             PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev);
537 
538                             dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION;
539 
540                             ObDereferenceObject(dev);
541 
542                             dev = dev2;
543                         }
544                     }
545 
546                     le2 = le2->Flink;
547                 }
548 
549                 ExReleaseResourceLite(&pdode->child_lock);
550 
551                 break;
552             }
553 
554             ExReleaseResourceLite(&pdode->child_lock);
555 
556             le = le->Flink;
557         }
558 
559         ExReleaseResourceLite(&pdo_list_lock);
560     } else if (sr.type == system_root_btrfs) {
561         boot_uuid = sr.uuid;
562 
563         ExAcquireResourceSharedLite(&pdo_list_lock, true);
564 
565         le = pdo_list.Flink;
566         while (le != &pdo_list) {
567             pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
568 
569             if (RtlCompareMemory(&pdode->uuid, &sr.uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
570                 if (!pdode->vde)
571                     pdo_to_add = pdode->pdo;
572 
573                 break;
574             }
575 
576             le = le->Flink;
577         }
578 
579         ExReleaseResourceLite(&pdo_list_lock);
580     }
581 
582     if (boot_vc) {
583         NTSTATUS Status;
584         UNICODE_STRING name;
585 
586         /* On Windows 8, mountmgr!MountMgrFindBootVolume returns the first volume in its database
587          * with the DO_SYSTEM_BOOT_PARTITION flag set. We've cleared the bit on the underlying devices,
588          * but as it caches it we need to disable and re-enable the volume so mountmgr receives a PNP
589          * notification to refresh its list. */
590 
591         static const WCHAR prefix[] = L"\\??";
592 
593         name.Length = name.MaximumLength = boot_vc->pnp_name.Length + sizeof(prefix) - sizeof(WCHAR);
594 
595         name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
596         if (!name.Buffer)
597             ERR("out of memory\n");
598         else {
599             RtlCopyMemory(name.Buffer, prefix, sizeof(prefix) - sizeof(WCHAR));
600             RtlCopyMemory(&name.Buffer[(sizeof(prefix) / sizeof(WCHAR)) - 1], boot_vc->pnp_name.Buffer, boot_vc->pnp_name.Length);
601 
602             Status = IoSetDeviceInterfaceState(&name, false);
603             if (!NT_SUCCESS(Status))
604                 ERR("IoSetDeviceInterfaceState returned %08lx\n", Status);
605 
606             Status = IoSetDeviceInterfaceState(&name, true);
607             if (!NT_SUCCESS(Status))
608                 ERR("IoSetDeviceInterfaceState returned %08lx\n", Status);
609 
610             ExFreePool(name.Buffer);
611         }
612     }
613 
614     if (sr.type == system_root_btrfs || boot_vc)
615         check_boot_options();
616 
617     // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls
618     // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our
619     // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile
620     // will return STATUS_NO_SUCH_DEVICE.
621     if (pdo_to_add)
622         boot_add_device(pdo_to_add);
623 }
624