xref: /reactos/drivers/filesystems/btrfs/btrfs.c (revision ac43fd2b)
1 /* Copyright (c) Mark Harmstone 2016-17
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 #ifdef _DEBUG
19 #define DEBUG
20 #endif
21 
22 #include "btrfs_drv.h"
23 #ifndef __REACTOS__
24 #ifndef _MSC_VER
25 #include <cpuid.h>
26 #else
27 #include <intrin.h>
28 #endif
29 #endif
30 #include <ntddscsi.h>
31 #include "btrfs.h"
32 #include <ata.h>
33 
34 #ifndef _MSC_VER
35 #include <initguid.h>
36 #include <ntddstor.h>
37 #undef INITGUID
38 #endif
39 
40 #include <ntdddisk.h>
41 #include <ntddvol.h>
42 
43 #ifdef _MSC_VER
44 #include <initguid.h>
45 #include <ntddstor.h>
46 #undef INITGUID
47 #endif
48 
49 #ifdef _MSC_VER
50 #include <ntstrsafe.h>
51 #else
52 NTSTATUS RtlStringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList); // not in mingw
53 #endif
54 
55 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
56                             BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
57                             BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
58                             BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD)
59 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
60 
61 static const WCHAR device_name[] = {'\\','B','t','r','f','s',0};
62 static const WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
63 
64 DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
65 
66 PDRIVER_OBJECT drvobj;
67 PDEVICE_OBJECT master_devobj, busobj;
68 #ifndef __REACTOS__
69 bool have_sse42 = false, have_sse2 = false;
70 #endif
71 uint64_t num_reads = 0;
72 LIST_ENTRY uid_map_list, gid_map_list;
73 LIST_ENTRY VcbList;
74 ERESOURCE global_loading_lock;
75 uint32_t debug_log_level = 0;
76 uint32_t mount_compress = 0;
77 uint32_t mount_compress_force = 0;
78 uint32_t mount_compress_type = 0;
79 uint32_t mount_zlib_level = 3;
80 uint32_t mount_zstd_level = 3;
81 uint32_t mount_flush_interval = 30;
82 uint32_t mount_max_inline = 2048;
83 uint32_t mount_skip_balance = 0;
84 uint32_t mount_no_barrier = 0;
85 uint32_t mount_no_trim = 0;
86 uint32_t mount_clear_cache = 0;
87 uint32_t mount_allow_degraded = 0;
88 uint32_t mount_readonly = 0;
89 uint32_t no_pnp = 0;
90 bool log_started = false;
91 UNICODE_STRING log_device, log_file, registry_path;
92 tPsUpdateDiskCounters fPsUpdateDiskCounters;
93 tCcCopyReadEx fCcCopyReadEx;
94 tCcCopyWriteEx fCcCopyWriteEx;
95 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx;
96 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
97 tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
98 tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;
99 tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;
100 tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
101 bool diskacc = false;
102 void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
103 ERESOURCE pdo_list_lock, mapping_lock;
104 LIST_ENTRY pdo_list;
105 bool finished_probing = false;
106 HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
107 bool degraded_wait = true;
108 KEVENT mountmgr_thread_event;
109 bool shutting_down = false;
110 
111 #ifdef _DEBUG
112 PFILE_OBJECT comfo = NULL;
113 PDEVICE_OBJECT comdo = NULL;
114 HANDLE log_handle = NULL;
115 ERESOURCE log_lock;
116 HANDLE serial_thread_handle = NULL;
117 
118 static void init_serial(bool first_time);
119 #endif
120 
121 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp);
122 
123 typedef struct {
124     KEVENT Event;
125     IO_STATUS_BLOCK iosb;
126 } read_context;
127 
128 // no longer in Windows headers??
129 extern BOOLEAN WdmlibRtlIsNtDdiVersionAvailable(ULONG Version);
130 
131 #ifdef _DEBUG
132 _Function_class_(IO_COMPLETION_ROUTINE)
133 static NTSTATUS __stdcall dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
134     read_context* context = conptr;
135 
136     UNUSED(DeviceObject);
137 
138     context->iosb = Irp->IoStatus;
139     KeSetEvent(&context->Event, 0, false);
140 
141     return STATUS_MORE_PROCESSING_REQUIRED;
142 }
143 
144 #define DEBUG_MESSAGE_LEN 1024
145 
146 #ifdef DEBUG_LONG_MESSAGES
147 void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
148 #else
149 void _debug_message(_In_ const char* func, _In_ char* s, ...) {
150 #endif
151     LARGE_INTEGER offset;
152     PIO_STACK_LOCATION IrpSp;
153     NTSTATUS Status;
154     PIRP Irp;
155     va_list ap;
156     char *buf2, *buf;
157     read_context context;
158     uint32_t length;
159 
160     buf2 = ExAllocatePoolWithTag(NonPagedPool, DEBUG_MESSAGE_LEN, ALLOC_TAG);
161 
162     if (!buf2) {
163         DbgPrint("Couldn't allocate buffer in debug_message\n");
164         return;
165     }
166 
167 #ifdef DEBUG_LONG_MESSAGES
168     sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThread(), func, file, line);
169 #else
170     sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func);
171 #endif
172     buf = &buf2[strlen(buf2)];
173 
174     va_start(ap, s);
175 
176     RtlStringCbVPrintfA(buf, DEBUG_MESSAGE_LEN - strlen(buf2), s, ap);
177 
178     ExAcquireResourceSharedLite(&log_lock, true);
179 
180     if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
181         DbgPrint(buf2);
182     } else if (log_device.Length > 0) {
183         if (!comdo) {
184             DbgPrint(buf2);
185             goto exit2;
186         }
187 
188         length = (uint32_t)strlen(buf2);
189 
190         offset.u.LowPart = 0;
191         offset.u.HighPart = 0;
192 
193         RtlZeroMemory(&context, sizeof(read_context));
194 
195         KeInitializeEvent(&context.Event, NotificationEvent, false);
196 
197         Irp = IoAllocateIrp(comdo->StackSize, false);
198 
199         if (!Irp) {
200             DbgPrint("IoAllocateIrp failed\n");
201             goto exit2;
202         }
203 
204         IrpSp = IoGetNextIrpStackLocation(Irp);
205         IrpSp->MajorFunction = IRP_MJ_WRITE;
206         IrpSp->FileObject = comfo;
207 
208         if (comdo->Flags & DO_BUFFERED_IO) {
209             Irp->AssociatedIrp.SystemBuffer = buf2;
210 
211             Irp->Flags = IRP_BUFFERED_IO;
212         } else if (comdo->Flags & DO_DIRECT_IO) {
213             Irp->MdlAddress = IoAllocateMdl(buf2, length, false, false, NULL);
214             if (!Irp->MdlAddress) {
215                 DbgPrint("IoAllocateMdl failed\n");
216                 goto exit;
217             }
218 
219             MmBuildMdlForNonPagedPool(Irp->MdlAddress);
220         } else {
221             Irp->UserBuffer = buf2;
222         }
223 
224         IrpSp->Parameters.Write.Length = length;
225         IrpSp->Parameters.Write.ByteOffset = offset;
226 
227         Irp->UserIosb = &context.iosb;
228 
229         Irp->UserEvent = &context.Event;
230 
231         IoSetCompletionRoutine(Irp, dbg_completion, &context, true, true, true);
232 
233         Status = IoCallDriver(comdo, Irp);
234 
235         if (Status == STATUS_PENDING) {
236             KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
237             Status = context.iosb.Status;
238         }
239 
240         if (comdo->Flags & DO_DIRECT_IO)
241             IoFreeMdl(Irp->MdlAddress);
242 
243         if (!NT_SUCCESS(Status)) {
244             DbgPrint("failed to write to COM1 - error %08x\n", Status);
245             goto exit;
246         }
247 
248 exit:
249         IoFreeIrp(Irp);
250     } else if (log_handle != NULL) {
251         IO_STATUS_BLOCK iosb;
252 
253         length = (uint32_t)strlen(buf2);
254 
255         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
256 
257         if (!NT_SUCCESS(Status)) {
258             DbgPrint("failed to write to file - error %08x\n", Status);
259         }
260     }
261 
262 exit2:
263     ExReleaseResourceLite(&log_lock);
264 
265     va_end(ap);
266 
267     if (buf2)
268         ExFreePool(buf2);
269 }
270 #endif
271 
272 bool is_top_level(_In_ PIRP Irp) {
273     if (!IoGetTopLevelIrp()) {
274         IoSetTopLevelIrp(Irp);
275         return true;
276     }
277 
278     return false;
279 }
280 
281 _Function_class_(DRIVER_UNLOAD)
282 static void __stdcall DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
283     UNICODE_STRING dosdevice_nameW;
284 
285     TRACE("(%p)\n", DriverObject);
286 
287     free_cache();
288 
289     IoUnregisterFileSystem(DriverObject->DeviceObject);
290 
291     if (notification_entry2) {
292         if (fIoUnregisterPlugPlayNotificationEx)
293             fIoUnregisterPlugPlayNotificationEx(notification_entry2);
294         else
295             IoUnregisterPlugPlayNotification(notification_entry2);
296     }
297 
298     if (notification_entry3) {
299         if (fIoUnregisterPlugPlayNotificationEx)
300             fIoUnregisterPlugPlayNotificationEx(notification_entry3);
301         else
302             IoUnregisterPlugPlayNotification(notification_entry3);
303     }
304 
305     if (notification_entry) {
306         if (fIoUnregisterPlugPlayNotificationEx)
307             fIoUnregisterPlugPlayNotificationEx(notification_entry);
308         else
309             IoUnregisterPlugPlayNotification(notification_entry);
310     }
311 
312     dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
313     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
314 
315     IoDeleteSymbolicLink(&dosdevice_nameW);
316     IoDeleteDevice(DriverObject->DeviceObject);
317 
318     while (!IsListEmpty(&uid_map_list)) {
319         LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
320         uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
321 
322         ExFreePool(um->sid);
323 
324         ExFreePool(um);
325     }
326 
327     while (!IsListEmpty(&gid_map_list)) {
328         gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
329 
330         ExFreePool(gm->sid);
331         ExFreePool(gm);
332     }
333 
334     // FIXME - free volumes and their devpaths
335 
336 #ifdef _DEBUG
337     if (comfo)
338         ObDereferenceObject(comfo);
339 
340     if (log_handle)
341         ZwClose(log_handle);
342 #endif
343 
344     ExDeleteResourceLite(&global_loading_lock);
345     ExDeleteResourceLite(&pdo_list_lock);
346 
347     if (log_device.Buffer)
348         ExFreePool(log_device.Buffer);
349 
350     if (log_file.Buffer)
351         ExFreePool(log_file.Buffer);
352 
353     if (registry_path.Buffer)
354         ExFreePool(registry_path.Buffer);
355 
356 #ifdef _DEBUG
357     ExDeleteResourceLite(&log_lock);
358 #endif
359     ExDeleteResourceLite(&mapping_lock);
360 }
361 
362 static bool get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_opt_ PIRP Irp) {
363     KEY searchkey;
364     traverse_ptr tp, prev_tp;
365     NTSTATUS Status;
366 
367     // get last entry
368     searchkey.obj_id = 0xffffffffffffffff;
369     searchkey.obj_type = 0xff;
370     searchkey.offset = 0xffffffffffffffff;
371 
372     Status = find_item(Vcb, r, &tp, &searchkey, false, Irp);
373     if (!NT_SUCCESS(Status)) {
374         ERR("error - find_item returned %08x\n", Status);
375         return false;
376     }
377 
378     if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
379         r->lastinode = tp.item->key.obj_id;
380         TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
381         return true;
382     }
383 
384     while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
385         tp = prev_tp;
386 
387         TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
388 
389         if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
390             r->lastinode = tp.item->key.obj_id;
391             TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
392             return true;
393         }
394     }
395 
396     r->lastinode = SUBVOL_ROOT_INODE;
397 
398     WARN("no INODE_ITEMs in tree %I64x\n", r->id);
399 
400     return true;
401 }
402 
403 _Success_(return)
404 static bool extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ uint8_t** data, _Out_ uint16_t* datalen) {
405     DIR_ITEM* xa = (DIR_ITEM*)item;
406     USHORT xasize;
407 
408     while (true) {
409         if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
410             WARN("DIR_ITEM is truncated\n");
411             return false;
412         }
413 
414         if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
415             TRACE("found xattr %s\n", name);
416 
417             *datalen = xa->m;
418 
419             if (xa->m > 0) {
420                 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
421                 if (!*data) {
422                     ERR("out of memory\n");
423                     return false;
424                 }
425 
426                 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
427             } else
428                 *data = NULL;
429 
430             return true;
431         }
432 
433         xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
434 
435         if (size > xasize) {
436             size -= xasize;
437             xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
438         } else
439             break;
440     }
441 
442     TRACE("xattr %s not found\n", name);
443 
444     return false;
445 }
446 
447 _Success_(return)
448 bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ uint64_t inode, _In_z_ char* name, _In_ uint32_t crc32,
449                _Out_ uint8_t** data, _Out_ uint16_t* datalen, _In_opt_ PIRP Irp) {
450     KEY searchkey;
451     traverse_ptr tp;
452     NTSTATUS Status;
453 
454     TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
455 
456     searchkey.obj_id = inode;
457     searchkey.obj_type = TYPE_XATTR_ITEM;
458     searchkey.offset = crc32;
459 
460     Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
461     if (!NT_SUCCESS(Status)) {
462         ERR("error - find_item returned %08x\n", Status);
463         return false;
464     }
465 
466     if (keycmp(tp.item->key, searchkey)) {
467         TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
468         return false;
469     }
470 
471     if (tp.item->size < sizeof(DIR_ITEM)) {
472         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
473         return false;
474     }
475 
476     return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
477 }
478 
479 _Dispatch_type_(IRP_MJ_CLOSE)
480 _Function_class_(DRIVER_DISPATCH)
481 static NTSTATUS __stdcall drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
482     NTSTATUS Status;
483     PIO_STACK_LOCATION IrpSp;
484     device_extension* Vcb = DeviceObject->DeviceExtension;
485     bool top_level;
486 
487     FsRtlEnterFileSystem();
488 
489     TRACE("close\n");
490 
491     top_level = is_top_level(Irp);
492 
493     if (DeviceObject == master_devobj) {
494         TRACE("Closing file system\n");
495         Status = STATUS_SUCCESS;
496         goto end;
497     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
498         Status = vol_close(DeviceObject, Irp);
499         goto end;
500     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
501         Status = STATUS_INVALID_PARAMETER;
502         goto end;
503     }
504 
505     IrpSp = IoGetCurrentIrpStackLocation(Irp);
506 
507     // FIXME - unmount if called for volume
508     // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
509 
510     Status = close_file(IrpSp->FileObject, Irp);
511 
512 end:
513     Irp->IoStatus.Status = Status;
514     Irp->IoStatus.Information = 0;
515 
516     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
517 
518     if (top_level)
519         IoSetTopLevelIrp(NULL);
520 
521     TRACE("returning %08x\n", Status);
522 
523     FsRtlExitFileSystem();
524 
525     return Status;
526 }
527 
528 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
529 _Function_class_(DRIVER_DISPATCH)
530 static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
531     NTSTATUS Status;
532     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
533     PFILE_OBJECT FileObject = IrpSp->FileObject;
534     fcb* fcb = FileObject->FsContext;
535     device_extension* Vcb = DeviceObject->DeviceExtension;
536     bool top_level;
537 
538     FsRtlEnterFileSystem();
539 
540     TRACE("flush buffers\n");
541 
542     top_level = is_top_level(Irp);
543 
544     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
545         Status = vol_flush_buffers(DeviceObject, Irp);
546         goto end;
547     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
548         Status = STATUS_SUCCESS;
549         goto end;
550     }
551 
552     if (!fcb) {
553         ERR("fcb was NULL\n");
554         Status = STATUS_SUCCESS;
555         goto end;
556     }
557 
558     if (fcb == Vcb->volume_fcb) {
559         Status = STATUS_SUCCESS;
560         goto end;
561     }
562 
563     Irp->IoStatus.Information = 0;
564 
565     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
566 
567     Status = STATUS_SUCCESS;
568     Irp->IoStatus.Status = Status;
569 
570     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
571         CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
572 
573         if (fcb->Header.PagingIoResource) {
574             ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
575             ExReleaseResourceLite(fcb->Header.PagingIoResource);
576         }
577 
578         Status = Irp->IoStatus.Status;
579     }
580 
581 end:
582     IoCompleteRequest(Irp, IO_NO_INCREMENT);
583 
584     TRACE("returning %08x\n", Status);
585 
586     if (top_level)
587         IoSetTopLevelIrp(NULL);
588 
589     FsRtlExitFileSystem();
590 
591     return Status;
592 }
593 
594 static void calculate_total_space(_In_ device_extension* Vcb, _Out_ uint64_t* totalsize, _Out_ uint64_t* freespace) {
595     uint64_t nfactor, dfactor, sectors_used;
596 
597     if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
598         nfactor = 1;
599         dfactor = 2;
600     } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
601         nfactor = Vcb->superblock.num_devices - 1;
602         dfactor = Vcb->superblock.num_devices;
603     } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
604         nfactor = Vcb->superblock.num_devices - 2;
605         dfactor = Vcb->superblock.num_devices;
606     } else {
607         nfactor = 1;
608         dfactor = 1;
609     }
610 
611     sectors_used = Vcb->superblock.bytes_used / Vcb->superblock.sector_size;
612 
613     *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
614     *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
615 }
616 
617 #ifndef __REACTOS__
618 // This function exists because we have to lie about our FS type in certain situations.
619 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
620 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
621 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
622 // blacklist cmd.exe too.
623 
624 static bool lie_about_fs_type() {
625     NTSTATUS Status;
626     PROCESS_BASIC_INFORMATION pbi;
627     PPEB peb;
628     LIST_ENTRY* le;
629     ULONG retlen;
630 #ifdef _AMD64_
631     ULONG_PTR wow64info;
632 #endif
633 
634     static const WCHAR mpr[] = L"MPR.DLL";
635     static const WCHAR cmd[] = L"CMD.EXE";
636     static const WCHAR fsutil[] = L"FSUTIL.EXE";
637     UNICODE_STRING mprus, cmdus, fsutilus;
638 
639     mprus.Buffer = (WCHAR*)mpr;
640     mprus.Length = mprus.MaximumLength = sizeof(mpr) - sizeof(WCHAR);
641     cmdus.Buffer = (WCHAR*)cmd;
642     cmdus.Length = cmdus.MaximumLength = sizeof(cmd) - sizeof(WCHAR);
643     fsutilus.Buffer = (WCHAR*)fsutil;
644     fsutilus.Length = fsutilus.MaximumLength = sizeof(fsutil) - sizeof(WCHAR);
645 
646     if (!PsGetCurrentProcess())
647         return false;
648 
649 #ifdef _AMD64_
650     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64info, sizeof(wow64info), NULL);
651 
652     if (NT_SUCCESS(Status) && wow64info != 0)
653         return true;
654 #endif
655 
656     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
657 
658     if (!NT_SUCCESS(Status)) {
659         ERR("ZwQueryInformationProcess returned %08x\n", Status);
660         return false;
661     }
662 
663     if (!pbi.PebBaseAddress)
664         return false;
665 
666     peb = pbi.PebBaseAddress;
667 
668     if (!peb->Ldr)
669         return false;
670 
671     le = peb->Ldr->InMemoryOrderModuleList.Flink;
672     while (le != &peb->Ldr->InMemoryOrderModuleList) {
673         LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
674         bool blacklist = false;
675 
676         if (entry->FullDllName.Length >= mprus.Length) {
677             UNICODE_STRING name;
678 
679             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
680             name.Length = name.MaximumLength = mprus.Length;
681 
682             blacklist = FsRtlAreNamesEqual(&name, &mprus, true, NULL);
683         }
684 
685         if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
686             UNICODE_STRING name;
687 
688             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
689             name.Length = name.MaximumLength = cmdus.Length;
690 
691             blacklist = FsRtlAreNamesEqual(&name, &cmdus, true, NULL);
692         }
693 
694         if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) {
695             UNICODE_STRING name;
696 
697             name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - fsutilus.Length) / sizeof(WCHAR)];
698             name.Length = name.MaximumLength = fsutilus.Length;
699 
700             blacklist = FsRtlAreNamesEqual(&name, &fsutilus, true, NULL);
701         }
702 
703         if (blacklist) {
704             void** frames;
705             ULONG i, num_frames;
706 
707             frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
708             if (!frames) {
709                 ERR("out of memory\n");
710                 return false;
711             }
712 
713             num_frames = RtlWalkFrameChain(frames, 256, 1);
714 
715             for (i = 0; i < num_frames; i++) {
716                 // entry->Reserved3[1] appears to be the image size
717                 if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
718                     ExFreePool(frames);
719                     return true;
720                 }
721             }
722 
723             ExFreePool(frames);
724         }
725 
726         le = le->Flink;
727     }
728 
729     return false;
730 }
731 #endif
732 
733 // version of RtlUTF8ToUnicodeN for Vista and below
734 NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src, ULONG src_len) {
735     NTSTATUS Status = STATUS_SUCCESS;
736     uint8_t* in = (uint8_t*)src;
737     uint16_t* out = (uint16_t*)dest;
738     ULONG needed = 0, left = dest_max / sizeof(uint16_t);
739 #ifdef __REACTOS__
740     ULONG i;
741 
742     for (i = 0; i < src_len; ++i) {
743 #else
744 
745     for (ULONG i = 0; i < src_len; i++) {
746 #endif
747         uint32_t cp;
748 
749         if (!(in[i] & 0x80))
750             cp = in[i];
751         else if ((in[i] & 0xe0) == 0xc0) {
752             if (i == src_len - 1 || (in[i+1] & 0xc0) != 0x80) {
753                 cp = 0xfffd;
754                 Status = STATUS_SOME_NOT_MAPPED;
755             } else {
756                 cp = ((in[i] & 0x1f) << 6) | (in[i+1] & 0x3f);
757                 i++;
758             }
759         } else if ((in[i] & 0xf0) == 0xe0) {
760             if (i >= src_len - 2 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80) {
761                 cp = 0xfffd;
762                 Status = STATUS_SOME_NOT_MAPPED;
763             } else {
764                 cp = ((in[i] & 0xf) << 12) | ((in[i+1] & 0x3f) << 6) | (in[i+2] & 0x3f);
765                 i += 2;
766             }
767         } else if ((in[i] & 0xf8) == 0xf0) {
768             if (i >= src_len - 3 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80 || (in[i+3] & 0xc0) != 0x80) {
769                 cp = 0xfffd;
770                 Status = STATUS_SOME_NOT_MAPPED;
771             } else {
772                 cp = ((in[i] & 0x7) << 18) | ((in[i+1] & 0x3f) << 12) | ((in[i+2] & 0x3f) << 6) | (in[i+3] & 0x3f);
773                 i += 3;
774             }
775         } else {
776             cp = 0xfffd;
777             Status = STATUS_SOME_NOT_MAPPED;
778         }
779 
780         if (cp > 0x10ffff) {
781             cp = 0xfffd;
782             Status = STATUS_SOME_NOT_MAPPED;
783         }
784 
785         if (dest) {
786             if (cp <= 0xffff) {
787                 if (left < 1)
788                     return STATUS_BUFFER_OVERFLOW;
789 
790                 *out = (uint16_t)cp;
791                 out++;
792 
793                 left--;
794             } else {
795                 if (left < 2)
796                     return STATUS_BUFFER_OVERFLOW;
797 
798                 cp -= 0x10000;
799 
800                 *out = 0xd800 | ((cp & 0xffc00) >> 10);
801                 out++;
802 
803                 *out = 0xdc00 | (cp & 0x3ff);
804                 out++;
805 
806                 left -= 2;
807             }
808         }
809 
810         if (cp <= 0xffff)
811             needed += sizeof(uint16_t);
812         else
813             needed += 2 * sizeof(uint16_t);
814     }
815 
816     if (dest_len)
817         *dest_len = needed;
818 
819     return Status;
820 }
821 
822 // version of RtlUnicodeToUTF8N for Vista and below
823 NTSTATUS utf16_to_utf8(char* dest, ULONG dest_max, ULONG* dest_len, WCHAR* src, ULONG src_len) {
824     NTSTATUS Status = STATUS_SUCCESS;
825     uint16_t* in = (uint16_t*)src;
826     uint8_t* out = (uint8_t*)dest;
827     ULONG in_len = src_len / sizeof(uint16_t);
828     ULONG needed = 0, left = dest_max;
829 #ifdef __REACTOS__
830     ULONG i = 0;
831 
832     for (i = 0; i < in_len; i++) {
833 #else
834 
835     for (ULONG i = 0; i < in_len; i++) {
836 #endif
837         uint32_t cp = *in;
838         in++;
839 
840         if ((cp & 0xfc00) == 0xd800) {
841             if (i == in_len - 1 || (*in & 0xfc00) != 0xdc00) {
842                 cp = 0xfffd;
843                 Status = STATUS_SOME_NOT_MAPPED;
844             } else {
845                 cp = (cp & 0x3ff) << 10;
846                 cp |= *in & 0x3ff;
847                 cp += 0x10000;
848 
849                 in++;
850                 i++;
851             }
852         } else if ((cp & 0xfc00) == 0xdc00) {
853             cp = 0xfffd;
854             Status = STATUS_SOME_NOT_MAPPED;
855         }
856 
857         if (cp > 0x10ffff) {
858             cp = 0xfffd;
859             Status = STATUS_SOME_NOT_MAPPED;
860         }
861 
862         if (dest) {
863             if (cp < 0x80) {
864                 if (left < 1)
865                     return STATUS_BUFFER_OVERFLOW;
866 
867                 *out = (uint8_t)cp;
868                 out++;
869 
870                 left--;
871             } else if (cp < 0x800) {
872                 if (left < 2)
873                     return STATUS_BUFFER_OVERFLOW;
874 
875                 *out = 0xc0 | ((cp & 0x7c0) >> 6);
876                 out++;
877 
878                 *out = 0x80 | (cp & 0x3f);
879                 out++;
880 
881                 left -= 2;
882             } else if (cp < 0x10000) {
883                 if (left < 3)
884                     return STATUS_BUFFER_OVERFLOW;
885 
886                 *out = 0xe0 | ((cp & 0xf000) >> 12);
887                 out++;
888 
889                 *out = 0x80 | ((cp & 0xfc0) >> 6);
890                 out++;
891 
892                 *out = 0x80 | (cp & 0x3f);
893                 out++;
894 
895                 left -= 3;
896             } else {
897                 if (left < 4)
898                     return STATUS_BUFFER_OVERFLOW;
899 
900                 *out = 0xf0 | ((cp & 0x1c0000) >> 18);
901                 out++;
902 
903                 *out = 0x80 | ((cp & 0x3f000) >> 12);
904                 out++;
905 
906                 *out = 0x80 | ((cp & 0xfc0) >> 6);
907                 out++;
908 
909                 *out = 0x80 | (cp & 0x3f);
910                 out++;
911 
912                 left -= 4;
913             }
914         }
915 
916         if (cp < 0x80)
917             needed++;
918         else if (cp < 0x800)
919             needed += 2;
920         else if (cp < 0x10000)
921             needed += 3;
922         else
923             needed += 4;
924     }
925 
926     if (dest_len)
927         *dest_len = needed;
928 
929     return Status;
930 }
931 
932 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION)
933 _Function_class_(DRIVER_DISPATCH)
934 static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
935     PIO_STACK_LOCATION IrpSp;
936     NTSTATUS Status;
937     ULONG BytesCopied = 0;
938     device_extension* Vcb = DeviceObject->DeviceExtension;
939     bool top_level;
940 
941     FsRtlEnterFileSystem();
942 
943     TRACE("query volume information\n");
944     top_level = is_top_level(Irp);
945 
946     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
947         Status = vol_query_volume_information(DeviceObject, Irp);
948         goto end;
949     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
950         Status = STATUS_INVALID_PARAMETER;
951         goto end;
952     }
953 
954     IrpSp = IoGetCurrentIrpStackLocation(Irp);
955 
956     Status = STATUS_NOT_IMPLEMENTED;
957 
958     switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
959         case FileFsAttributeInformation:
960         {
961             FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
962             bool overflow = false;
963 #ifndef __REACTOS__
964             static const WCHAR ntfs[] = L"NTFS";
965 #endif
966             static const WCHAR btrfs[] = L"Btrfs";
967             const WCHAR* fs_name;
968             ULONG fs_name_len, orig_fs_name_len;
969 
970 #ifndef __REACTOS__
971             if (Irp->RequestorMode == UserMode && lie_about_fs_type()) {
972                 fs_name = ntfs;
973                 orig_fs_name_len = fs_name_len = sizeof(ntfs) - sizeof(WCHAR);
974             } else {
975                 fs_name = btrfs;
976                 orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
977             }
978 #else
979             fs_name = btrfs;
980             orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
981 #endif
982 
983             TRACE("FileFsAttributeInformation\n");
984 
985             if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
986                 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
987                     fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
988                 else
989                     fs_name_len = 0;
990 
991                 overflow = true;
992             }
993 
994             data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
995                                          FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
996                                          FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
997                                          FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING |
998                                          FILE_SUPPORTS_POSIX_UNLINK_RENAME;
999             if (Vcb->readonly)
1000                 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
1001 
1002             // should also be FILE_FILE_COMPRESSION when supported
1003             data->MaximumComponentNameLength = 255; // FIXME - check
1004             data->FileSystemNameLength = orig_fs_name_len;
1005             RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
1006 
1007             BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
1008             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
1009             break;
1010         }
1011 
1012         case FileFsDeviceInformation:
1013         {
1014             FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
1015 
1016             TRACE("FileFsDeviceInformation\n");
1017 
1018             ffdi->DeviceType = FILE_DEVICE_DISK;
1019 
1020             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1021             ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
1022             ExReleaseResourceLite(&Vcb->tree_lock);
1023 
1024             if (Vcb->readonly)
1025                 ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
1026             else
1027                 ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
1028 
1029             BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
1030             Status = STATUS_SUCCESS;
1031 
1032             break;
1033         }
1034 
1035         case FileFsFullSizeInformation:
1036         {
1037             FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1038 
1039             TRACE("FileFsFullSizeInformation\n");
1040 
1041             calculate_total_space(Vcb, (uint64_t*)&ffsi->TotalAllocationUnits.QuadPart, (uint64_t*)&ffsi->ActualAvailableAllocationUnits.QuadPart);
1042             ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
1043             ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1044             ffsi->BytesPerSector = 512;
1045 
1046             BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
1047             Status = STATUS_SUCCESS;
1048 
1049             break;
1050         }
1051 
1052         case FileFsObjectIdInformation:
1053         {
1054             FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
1055 
1056             TRACE("FileFsObjectIdInformation\n");
1057 
1058             RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
1059             RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
1060 
1061             BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
1062             Status = STATUS_SUCCESS;
1063 
1064             break;
1065         }
1066 
1067         case FileFsSizeInformation:
1068         {
1069             FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1070 
1071             TRACE("FileFsSizeInformation\n");
1072 
1073             calculate_total_space(Vcb, (uint64_t*)&ffsi->TotalAllocationUnits.QuadPart, (uint64_t*)&ffsi->AvailableAllocationUnits.QuadPart);
1074             ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1075             ffsi->BytesPerSector = 512;
1076 
1077             BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
1078             Status = STATUS_SUCCESS;
1079 
1080             break;
1081         }
1082 
1083         case FileFsVolumeInformation:
1084         {
1085             FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1086             FILE_FS_VOLUME_INFORMATION ffvi;
1087             bool overflow = false;
1088             ULONG label_len, orig_label_len;
1089 
1090             TRACE("FileFsVolumeInformation\n");
1091             TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
1092 
1093             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1094 
1095             Status = utf8_to_utf16(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1096             if (!NT_SUCCESS(Status)) {
1097                 ERR("utf8_to_utf16 returned %08x\n", Status);
1098                 ExReleaseResourceLite(&Vcb->tree_lock);
1099                 break;
1100             }
1101 
1102             orig_label_len = label_len;
1103 
1104             if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
1105                 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
1106                     label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
1107                 else
1108                     label_len = 0;
1109 
1110                 overflow = true;
1111             }
1112 
1113             TRACE("label_len = %u\n", label_len);
1114 
1115             ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
1116             ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
1117             ffvi.VolumeLabelLength = orig_label_len;
1118             ffvi.SupportsObjects = false;
1119 
1120             RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
1121 
1122             if (label_len > 0) {
1123                 ULONG bytecount;
1124 
1125                 Status = utf8_to_utf16(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1126                 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
1127                     ERR("utf8_to_utf16 returned %08x\n", Status);
1128                     ExReleaseResourceLite(&Vcb->tree_lock);
1129                     break;
1130                 }
1131 
1132                 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
1133             }
1134 
1135             ExReleaseResourceLite(&Vcb->tree_lock);
1136 
1137             BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
1138             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
1139             break;
1140         }
1141 
1142 #ifndef __REACTOS__
1143 #ifdef _MSC_VER // not in mingw yet
1144         case FileFsSectorSizeInformation:
1145         {
1146             FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1147 
1148             data->LogicalBytesPerSector = Vcb->superblock.sector_size;
1149             data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1150             data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
1151             data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1152             data->ByteOffsetForSectorAlignment = 0;
1153             data->ByteOffsetForPartitionAlignment = 0;
1154 
1155             data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
1156 
1157             if (Vcb->trim && !Vcb->options.no_trim)
1158                 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
1159 
1160             BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
1161 
1162             break;
1163         }
1164 #endif
1165 #endif /* __REACTOS__ */
1166 
1167         default:
1168             Status = STATUS_INVALID_PARAMETER;
1169             WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
1170             break;
1171     }
1172 
1173     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
1174         Irp->IoStatus.Information = 0;
1175     else
1176         Irp->IoStatus.Information = BytesCopied;
1177 
1178 end:
1179     Irp->IoStatus.Status = Status;
1180 
1181     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
1182 
1183     if (top_level)
1184         IoSetTopLevelIrp(NULL);
1185 
1186     TRACE("query volume information returning %08x\n", Status);
1187 
1188     FsRtlExitFileSystem();
1189 
1190     return Status;
1191 }
1192 
1193 _Function_class_(IO_COMPLETION_ROUTINE)
1194 static NTSTATUS __stdcall read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
1195     read_context* context = conptr;
1196 
1197     UNUSED(DeviceObject);
1198 
1199     context->iosb = Irp->IoStatus;
1200     KeSetEvent(&context->Event, 0, false);
1201 
1202     return STATUS_MORE_PROCESSING_REQUIRED;
1203 }
1204 
1205 NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ uint64_t id,
1206                      _Out_ root** rootptr, _In_ bool no_tree, _In_ uint64_t offset, _In_opt_ PIRP Irp) {
1207     NTSTATUS Status;
1208     root* r;
1209     tree* t = NULL;
1210     ROOT_ITEM* ri;
1211     traverse_ptr tp;
1212 
1213     r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
1214     if (!r) {
1215         ERR("out of memory\n");
1216         return STATUS_INSUFFICIENT_RESOURCES;
1217     }
1218 
1219     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
1220     if (!r->nonpaged) {
1221         ERR("out of memory\n");
1222         ExFreePool(r);
1223         return STATUS_INSUFFICIENT_RESOURCES;
1224     }
1225 
1226     if (!no_tree) {
1227         t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
1228         if (!t) {
1229             ERR("out of memory\n");
1230             ExFreePool(r->nonpaged);
1231             ExFreePool(r);
1232             return STATUS_INSUFFICIENT_RESOURCES;
1233         }
1234 
1235         t->nonpaged = NULL;
1236 
1237         t->is_unique = true;
1238         t->uniqueness_determined = true;
1239         t->buf = NULL;
1240     }
1241 
1242     ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
1243     if (!ri) {
1244         ERR("out of memory\n");
1245 
1246         if (t)
1247             ExFreePool(t);
1248 
1249         ExFreePool(r->nonpaged);
1250         ExFreePool(r);
1251         return STATUS_INSUFFICIENT_RESOURCES;
1252     }
1253 
1254     r->id = id;
1255     r->treeholder.address = 0;
1256     r->treeholder.generation = Vcb->superblock.generation;
1257     r->treeholder.tree = t;
1258     r->lastinode = 0;
1259     r->dirty = false;
1260     r->received = false;
1261     r->reserved = NULL;
1262     r->parent = 0;
1263     r->send_ops = 0;
1264     RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
1265     r->root_item.num_references = 1;
1266     r->fcbs_version = 0;
1267     r->checked_for_orphans = true;
1268     InitializeListHead(&r->fcbs);
1269     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
1270 
1271     RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
1272 
1273     // We ask here for a traverse_ptr to the item we're inserting, so we can
1274     // copy some of the tree's variables
1275 
1276     Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
1277     if (!NT_SUCCESS(Status)) {
1278         ERR("insert_tree_item returned %08x\n", Status);
1279         ExFreePool(ri);
1280 
1281         if (t)
1282             ExFreePool(t);
1283 
1284         ExFreePool(r->nonpaged);
1285         ExFreePool(r);
1286         return Status;
1287     }
1288 
1289     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1290 
1291     InsertTailList(&Vcb->roots, &r->list_entry);
1292 
1293     if (!no_tree) {
1294         RtlZeroMemory(&t->header, sizeof(tree_header));
1295         t->header.fs_uuid = tp.tree->header.fs_uuid;
1296         t->header.address = 0;
1297         t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1298         t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1299         t->header.generation = Vcb->superblock.generation;
1300         t->header.tree_id = id;
1301         t->header.num_items = 0;
1302         t->header.level = 0;
1303 
1304         t->has_address = false;
1305         t->size = 0;
1306         t->Vcb = Vcb;
1307         t->parent = NULL;
1308         t->paritem = NULL;
1309         t->root = r;
1310 
1311         InitializeListHead(&t->itemlist);
1312 
1313         t->new_address = 0;
1314         t->has_new_address = false;
1315         t->updated_extents = false;
1316 
1317         InsertTailList(&Vcb->trees, &t->list_entry);
1318         t->list_entry_hash.Flink = NULL;
1319 
1320         t->write = true;
1321         Vcb->need_write = true;
1322     }
1323 
1324     *rootptr = r;
1325 
1326     return STATUS_SUCCESS;
1327 }
1328 
1329 static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMATION* ffli) {
1330     ULONG utf8len;
1331     NTSTATUS Status;
1332     ULONG vollen, i;
1333 
1334     TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
1335 
1336     vollen = ffli->VolumeLabelLength;
1337 
1338     for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1339         if (ffli->VolumeLabel[i] == 0) {
1340             vollen = i * sizeof(WCHAR);
1341             break;
1342         } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1343             Status = STATUS_INVALID_VOLUME_LABEL;
1344             goto end;
1345         }
1346     }
1347 
1348     if (vollen == 0) {
1349         utf8len = 0;
1350     } else {
1351         Status = utf16_to_utf8(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1352         if (!NT_SUCCESS(Status))
1353             goto end;
1354 
1355         if (utf8len > MAX_LABEL_SIZE) {
1356             Status = STATUS_INVALID_VOLUME_LABEL;
1357             goto end;
1358         }
1359     }
1360 
1361     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1362 
1363     if (utf8len > 0) {
1364         Status = utf16_to_utf8((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1365         if (!NT_SUCCESS(Status))
1366             goto release;
1367     } else
1368         Status = STATUS_SUCCESS;
1369 
1370     if (utf8len < MAX_LABEL_SIZE)
1371         RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1372 
1373     Vcb->need_write = true;
1374 
1375 release:
1376     ExReleaseResourceLite(&Vcb->tree_lock);
1377 
1378 end:
1379     TRACE("returning %08x\n", Status);
1380 
1381     return Status;
1382 }
1383 
1384 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION)
1385 _Function_class_(DRIVER_DISPATCH)
1386 static NTSTATUS __stdcall drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1387     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1388     device_extension* Vcb = DeviceObject->DeviceExtension;
1389     NTSTATUS Status;
1390     bool top_level;
1391 
1392     FsRtlEnterFileSystem();
1393 
1394     TRACE("set volume information\n");
1395 
1396     top_level = is_top_level(Irp);
1397 
1398     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1399         Status = vol_set_volume_information(DeviceObject, Irp);
1400         goto end;
1401     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1402         Status = STATUS_INVALID_PARAMETER;
1403         goto end;
1404     }
1405 
1406     Status = STATUS_NOT_IMPLEMENTED;
1407 
1408     if (Vcb->readonly) {
1409         Status = STATUS_MEDIA_WRITE_PROTECTED;
1410         goto end;
1411     }
1412 
1413     if (Vcb->removing || Vcb->locked) {
1414         Status = STATUS_ACCESS_DENIED;
1415         goto end;
1416     }
1417 
1418     switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1419         case FileFsControlInformation:
1420             FIXME("STUB: FileFsControlInformation\n");
1421             break;
1422 
1423         case FileFsLabelInformation:
1424             TRACE("FileFsLabelInformation\n");
1425 
1426             Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1427             break;
1428 
1429         case FileFsObjectIdInformation:
1430             FIXME("STUB: FileFsObjectIdInformation\n");
1431             break;
1432 
1433         default:
1434             WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1435             break;
1436     }
1437 
1438 end:
1439     Irp->IoStatus.Status = Status;
1440     Irp->IoStatus.Information = 0;
1441 
1442     TRACE("returning %08x\n", Status);
1443 
1444     IoCompleteRequest( Irp, IO_NO_INCREMENT );
1445 
1446     if (top_level)
1447         IoSetTopLevelIrp(NULL);
1448 
1449     FsRtlExitFileSystem();
1450 
1451     return Status;
1452 }
1453 
1454 static WCHAR* file_desc_fcb(_In_ fcb* fcb) {
1455     char s[60];
1456     NTSTATUS Status;
1457     UNICODE_STRING us;
1458     ANSI_STRING as;
1459 
1460     if (fcb->debug_desc)
1461         return fcb->debug_desc;
1462 
1463     if (fcb == fcb->Vcb->volume_fcb)
1464         return L"volume FCB";
1465 
1466     fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
1467     if (!fcb->debug_desc)
1468         return L"(memory error)";
1469 
1470     // I know this is pretty hackish...
1471     // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1472     // without the CRT, which breaks drivers.
1473 
1474     sprintf(s, "subvol %x, inode %x", (uint32_t)fcb->subvol->id, (uint32_t)fcb->inode);
1475 
1476     as.Buffer = s;
1477     as.Length = as.MaximumLength = (USHORT)strlen(s);
1478 
1479     us.Buffer = fcb->debug_desc;
1480     us.MaximumLength = 60 * sizeof(WCHAR);
1481     us.Length = 0;
1482 
1483     Status = RtlAnsiStringToUnicodeString(&us, &as, false);
1484     if (!NT_SUCCESS(Status))
1485         return L"(RtlAnsiStringToUnicodeString error)";
1486 
1487     us.Buffer[us.Length / sizeof(WCHAR)] = 0;
1488 
1489     return fcb->debug_desc;
1490 }
1491 
1492 WCHAR* file_desc_fileref(_In_ file_ref* fileref) {
1493     NTSTATUS Status;
1494     UNICODE_STRING fn;
1495     ULONG reqlen;
1496 
1497     if (fileref->debug_desc)
1498         return fileref->debug_desc;
1499 
1500     fn.Length = fn.MaximumLength = 0;
1501     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1502     if (Status != STATUS_BUFFER_OVERFLOW)
1503         return L"ERROR";
1504 
1505     if (reqlen > 0xffff - sizeof(WCHAR))
1506         return L"(too long)";
1507 
1508     fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
1509     if (!fileref->debug_desc)
1510         return L"(memory error)";
1511 
1512     fn.Buffer = fileref->debug_desc;
1513     fn.Length = 0;
1514     fn.MaximumLength = (USHORT)(reqlen + sizeof(WCHAR));
1515 
1516     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1517     if (!NT_SUCCESS(Status)) {
1518         ExFreePool(fileref->debug_desc);
1519         fileref->debug_desc = NULL;
1520         return L"ERROR";
1521     }
1522 
1523     fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
1524 
1525     return fileref->debug_desc;
1526 }
1527 
1528 _Ret_z_
1529 WCHAR* file_desc(_In_ PFILE_OBJECT FileObject) {
1530     fcb* fcb = FileObject->FsContext;
1531     ccb* ccb = FileObject->FsContext2;
1532     file_ref* fileref = ccb ? ccb->fileref : NULL;
1533 
1534     if (fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
1535         return L"(paging file)";
1536 
1537     if (fileref)
1538         return file_desc_fileref(fileref);
1539     else
1540         return file_desc_fcb(fcb);
1541 }
1542 
1543 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1544     UNICODE_STRING fn;
1545     NTSTATUS Status;
1546     ULONG reqlen;
1547     USHORT name_offset;
1548     fcb* fcb = fileref->fcb;
1549 
1550     fn.Length = fn.MaximumLength = 0;
1551     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1552     if (Status != STATUS_BUFFER_OVERFLOW) {
1553         ERR("fileref_get_filename returned %08x\n", Status);
1554         return;
1555     }
1556 
1557     if (reqlen > 0xffff) {
1558         WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1559         return;
1560     }
1561 
1562     fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1563     if (!fn.Buffer) {
1564         ERR("out of memory\n");
1565         return;
1566     }
1567 
1568     fn.MaximumLength = (USHORT)reqlen;
1569     fn.Length = 0;
1570 
1571     Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
1572     if (!NT_SUCCESS(Status)) {
1573         ERR("fileref_get_filename returned %08x\n", Status);
1574         ExFreePool(fn.Buffer);
1575         return;
1576     }
1577 
1578     FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1579                                   (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1580     ExFreePool(fn.Buffer);
1581 }
1582 
1583 void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1584     fcb* fcb = fileref->fcb;
1585     LIST_ENTRY* le;
1586     NTSTATUS Status;
1587 
1588     // no point looking for hardlinks if st_nlink == 1
1589     if (fileref->fcb->inode_item.st_nlink == 1) {
1590         send_notification_fileref(fileref, filter_match, action, stream);
1591         return;
1592     }
1593 
1594     ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1595 
1596     le = fcb->hardlinks.Flink;
1597     while (le != &fcb->hardlinks) {
1598         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1599         file_ref* parfr;
1600 
1601         Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
1602 
1603         if (!NT_SUCCESS(Status))
1604             ERR("open_fileref_by_inode returned %08x\n", Status);
1605         else if (!parfr->deleted) {
1606             UNICODE_STRING fn;
1607             ULONG pathlen;
1608 
1609             fn.Length = fn.MaximumLength = 0;
1610             Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
1611             if (Status != STATUS_BUFFER_OVERFLOW) {
1612                 ERR("fileref_get_filename returned %08x\n", Status);
1613                 free_fileref(parfr);
1614                 break;
1615             }
1616 
1617             if (parfr != fcb->Vcb->root_fileref)
1618                 pathlen += sizeof(WCHAR);
1619 
1620             if (pathlen + hl->name.Length > 0xffff) {
1621                 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1622                 free_fileref(parfr);
1623                 break;
1624             }
1625 
1626             fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
1627             fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
1628             if (!fn.Buffer) {
1629                 ERR("out of memory\n");
1630                 free_fileref(parfr);
1631                 break;
1632             }
1633 
1634             Status = fileref_get_filename(parfr, &fn, NULL, NULL);
1635             if (!NT_SUCCESS(Status)) {
1636                 ERR("fileref_get_filename returned %08x\n", Status);
1637                 free_fileref(parfr);
1638                 ExFreePool(fn.Buffer);
1639                 break;
1640             }
1641 
1642             if (parfr != fcb->Vcb->root_fileref) {
1643                 fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
1644                 fn.Length += sizeof(WCHAR);
1645             }
1646 
1647             RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
1648             fn.Length += hl->name.Length;
1649 
1650             FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
1651                                           (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1652 
1653             ExFreePool(fn.Buffer);
1654 
1655             free_fileref(parfr);
1656         }
1657 
1658         le = le->Flink;
1659     }
1660 
1661     ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1662 }
1663 
1664 void mark_fcb_dirty(_In_ fcb* fcb) {
1665     if (!fcb->dirty) {
1666 #ifdef DEBUG_FCB_REFCOUNTS
1667         LONG rc;
1668 #endif
1669         fcb->dirty = true;
1670 
1671 #ifdef DEBUG_FCB_REFCOUNTS
1672         rc = InterlockedIncrement(&fcb->refcount);
1673         WARN("fcb %p: refcount now %i\n", fcb, rc);
1674 #else
1675         InterlockedIncrement(&fcb->refcount);
1676 #endif
1677 
1678         ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, true);
1679         InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
1680         ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
1681     }
1682 
1683     fcb->Vcb->need_write = true;
1684 }
1685 
1686 void mark_fileref_dirty(_In_ file_ref* fileref) {
1687     if (!fileref->dirty) {
1688         fileref->dirty = true;
1689         increase_fileref_refcount(fileref);
1690 
1691         ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, true);
1692         InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
1693         ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
1694     }
1695 
1696     fileref->fcb->Vcb->need_write = true;
1697 }
1698 
1699 #ifdef DEBUG_FCB_REFCOUNTS
1700 void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func) {
1701     LONG rc = InterlockedDecrement(&fcb->refcount);
1702 #else
1703 void free_fcb(_Inout_ fcb* fcb) {
1704     InterlockedDecrement(&fcb->refcount);
1705 #endif
1706 
1707 #ifdef DEBUG_FCB_REFCOUNTS
1708     ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1709 #endif
1710 }
1711 
1712 void reap_fcb(fcb* fcb) {
1713     uint8_t c = fcb->hash >> 24;
1714 
1715     if (fcb->subvol && fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
1716         if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
1717             fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
1718         else
1719             fcb->subvol->fcbs_ptrs[c] = NULL;
1720     }
1721 
1722     if (fcb->list_entry.Flink)
1723         RemoveEntryList(&fcb->list_entry);
1724 
1725     if (fcb->list_entry_all.Flink)
1726         RemoveEntryList(&fcb->list_entry_all);
1727 
1728     ExDeleteResourceLite(&fcb->nonpaged->resource);
1729     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1730     ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
1731 
1732     ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
1733 
1734     if (fcb->sd)
1735         ExFreePool(fcb->sd);
1736 
1737     if (fcb->adsxattr.Buffer)
1738         ExFreePool(fcb->adsxattr.Buffer);
1739 
1740     if (fcb->reparse_xattr.Buffer)
1741         ExFreePool(fcb->reparse_xattr.Buffer);
1742 
1743     if (fcb->ea_xattr.Buffer)
1744         ExFreePool(fcb->ea_xattr.Buffer);
1745 
1746     if (fcb->adsdata.Buffer)
1747         ExFreePool(fcb->adsdata.Buffer);
1748 
1749     if (fcb->debug_desc)
1750         ExFreePool(fcb->debug_desc);
1751 
1752     while (!IsListEmpty(&fcb->extents)) {
1753         LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
1754         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1755 
1756         if (ext->csum)
1757             ExFreePool(ext->csum);
1758 
1759         ExFreePool(ext);
1760     }
1761 
1762     while (!IsListEmpty(&fcb->hardlinks)) {
1763         LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
1764         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1765 
1766         if (hl->name.Buffer)
1767             ExFreePool(hl->name.Buffer);
1768 
1769         if (hl->utf8.Buffer)
1770             ExFreePool(hl->utf8.Buffer);
1771 
1772         ExFreePool(hl);
1773     }
1774 
1775     while (!IsListEmpty(&fcb->xattrs)) {
1776         xattr* xa = CONTAINING_RECORD(RemoveHeadList(&fcb->xattrs), xattr, list_entry);
1777 
1778         ExFreePool(xa);
1779     }
1780 
1781     while (!IsListEmpty(&fcb->dir_children_index)) {
1782         LIST_ENTRY* le = RemoveHeadList(&fcb->dir_children_index);
1783         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
1784 
1785         ExFreePool(dc->utf8.Buffer);
1786         ExFreePool(dc->name.Buffer);
1787         ExFreePool(dc->name_uc.Buffer);
1788         ExFreePool(dc);
1789     }
1790 
1791     if (fcb->hash_ptrs)
1792         ExFreePool(fcb->hash_ptrs);
1793 
1794     if (fcb->hash_ptrs_uc)
1795         ExFreePool(fcb->hash_ptrs_uc);
1796 
1797     FsRtlUninitializeFileLock(&fcb->lock);
1798 
1799     if (fcb->pool_type == NonPagedPool)
1800         ExFreePool(fcb);
1801     else
1802         ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
1803 }
1804 
1805 void reap_fcbs(device_extension* Vcb) {
1806     LIST_ENTRY* le;
1807 
1808     le = Vcb->all_fcbs.Flink;
1809     while (le != &Vcb->all_fcbs) {
1810         fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
1811         LIST_ENTRY* le2 = le->Flink;
1812 
1813         if (fcb->refcount == 0)
1814             reap_fcb(fcb);
1815 
1816         le = le2;
1817     }
1818 }
1819 
1820 void free_fileref(_Inout_ file_ref* fr) {
1821     LONG rc;
1822 
1823     rc = InterlockedDecrement(&fr->refcount);
1824 #ifdef __REACTOS__
1825     (void)rc;
1826 #endif
1827 
1828 #ifdef DEBUG_FCB_REFCOUNTS
1829     ERR("fileref %p: refcount now %i\n", fr, rc);
1830 #endif
1831 
1832 #ifdef _DEBUG
1833     if (rc < 0) {
1834         ERR("fileref %p: refcount now %i\n", fr, rc);
1835         int3;
1836     }
1837 #endif
1838 }
1839 
1840 void reap_fileref(device_extension* Vcb, file_ref* fr) {
1841     // FIXME - do we need a file_ref lock?
1842 
1843     // FIXME - do delete if needed
1844 
1845     if (fr->debug_desc)
1846         ExFreePool(fr->debug_desc);
1847 
1848     ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
1849 
1850     ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
1851 
1852     // FIXME - throw error if children not empty
1853 
1854     if (fr->fcb->fileref == fr)
1855         fr->fcb->fileref = NULL;
1856 
1857     if (fr->dc) {
1858         if (fr->fcb->ads)
1859             fr->dc->size = fr->fcb->adsdata.Length;
1860 
1861         fr->dc->fileref = NULL;
1862     }
1863 
1864     if (fr->list_entry.Flink)
1865         RemoveEntryList(&fr->list_entry);
1866 
1867     if (fr->parent)
1868         free_fileref(fr->parent);
1869 
1870     free_fcb(fr->fcb);
1871 
1872     ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
1873 }
1874 
1875 void reap_filerefs(device_extension* Vcb, file_ref* fr) {
1876     LIST_ENTRY* le;
1877 
1878     // FIXME - recursion is a bad idea in kernel mode
1879 
1880     le = fr->children.Flink;
1881     while (le != &fr->children) {
1882         file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
1883         LIST_ENTRY* le2 = le->Flink;
1884 
1885         reap_filerefs(Vcb, c);
1886 
1887         le = le2;
1888     }
1889 
1890     if (fr->refcount == 0)
1891         reap_fileref(Vcb, fr);
1892 }
1893 
1894 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
1895     fcb* fcb;
1896     ccb* ccb;
1897     file_ref* fileref = NULL;
1898     LONG open_files;
1899 
1900     UNUSED(Irp);
1901 
1902     TRACE("FileObject = %p\n", FileObject);
1903 
1904     fcb = FileObject->FsContext;
1905     if (!fcb) {
1906         TRACE("FCB was NULL, returning success\n");
1907         return STATUS_SUCCESS;
1908     }
1909 
1910     open_files = InterlockedDecrement(&fcb->Vcb->open_files);
1911 
1912     ccb = FileObject->FsContext2;
1913 
1914     TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
1915 
1916     // FIXME - make sure notification gets sent if file is being deleted
1917 
1918     if (ccb) {
1919         if (ccb->query_string.Buffer)
1920             RtlFreeUnicodeString(&ccb->query_string);
1921 
1922         if (ccb->filename.Buffer)
1923             ExFreePool(ccb->filename.Buffer);
1924 
1925         // FIXME - use refcounts for fileref
1926         fileref = ccb->fileref;
1927 
1928         if (fcb->Vcb->running_sends > 0) {
1929             bool send_cancelled = false;
1930 
1931             ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1932 
1933             if (ccb->send) {
1934                 ccb->send->cancelling = true;
1935                 send_cancelled = true;
1936                 KeSetEvent(&ccb->send->cleared_event, 0, false);
1937             }
1938 
1939             ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1940 
1941             if (send_cancelled) {
1942                 while (ccb->send) {
1943                     ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1944                     ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1945                 }
1946             }
1947         }
1948 
1949         ExFreePool(ccb);
1950     }
1951 
1952     CcUninitializeCacheMap(FileObject, NULL, NULL);
1953 
1954     if (open_files == 0 && fcb->Vcb->removing) {
1955         uninit(fcb->Vcb);
1956         return STATUS_SUCCESS;
1957     }
1958 
1959     if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
1960         return STATUS_SUCCESS;
1961 
1962     if (fileref)
1963         free_fileref(fileref);
1964     else
1965         free_fcb(fcb);
1966 
1967     return STATUS_SUCCESS;
1968 }
1969 
1970 void uninit(_In_ device_extension* Vcb) {
1971     uint64_t i;
1972     KIRQL irql;
1973     NTSTATUS Status;
1974     LIST_ENTRY* le;
1975     LARGE_INTEGER time;
1976 
1977     if (!Vcb->removing) {
1978         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1979         Vcb->removing = true;
1980         ExReleaseResourceLite(&Vcb->tree_lock);
1981     }
1982 
1983     IoAcquireVpbSpinLock(&irql);
1984     Vcb->Vpb->Flags &= ~VPB_MOUNTED;
1985     Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
1986     IoReleaseVpbSpinLock(irql);
1987 
1988     RemoveEntryList(&Vcb->list_entry);
1989 
1990     if (Vcb->balance.thread) {
1991         Vcb->balance.paused = false;
1992         Vcb->balance.stopping = true;
1993         KeSetEvent(&Vcb->balance.event, 0, false);
1994         KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, false, NULL);
1995     }
1996 
1997     if (Vcb->scrub.thread) {
1998         Vcb->scrub.paused = false;
1999         Vcb->scrub.stopping = true;
2000         KeSetEvent(&Vcb->scrub.event, 0, false);
2001         KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, false, NULL);
2002     }
2003 
2004     if (Vcb->running_sends != 0) {
2005         bool send_cancelled = false;
2006 
2007         ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2008 
2009         le = Vcb->send_ops.Flink;
2010         while (le != &Vcb->send_ops) {
2011             send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
2012 
2013             if (!send->cancelling) {
2014                 send->cancelling = true;
2015                 send_cancelled = true;
2016                 send->ccb = NULL;
2017                 KeSetEvent(&send->cleared_event, 0, false);
2018             }
2019 
2020             le = le->Flink;
2021         }
2022 
2023         ExReleaseResourceLite(&Vcb->send_load_lock);
2024 
2025         if (send_cancelled) {
2026             while (Vcb->running_sends != 0) {
2027                 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2028                 ExReleaseResourceLite(&Vcb->send_load_lock);
2029             }
2030         }
2031     }
2032 
2033     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
2034     if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
2035         WARN("registry_mark_volume_unmounted returned %08x\n", Status);
2036 
2037     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2038         Vcb->calcthreads.threads[i].quit = true;
2039     }
2040 
2041     KeSetEvent(&Vcb->calcthreads.event, 0, false);
2042 
2043     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2044         KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, false, NULL);
2045 
2046         ZwClose(Vcb->calcthreads.threads[i].handle);
2047     }
2048 
2049     ExDeleteResourceLite(&Vcb->calcthreads.lock);
2050     ExFreePool(Vcb->calcthreads.threads);
2051 
2052     time.QuadPart = 0;
2053     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
2054     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, false, NULL);
2055 
2056     reap_fcb(Vcb->volume_fcb);
2057     reap_fcb(Vcb->dummy_fcb);
2058 
2059     if (Vcb->root_file)
2060         ObDereferenceObject(Vcb->root_file);
2061 
2062     le = Vcb->chunks.Flink;
2063     while (le != &Vcb->chunks) {
2064         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
2065 
2066         if (c->cache) {
2067             reap_fcb(c->cache);
2068             c->cache = NULL;
2069         }
2070 
2071         le = le->Flink;
2072     }
2073 
2074     while (!IsListEmpty(&Vcb->roots)) {
2075         root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
2076 
2077         ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2078         ExFreePool(r->nonpaged);
2079         ExFreePool(r);
2080     }
2081 
2082     while (!IsListEmpty(&Vcb->chunks)) {
2083         chunk* c = CONTAINING_RECORD(RemoveHeadList(&Vcb->chunks), chunk, list_entry);
2084 
2085         while (!IsListEmpty(&c->space)) {
2086             LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2087             space* s = CONTAINING_RECORD(le2, space, list_entry);
2088 
2089             ExFreePool(s);
2090         }
2091 
2092         while (!IsListEmpty(&c->deleting)) {
2093             LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2094             space* s = CONTAINING_RECORD(le2, space, list_entry);
2095 
2096             ExFreePool(s);
2097         }
2098 
2099         if (c->devices)
2100             ExFreePool(c->devices);
2101 
2102         if (c->cache)
2103             reap_fcb(c->cache);
2104 
2105         ExDeleteResourceLite(&c->range_locks_lock);
2106         ExDeleteResourceLite(&c->partial_stripes_lock);
2107         ExDeleteResourceLite(&c->lock);
2108         ExDeleteResourceLite(&c->changed_extents_lock);
2109 
2110         ExFreePool(c->chunk_item);
2111         ExFreePool(c);
2112     }
2113 
2114     // FIXME - free any open fcbs?
2115 
2116     while (!IsListEmpty(&Vcb->devices)) {
2117         device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
2118 
2119         while (!IsListEmpty(&dev->space)) {
2120             LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
2121             space* s = CONTAINING_RECORD(le2, space, list_entry);
2122 
2123             ExFreePool(s);
2124         }
2125 
2126         ExFreePool(dev);
2127     }
2128 
2129     ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, true);
2130     while (!IsListEmpty(&Vcb->scrub.errors)) {
2131         scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
2132 
2133         ExFreePool(err);
2134     }
2135     ExReleaseResourceLite(&Vcb->scrub.stats_lock);
2136 
2137     ExDeleteResourceLite(&Vcb->fcb_lock);
2138     ExDeleteResourceLite(&Vcb->fileref_lock);
2139     ExDeleteResourceLite(&Vcb->load_lock);
2140     ExDeleteResourceLite(&Vcb->tree_lock);
2141     ExDeleteResourceLite(&Vcb->chunk_lock);
2142     ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
2143     ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
2144     ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
2145     ExDeleteResourceLite(&Vcb->scrub.stats_lock);
2146     ExDeleteResourceLite(&Vcb->send_load_lock);
2147 
2148     ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
2149     ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
2150     ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
2151     ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
2152     ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
2153     ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
2154     ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
2155     ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
2156     ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
2157 
2158     ZwClose(Vcb->flush_thread_handle);
2159 }
2160 
2161 static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
2162     NTSTATUS Status;
2163     LIST_ENTRY* le;
2164 
2165     // excise extents
2166 
2167     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2168         Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
2169         if (!NT_SUCCESS(Status)) {
2170             ERR("excise_extents returned %08x\n", Status);
2171             return Status;
2172         }
2173     }
2174 
2175     fileref->fcb->Header.AllocationSize.QuadPart = 0;
2176     fileref->fcb->Header.FileSize.QuadPart = 0;
2177     fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2178 
2179     if (FileObject) {
2180         CC_FILE_SIZES ccfs;
2181 
2182         ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2183         ccfs.FileSize = fileref->fcb->Header.FileSize;
2184         ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2185 
2186         Status = STATUS_SUCCESS;
2187 
2188         _SEH2_TRY {
2189             CcSetFileSizes(FileObject, &ccfs);
2190         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2191             Status = _SEH2_GetExceptionCode();
2192         } _SEH2_END;
2193 
2194         if (!NT_SUCCESS(Status)) {
2195             ERR("CcSetFileSizes threw exception %08x\n", Status);
2196             return Status;
2197         }
2198     }
2199 
2200     fileref->fcb->deleted = true;
2201 
2202     le = fileref->children.Flink;
2203     while (le != &fileref->children) {
2204         file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2205 
2206         if (fr2->fcb->ads) {
2207             fr2->fcb->deleted = true;
2208             mark_fcb_dirty(fr2->fcb);
2209         }
2210 
2211         le = le->Flink;
2212     }
2213 
2214     return STATUS_SUCCESS;
2215 }
2216 
2217 NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_ bool make_orphan, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
2218     LARGE_INTEGER newlength, time;
2219     BTRFS_TIME now;
2220     NTSTATUS Status;
2221     ULONG utf8len = 0;
2222 
2223     KeQuerySystemTime(&time);
2224     win_time_to_unix(time, &now);
2225 
2226     ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
2227 
2228     if (fileref->deleted) {
2229         ExReleaseResourceLite(fileref->fcb->Header.Resource);
2230         return STATUS_SUCCESS;
2231     }
2232 
2233     if (fileref->fcb->subvol->send_ops > 0) {
2234         ExReleaseResourceLite(fileref->fcb->Header.Resource);
2235         return STATUS_ACCESS_DENIED;
2236     }
2237 
2238     fileref->deleted = true;
2239     mark_fileref_dirty(fileref);
2240 
2241     // delete INODE_ITEM (0x1)
2242 
2243     TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2244 
2245     if (!fileref->fcb->ads) {
2246         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2247             LIST_ENTRY* le;
2248 
2249             mark_fcb_dirty(fileref->fcb);
2250 
2251             fileref->fcb->inode_item_changed = true;
2252 
2253             if (fileref->fcb->inode_item.st_nlink > 1 || make_orphan) {
2254                 fileref->fcb->inode_item.st_nlink--;
2255                 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2256                 fileref->fcb->inode_item.sequence++;
2257                 fileref->fcb->inode_item.st_ctime = now;
2258             } else {
2259                 Status = delete_fileref_fcb(fileref, FileObject, Irp, rollback);
2260                 if (!NT_SUCCESS(Status)) {
2261                     ERR("delete_fileref_fcb returned %08x\n", Status);
2262                     ExReleaseResourceLite(fileref->fcb->Header.Resource);
2263                     return Status;
2264                 }
2265             }
2266 
2267             if (fileref->dc) {
2268                 le = fileref->fcb->hardlinks.Flink;
2269                 while (le != &fileref->fcb->hardlinks) {
2270                     hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2271 
2272                     if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
2273                         RemoveEntryList(&hl->list_entry);
2274 
2275                         if (hl->name.Buffer)
2276                             ExFreePool(hl->name.Buffer);
2277 
2278                         if (hl->utf8.Buffer)
2279                             ExFreePool(hl->utf8.Buffer);
2280 
2281                         ExFreePool(hl);
2282                         break;
2283                     }
2284 
2285                     le = le->Flink;
2286                 }
2287             }
2288         } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
2289             if (fileref->fcb->subvol->root_item.num_references > 1) {
2290                 fileref->fcb->subvol->root_item.num_references--;
2291 
2292                 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2293             } else {
2294                 LIST_ENTRY* le;
2295 
2296                 // FIXME - we need a lock here
2297 
2298                 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2299 
2300                 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2301 
2302                 le = fileref->children.Flink;
2303                 while (le != &fileref->children) {
2304                     file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2305 
2306                     if (fr2->fcb->ads) {
2307                         fr2->fcb->deleted = true;
2308                         mark_fcb_dirty(fr2->fcb);
2309                     }
2310 
2311                     le = le->Flink;
2312                 }
2313             }
2314         }
2315     } else {
2316         fileref->fcb->deleted = true;
2317         mark_fcb_dirty(fileref->fcb);
2318     }
2319 
2320     // remove dir_child from parent
2321 
2322     if (fileref->dc) {
2323         TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
2324 
2325         ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2326         RemoveEntryList(&fileref->dc->list_entry_index);
2327 
2328         if (!fileref->fcb->ads)
2329             remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2330 
2331         ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2332 
2333         if (!fileref->oldutf8.Buffer)
2334             fileref->oldutf8 = fileref->dc->utf8;
2335         else
2336             ExFreePool(fileref->dc->utf8.Buffer);
2337 
2338         utf8len = fileref->dc->utf8.Length;
2339 
2340         fileref->oldindex = fileref->dc->index;
2341 
2342         ExFreePool(fileref->dc->name.Buffer);
2343         ExFreePool(fileref->dc->name_uc.Buffer);
2344         ExFreePool(fileref->dc);
2345 
2346         fileref->dc = NULL;
2347     }
2348 
2349     // update INODE_ITEM of parent
2350 
2351     ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, true);
2352 
2353     fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2354     fileref->parent->fcb->inode_item.sequence++;
2355     fileref->parent->fcb->inode_item.st_ctime = now;
2356 
2357     if (!fileref->fcb->ads) {
2358         TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2359         fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
2360         TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2361         fileref->parent->fcb->inode_item.st_mtime = now;
2362     }
2363 
2364     fileref->parent->fcb->inode_item_changed = true;
2365     ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2366 
2367     if (!fileref->fcb->ads && fileref->parent->dc)
2368         send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2369 
2370     mark_fcb_dirty(fileref->parent->fcb);
2371 
2372     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2373     fileref->fcb->subvol->root_item.ctime = now;
2374 
2375     newlength.QuadPart = 0;
2376 
2377     if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2378         TRACE("CcUninitializeCacheMap failed\n");
2379 
2380     ExReleaseResourceLite(fileref->fcb->Header.Resource);
2381 
2382     return STATUS_SUCCESS;
2383 }
2384 
2385 _Dispatch_type_(IRP_MJ_CLEANUP)
2386 _Function_class_(DRIVER_DISPATCH)
2387 static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
2388     NTSTATUS Status;
2389     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2390     PFILE_OBJECT FileObject = IrpSp->FileObject;
2391     device_extension* Vcb = DeviceObject->DeviceExtension;
2392     fcb* fcb = FileObject->FsContext;
2393     bool top_level;
2394 
2395     FsRtlEnterFileSystem();
2396 
2397     TRACE("cleanup\n");
2398 
2399     top_level = is_top_level(Irp);
2400 
2401     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2402         Status = vol_cleanup(DeviceObject, Irp);
2403         goto exit;
2404     } else if (DeviceObject == master_devobj) {
2405         TRACE("closing file system\n");
2406         Status = STATUS_SUCCESS;
2407         goto exit;
2408     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2409         Status = STATUS_INVALID_PARAMETER;
2410         goto exit;
2411     }
2412 
2413     if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
2414         TRACE("FileObject %p already cleaned up\n", FileObject);
2415         Status = STATUS_SUCCESS;
2416         goto exit;
2417     }
2418 
2419     if (!fcb) {
2420         ERR("fcb was NULL\n");
2421         Status = STATUS_INVALID_PARAMETER;
2422         goto exit;
2423     }
2424 
2425     // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2426     // messages belonging to other devices.
2427 
2428     if (FileObject && FileObject->FsContext) {
2429         LONG oc;
2430         ccb* ccb;
2431         file_ref* fileref;
2432         bool locked = true;
2433 
2434         ccb = FileObject->FsContext2;
2435         fileref = ccb ? ccb->fileref : NULL;
2436 
2437         TRACE("cleanup called for FileObject %p\n", FileObject);
2438         TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
2439 
2440         ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
2441 
2442         ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2443 
2444         IoRemoveShareAccess(FileObject, &fcb->share_access);
2445 
2446         FsRtlFastUnlockAll(&fcb->lock, FileObject, IoGetRequestorProcess(Irp), NULL);
2447 
2448         if (ccb)
2449             FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
2450 
2451         if (fileref) {
2452             oc = InterlockedDecrement(&fileref->open_count);
2453 #ifdef DEBUG_FCB_REFCOUNTS
2454             ERR("fileref %p: open_count now %i\n", fileref, oc);
2455 #endif
2456         }
2457 
2458         if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2459             fileref->delete_on_close = true;
2460 
2461         if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
2462             fileref->delete_on_close = false;
2463 
2464         if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
2465             TRACE("unlocking volume\n");
2466             do_unlock_volume(fcb->Vcb);
2467             FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2468         }
2469 
2470         if (ccb && ccb->reserving) {
2471             fcb->subvol->reserved = NULL;
2472             ccb->reserving = false;
2473             // FIXME - flush all of subvol's fcbs
2474         }
2475 
2476         if (fileref && (oc == 0 || (fileref->delete_on_close && fileref->posix_delete))) {
2477             if (!fcb->Vcb->removing) {
2478                 if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) { // last handle closed on POSIX-deleted file
2479                     LIST_ENTRY rollback;
2480 
2481                     InitializeListHead(&rollback);
2482 
2483                     Status = delete_fileref_fcb(fileref, FileObject, Irp, &rollback);
2484                     if (!NT_SUCCESS(Status)) {
2485                         ERR("delete_fileref_fcb returned %08x\n", Status);
2486                         do_rollback(fcb->Vcb, &rollback);
2487                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
2488                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2489                         goto exit;
2490                     }
2491 
2492                     clear_rollback(&rollback);
2493 
2494                     mark_fcb_dirty(fileref->fcb);
2495                 } else if (fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2496                     LIST_ENTRY rollback;
2497 
2498                     InitializeListHead(&rollback);
2499 
2500                     if (!fileref->fcb->ads || fileref->dc) {
2501                         if (fileref->fcb->ads) {
2502                             send_notification_fileref(fileref->parent, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2503                                                       FILE_ACTION_REMOVED, &fileref->dc->name);
2504                         } else
2505                             send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
2506                     }
2507 
2508                     ExReleaseResourceLite(fcb->Header.Resource);
2509                     locked = false;
2510 
2511                     // fileref_lock needs to be acquired before fcb->Header.Resource
2512                     ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
2513 
2514                     Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
2515                     if (!NT_SUCCESS(Status)) {
2516                         ERR("delete_fileref returned %08x\n", Status);
2517                         do_rollback(fcb->Vcb, &rollback);
2518                         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2519                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2520                         goto exit;
2521                     }
2522 
2523                     ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2524 
2525                     clear_rollback(&rollback);
2526                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && FileObject->SectionObjectPointer->DataSectionObject) {
2527                     IO_STATUS_BLOCK iosb;
2528                     CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2529 
2530                     if (!NT_SUCCESS(iosb.Status)) {
2531                         ERR("CcFlushCache returned %08x\n", iosb.Status);
2532                     }
2533 
2534                     if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2535                         ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
2536                         ExReleaseResourceLite(fcb->Header.PagingIoResource);
2537                     }
2538 
2539                     CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, false);
2540 
2541                     TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2542                         FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2543                 }
2544             }
2545 
2546             if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2547                 CcUninitializeCacheMap(FileObject, NULL, NULL);
2548         }
2549 
2550         if (locked)
2551             ExReleaseResourceLite(fcb->Header.Resource);
2552 
2553         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2554 
2555         FileObject->Flags |= FO_CLEANUP_COMPLETE;
2556     }
2557 
2558     Status = STATUS_SUCCESS;
2559 
2560 exit:
2561     TRACE("returning %08x\n", Status);
2562 
2563     Irp->IoStatus.Status = Status;
2564     Irp->IoStatus.Information = 0;
2565 
2566     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2567 
2568     if (top_level)
2569         IoSetTopLevelIrp(NULL);
2570 
2571     FsRtlExitFileSystem();
2572 
2573     return Status;
2574 }
2575 
2576 _Success_(return)
2577 bool get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ uint16_t len, _Out_ ULONG* atts) {
2578     if (len > 2 && val[0] == '0' && val[1] == 'x') {
2579         int i;
2580         ULONG dosnum = 0;
2581 
2582         for (i = 2; i < len; i++) {
2583             dosnum *= 0x10;
2584 
2585             if (val[i] >= '0' && val[i] <= '9')
2586                 dosnum |= val[i] - '0';
2587             else if (val[i] >= 'a' && val[i] <= 'f')
2588                 dosnum |= val[i] + 10 - 'a';
2589             else if (val[i] >= 'A' && val[i] <= 'F')
2590                 dosnum |= val[i] + 10 - 'a';
2591         }
2592 
2593         TRACE("DOSATTRIB: %08x\n", dosnum);
2594 
2595         *atts = dosnum;
2596 
2597         return true;
2598     }
2599 
2600     return false;
2601 }
2602 
2603 ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_ uint64_t inode,
2604                           _In_ uint8_t type, _In_ bool dotfile, _In_ bool ignore_xa, _In_opt_ PIRP Irp) {
2605     ULONG att;
2606     char* eaval;
2607     uint16_t ealen;
2608 
2609     if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (uint8_t**)&eaval, &ealen, Irp)) {
2610         ULONG dosnum = 0;
2611 
2612         if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
2613             ExFreePool(eaval);
2614 
2615             if (type == BTRFS_TYPE_DIRECTORY)
2616                 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2617             else if (type == BTRFS_TYPE_SYMLINK)
2618                 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2619 
2620             if (type != BTRFS_TYPE_DIRECTORY)
2621                 dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
2622 
2623             if (inode == SUBVOL_ROOT_INODE) {
2624                 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2625                     dosnum |= FILE_ATTRIBUTE_READONLY;
2626                 else
2627                     dosnum &= ~FILE_ATTRIBUTE_READONLY;
2628             }
2629 
2630             return dosnum;
2631         }
2632 
2633         ExFreePool(eaval);
2634     }
2635 
2636     switch (type) {
2637         case BTRFS_TYPE_DIRECTORY:
2638             att = FILE_ATTRIBUTE_DIRECTORY;
2639             break;
2640 
2641         case BTRFS_TYPE_SYMLINK:
2642             att = FILE_ATTRIBUTE_REPARSE_POINT;
2643             break;
2644 
2645         default:
2646             att = 0;
2647             break;
2648     }
2649 
2650     if (dotfile) {
2651         att |= FILE_ATTRIBUTE_HIDDEN;
2652     }
2653 
2654     att |= FILE_ATTRIBUTE_ARCHIVE;
2655 
2656     if (inode == SUBVOL_ROOT_INODE) {
2657         if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2658             att |= FILE_ATTRIBUTE_READONLY;
2659         else
2660             att &= ~FILE_ATTRIBUTE_READONLY;
2661     }
2662 
2663     // FIXME - get READONLY from ii->st_mode
2664     // FIXME - return SYSTEM for block/char devices?
2665 
2666     if (att == 0)
2667         att = FILE_ATTRIBUTE_NORMAL;
2668 
2669     return att;
2670 }
2671 
2672 NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ PFILE_OBJECT FileObject, _In_ uint64_t StartingOffset, _In_ ULONG Length,
2673                         _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ bool override) {
2674     IO_STATUS_BLOCK IoStatus;
2675     LARGE_INTEGER Offset;
2676     PIRP Irp;
2677     PIO_STACK_LOCATION IrpSp;
2678     NTSTATUS Status;
2679     read_context context;
2680 
2681     num_reads++;
2682 
2683     RtlZeroMemory(&context, sizeof(read_context));
2684     KeInitializeEvent(&context.Event, NotificationEvent, false);
2685 
2686     Offset.QuadPart = (LONGLONG)StartingOffset;
2687 
2688     Irp = IoAllocateIrp(DeviceObject->StackSize, false);
2689 
2690     if (!Irp) {
2691         ERR("IoAllocateIrp failed\n");
2692         return STATUS_INSUFFICIENT_RESOURCES;
2693     }
2694 
2695     Irp->Flags |= IRP_NOCACHE;
2696     IrpSp = IoGetNextIrpStackLocation(Irp);
2697     IrpSp->MajorFunction = IRP_MJ_READ;
2698     IrpSp->FileObject = FileObject;
2699 
2700     if (override)
2701         IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2702 
2703     if (DeviceObject->Flags & DO_BUFFERED_IO) {
2704         Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
2705         if (!Irp->AssociatedIrp.SystemBuffer) {
2706             ERR("out of memory\n");
2707             Status = STATUS_INSUFFICIENT_RESOURCES;
2708             goto exit;
2709         }
2710 
2711         Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
2712 
2713         Irp->UserBuffer = Buffer;
2714     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2715         Irp->MdlAddress = IoAllocateMdl(Buffer, Length, false, false, NULL);
2716         if (!Irp->MdlAddress) {
2717             ERR("IoAllocateMdl failed\n");
2718             Status = STATUS_INSUFFICIENT_RESOURCES;
2719             goto exit;
2720         }
2721 
2722         Status = STATUS_SUCCESS;
2723 
2724         _SEH2_TRY {
2725             MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2726         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2727             Status = _SEH2_GetExceptionCode();
2728         } _SEH2_END;
2729 
2730         if (!NT_SUCCESS(Status)) {
2731             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
2732             IoFreeMdl(Irp->MdlAddress);
2733             goto exit;
2734         }
2735     } else
2736         Irp->UserBuffer = Buffer;
2737 
2738     IrpSp->Parameters.Read.Length = Length;
2739     IrpSp->Parameters.Read.ByteOffset = Offset;
2740 
2741     Irp->UserIosb = &IoStatus;
2742 
2743     Irp->UserEvent = &context.Event;
2744 
2745     IoSetCompletionRoutine(Irp, read_completion, &context, true, true, true);
2746 
2747     Status = IoCallDriver(DeviceObject, Irp);
2748 
2749     if (Status == STATUS_PENDING) {
2750         KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
2751         Status = context.iosb.Status;
2752     }
2753 
2754     if (DeviceObject->Flags & DO_DIRECT_IO) {
2755         MmUnlockPages(Irp->MdlAddress);
2756         IoFreeMdl(Irp->MdlAddress);
2757     }
2758 
2759 exit:
2760     IoFreeIrp(Irp);
2761 
2762     return Status;
2763 }
2764 
2765 static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj, _In_ uint64_t length) {
2766     NTSTATUS Status;
2767     superblock* sb;
2768     ULONG i, to_read;
2769     uint8_t valid_superblocks;
2770 
2771     to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
2772 
2773     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2774     if (!sb) {
2775         ERR("out of memory\n");
2776         return STATUS_INSUFFICIENT_RESOURCES;
2777     }
2778 
2779     if (superblock_addrs[0] + to_read > length) {
2780         WARN("device was too short to have any superblock\n");
2781         ExFreePool(sb);
2782         return STATUS_UNRECOGNIZED_VOLUME;
2783     }
2784 
2785     i = 0;
2786     valid_superblocks = 0;
2787 
2788     while (superblock_addrs[i] > 0) {
2789         uint32_t crc32;
2790 
2791         if (i > 0 && superblock_addrs[i] + to_read > length)
2792             break;
2793 
2794         Status = sync_read_phys(device, fileobj, superblock_addrs[i], to_read, (PUCHAR)sb, false);
2795         if (!NT_SUCCESS(Status)) {
2796             ERR("Failed to read superblock %u: %08x\n", i, Status);
2797             ExFreePool(sb);
2798             return Status;
2799         }
2800 
2801         if (sb->magic != BTRFS_MAGIC) {
2802             if (i == 0) {
2803                 TRACE("not a BTRFS volume\n");
2804                 ExFreePool(sb);
2805                 return STATUS_UNRECOGNIZED_VOLUME;
2806             }
2807         } else {
2808             TRACE("got superblock %u!\n", i);
2809 
2810             crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2811 
2812             if (crc32 != *((uint32_t*)sb->checksum))
2813                 WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
2814             else if (sb->sector_size == 0)
2815                 WARN("superblock sector size was 0\n");
2816             else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
2817                 WARN("invalid node size %x\n", sb->node_size);
2818             else if ((sb->node_size % sb->sector_size) != 0)
2819                 WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
2820             else if (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation) {
2821                 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2822                 valid_superblocks++;
2823             }
2824         }
2825 
2826         i++;
2827     }
2828 
2829     ExFreePool(sb);
2830 
2831     if (valid_superblocks == 0) {
2832         ERR("could not find any valid superblocks\n");
2833         return STATUS_INTERNAL_ERROR;
2834     }
2835 
2836     TRACE("label is %s\n", Vcb->superblock.label);
2837 
2838     return STATUS_SUCCESS;
2839 }
2840 
2841 NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
2842                    _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ bool Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
2843     PIRP Irp;
2844     KEVENT Event;
2845     NTSTATUS Status;
2846     PIO_STACK_LOCATION IrpSp;
2847     IO_STATUS_BLOCK IoStatus;
2848 
2849     KeInitializeEvent(&Event, NotificationEvent, false);
2850 
2851     Irp = IoBuildDeviceIoControlRequest(ControlCode,
2852                                         DeviceObject,
2853                                         InputBuffer,
2854                                         InputBufferSize,
2855                                         OutputBuffer,
2856                                         OutputBufferSize,
2857                                         false,
2858                                         &Event,
2859                                         &IoStatus);
2860 
2861     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2862 
2863     if (Override) {
2864         IrpSp = IoGetNextIrpStackLocation(Irp);
2865         IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2866     }
2867 
2868     Status = IoCallDriver(DeviceObject, Irp);
2869 
2870     if (Status == STATUS_PENDING) {
2871         KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL);
2872         Status = IoStatus.Status;
2873     }
2874 
2875     if (iosb)
2876         *iosb = IoStatus;
2877 
2878     return Status;
2879 }
2880 
2881 _Requires_exclusive_lock_held_(Vcb->tree_lock)
2882 static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ uint64_t id, _In_ uint64_t addr,
2883                          _In_ uint64_t generation, _In_opt_ traverse_ptr* tp) {
2884     root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2885     if (!r) {
2886         ERR("out of memory\n");
2887         return STATUS_INSUFFICIENT_RESOURCES;
2888     }
2889 
2890     r->id = id;
2891     r->dirty = false;
2892     r->received = false;
2893     r->reserved = NULL;
2894     r->treeholder.address = addr;
2895     r->treeholder.tree = NULL;
2896     r->treeholder.generation = generation;
2897     r->parent = 0;
2898     r->send_ops = 0;
2899     r->fcbs_version = 0;
2900     r->checked_for_orphans = false;
2901     InitializeListHead(&r->fcbs);
2902     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
2903 
2904     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2905     if (!r->nonpaged) {
2906         ERR("out of memory\n");
2907         ExFreePool(r);
2908         return STATUS_INSUFFICIENT_RESOURCES;
2909     }
2910 
2911     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2912 
2913     r->lastinode = 0;
2914 
2915     if (tp) {
2916         RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2917         if (tp->item->size < sizeof(ROOT_ITEM))
2918             RtlZeroMemory(((uint8_t*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2919     } else
2920         RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
2921 
2922     if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
2923         // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2924         get_last_inode(Vcb, r, NULL);
2925 
2926         if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
2927             r->lastinode = 0x100;
2928     }
2929 
2930     InsertTailList(&Vcb->roots, &r->list_entry);
2931 
2932     switch (r->id) {
2933         case BTRFS_ROOT_ROOT:
2934             Vcb->root_root = r;
2935             break;
2936 
2937         case BTRFS_ROOT_EXTENT:
2938             Vcb->extent_root = r;
2939             break;
2940 
2941         case BTRFS_ROOT_CHUNK:
2942             Vcb->chunk_root = r;
2943             break;
2944 
2945         case BTRFS_ROOT_DEVTREE:
2946             Vcb->dev_root = r;
2947             break;
2948 
2949         case BTRFS_ROOT_CHECKSUM:
2950             Vcb->checksum_root = r;
2951             break;
2952 
2953         case BTRFS_ROOT_UUID:
2954             Vcb->uuid_root = r;
2955             break;
2956 
2957         case BTRFS_ROOT_FREE_SPACE:
2958             Vcb->space_root = r;
2959             break;
2960 
2961         case BTRFS_ROOT_DATA_RELOC:
2962             Vcb->data_reloc_root = r;
2963             break;
2964     }
2965 
2966     return STATUS_SUCCESS;
2967 }
2968 
2969 static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock) _In_ device_extension* Vcb, _In_opt_ PIRP Irp) {
2970     traverse_ptr tp, next_tp;
2971     KEY searchkey;
2972     bool b;
2973     NTSTATUS Status;
2974 
2975     searchkey.obj_id = 0;
2976     searchkey.obj_type = 0;
2977     searchkey.offset = 0;
2978 
2979     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
2980     if (!NT_SUCCESS(Status)) {
2981         ERR("error - find_item returned %08x\n", Status);
2982         return Status;
2983     }
2984 
2985     do {
2986         TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2987 
2988         if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2989             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2990 
2991             if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2992                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_ITEM, byte_limit));
2993             } else {
2994                 TRACE("root %I64x - address %I64x\n", tp.item->key.obj_id, ri->block_number);
2995 
2996                 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
2997                 if (!NT_SUCCESS(Status)) {
2998                     ERR("add_root returned %08x\n", Status);
2999                     return Status;
3000                 }
3001             }
3002         } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
3003             root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
3004 
3005             if (lastroot->id == tp.item->key.obj_id)
3006                 lastroot->parent = tp.item->key.offset;
3007         }
3008 
3009         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3010 
3011         if (b)
3012             tp = next_tp;
3013     } while (b);
3014 
3015     if (!Vcb->readonly && !Vcb->data_reloc_root) {
3016         root* reloc_root;
3017         INODE_ITEM* ii;
3018         uint16_t irlen;
3019         INODE_REF* ir;
3020         LARGE_INTEGER time;
3021         BTRFS_TIME now;
3022 
3023         WARN("data reloc root doesn't exist, creating it\n");
3024 
3025         Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, false, 0, Irp);
3026 
3027         if (!NT_SUCCESS(Status)) {
3028             ERR("create_root returned %08x\n", Status);
3029             return Status;
3030         }
3031 
3032         reloc_root->root_item.inode.generation = 1;
3033         reloc_root->root_item.inode.st_size = 3;
3034         reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
3035         reloc_root->root_item.inode.st_nlink = 1;
3036         reloc_root->root_item.inode.st_mode = 040755;
3037         reloc_root->root_item.inode.flags = 0xffffffff80000000;
3038         reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
3039         reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
3040 
3041         ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
3042         if (!ii) {
3043             ERR("out of memory\n");
3044             return STATUS_INSUFFICIENT_RESOURCES;
3045         }
3046 
3047         KeQuerySystemTime(&time);
3048         win_time_to_unix(time, &now);
3049 
3050         RtlZeroMemory(ii, sizeof(INODE_ITEM));
3051         ii->generation = Vcb->superblock.generation;
3052         ii->st_blocks = Vcb->superblock.node_size;
3053         ii->st_nlink = 1;
3054         ii->st_mode = 040755;
3055         ii->st_atime = now;
3056         ii->st_ctime = now;
3057         ii->st_mtime = now;
3058 
3059         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
3060         if (!NT_SUCCESS(Status)) {
3061             ERR("insert_tree_item returned %08x\n", Status);
3062             ExFreePool(ii);
3063             return Status;
3064         }
3065 
3066         irlen = (uint16_t)offsetof(INODE_REF, name[0]) + 2;
3067         ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
3068         if (!ir) {
3069             ERR("out of memory\n");
3070             return STATUS_INSUFFICIENT_RESOURCES;
3071         }
3072 
3073         ir->index = 0;
3074         ir->n = 2;
3075         ir->name[0] = '.';
3076         ir->name[1] = '.';
3077 
3078         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
3079         if (!NT_SUCCESS(Status)) {
3080             ERR("insert_tree_item returned %08x\n", Status);
3081             ExFreePool(ir);
3082             return Status;
3083         }
3084 
3085         Vcb->data_reloc_root = reloc_root;
3086         Vcb->need_write = true;
3087     }
3088 
3089     return STATUS_SUCCESS;
3090 }
3091 
3092 static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ device* dev, _In_opt_ PIRP Irp) {
3093     KEY searchkey;
3094     traverse_ptr tp, next_tp;
3095     bool b;
3096     uint64_t lastaddr;
3097     NTSTATUS Status;
3098 
3099     InitializeListHead(&dev->space);
3100 
3101     searchkey.obj_id = 0;
3102     searchkey.obj_type = TYPE_DEV_STATS;
3103     searchkey.offset = dev->devitem.dev_id;
3104 
3105     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3106     if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
3107         RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(uint64_t) * 5, tp.item->size));
3108 
3109     searchkey.obj_id = dev->devitem.dev_id;
3110     searchkey.obj_type = TYPE_DEV_EXTENT;
3111     searchkey.offset = 0;
3112 
3113     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3114     if (!NT_SUCCESS(Status)) {
3115         ERR("error - find_item returned %08x\n", Status);
3116         return Status;
3117     }
3118 
3119     lastaddr = 0;
3120 
3121     do {
3122         if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
3123             if (tp.item->size >= sizeof(DEV_EXTENT)) {
3124                 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
3125 
3126                 if (tp.item->key.offset > lastaddr) {
3127                     Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
3128                     if (!NT_SUCCESS(Status)) {
3129                         ERR("add_space_entry returned %08x\n", Status);
3130                         return Status;
3131                     }
3132                 }
3133 
3134                 lastaddr = tp.item->key.offset + de->length;
3135             } else {
3136                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
3137             }
3138         }
3139 
3140         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3141 
3142         if (b) {
3143             tp = next_tp;
3144             if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
3145                 break;
3146         }
3147     } while (b);
3148 
3149     if (lastaddr < dev->devitem.num_bytes) {
3150         Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
3151         if (!NT_SUCCESS(Status)) {
3152             ERR("add_space_entry returned %08x\n", Status);
3153             return Status;
3154         }
3155     }
3156 
3157     // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3158 
3159     space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
3160 
3161     return STATUS_SUCCESS;
3162 }
3163 
3164 static void add_device_to_list(_In_ device_extension* Vcb, _In_ device* dev) {
3165     LIST_ENTRY* le;
3166 
3167     le = Vcb->devices.Flink;
3168 
3169     while (le != &Vcb->devices) {
3170         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
3171 
3172         if (dev2->devitem.dev_id > dev->devitem.dev_id) {
3173             InsertHeadList(le->Blink, &dev->list_entry);
3174             return;
3175         }
3176 
3177         le = le->Flink;
3178     }
3179 
3180     InsertTailList(&Vcb->devices, &dev->list_entry);
3181 }
3182 
3183 _Ret_maybenull_
3184 device* find_device_from_uuid(_In_ device_extension* Vcb, _In_ BTRFS_UUID* uuid) {
3185     volume_device_extension* vde;
3186     pdo_device_extension* pdode;
3187     LIST_ENTRY* le;
3188 
3189     le = Vcb->devices.Flink;
3190     while (le != &Vcb->devices) {
3191         device* dev = CONTAINING_RECORD(le, device, list_entry);
3192 
3193         TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
3194             dev->devitem.device_uuid.uuid[0], dev->devitem.device_uuid.uuid[1], dev->devitem.device_uuid.uuid[2], dev->devitem.device_uuid.uuid[3], dev->devitem.device_uuid.uuid[4], dev->devitem.device_uuid.uuid[5], dev->devitem.device_uuid.uuid[6], dev->devitem.device_uuid.uuid[7],
3195             dev->devitem.device_uuid.uuid[8], dev->devitem.device_uuid.uuid[9], dev->devitem.device_uuid.uuid[10], dev->devitem.device_uuid.uuid[11], dev->devitem.device_uuid.uuid[12], dev->devitem.device_uuid.uuid[13], dev->devitem.device_uuid.uuid[14], dev->devitem.device_uuid.uuid[15]);
3196 
3197         if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3198             TRACE("returning device %I64x\n", dev->devitem.dev_id);
3199             return dev;
3200         }
3201 
3202         le = le->Flink;
3203     }
3204 
3205     vde = Vcb->vde;
3206 
3207     if (!vde)
3208         goto end;
3209 
3210     pdode = vde->pdode;
3211 
3212     ExAcquireResourceSharedLite(&pdode->child_lock, true);
3213 
3214     if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3215         le = pdode->children.Flink;
3216 
3217         while (le != &pdode->children) {
3218             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
3219 
3220             if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3221                 device* dev;
3222 
3223                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3224                 if (!dev) {
3225                     ExReleaseResourceLite(&pdode->child_lock);
3226                     ERR("out of memory\n");
3227                     return NULL;
3228                 }
3229 
3230                 RtlZeroMemory(dev, sizeof(device));
3231                 dev->devobj = vc->devobj;
3232                 dev->fileobj = vc->fileobj;
3233                 dev->devitem.device_uuid = *uuid;
3234                 dev->devitem.dev_id = vc->devid;
3235                 dev->devitem.num_bytes = vc->size;
3236                 dev->seeding = vc->seeding;
3237                 dev->readonly = dev->seeding;
3238                 dev->reloc = false;
3239                 dev->removable = false;
3240                 dev->disk_num = vc->disk_num;
3241                 dev->part_num = vc->part_num;
3242                 dev->num_trim_entries = 0;
3243                 InitializeListHead(&dev->trim_list);
3244 
3245                 add_device_to_list(Vcb, dev);
3246                 Vcb->devices_loaded++;
3247 
3248                 ExReleaseResourceLite(&pdode->child_lock);
3249 
3250                 return dev;
3251             }
3252 
3253             le = le->Flink;
3254         }
3255     }
3256 
3257     ExReleaseResourceLite(&pdode->child_lock);
3258 
3259 end:
3260     WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3261          uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
3262          uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
3263 
3264     return NULL;
3265 }
3266 
3267 static bool is_device_removable(_In_ PDEVICE_OBJECT devobj) {
3268     NTSTATUS Status;
3269     STORAGE_HOTPLUG_INFO shi;
3270 
3271     Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), true, NULL);
3272 
3273     if (!NT_SUCCESS(Status)) {
3274         ERR("dev_ioctl returned %08x\n", Status);
3275         return false;
3276     }
3277 
3278     return shi.MediaRemovable != 0 ? true : false;
3279 }
3280 
3281 static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
3282     NTSTATUS Status;
3283     ULONG cc;
3284     IO_STATUS_BLOCK iosb;
3285 
3286     Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
3287 
3288     if (!NT_SUCCESS(Status)) {
3289         ERR("dev_ioctl returned %08x\n", Status);
3290         return 0;
3291     }
3292 
3293     if (iosb.Information < sizeof(ULONG)) {
3294         ERR("iosb.Information was too short\n");
3295         return 0;
3296     }
3297 
3298     return cc;
3299 }
3300 
3301 void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ bool get_nums) {
3302     NTSTATUS Status;
3303     ULONG aptelen;
3304     ATA_PASS_THROUGH_EX* apte;
3305     STORAGE_PROPERTY_QUERY spq;
3306     DEVICE_TRIM_DESCRIPTOR dtd;
3307 
3308     dev->removable = is_device_removable(dev->devobj);
3309     dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
3310 
3311     if (get_nums) {
3312         STORAGE_DEVICE_NUMBER sdn;
3313 
3314         Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
3315                            &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
3316 
3317         if (!NT_SUCCESS(Status)) {
3318             WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
3319             dev->disk_num = 0xffffffff;
3320             dev->part_num = 0xffffffff;
3321         } else {
3322             dev->disk_num = sdn.DeviceNumber;
3323             dev->part_num = sdn.PartitionNumber;
3324         }
3325     }
3326 
3327     dev->trim = false;
3328     dev->readonly = dev->seeding;
3329     dev->reloc = false;
3330     dev->num_trim_entries = 0;
3331     dev->stats_changed = false;
3332     InitializeListHead(&dev->trim_list);
3333 
3334     if (!dev->readonly) {
3335         Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
3336                         NULL, 0, true, NULL);
3337         if (Status == STATUS_MEDIA_WRITE_PROTECTED)
3338             dev->readonly = true;
3339     }
3340 
3341     aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
3342     apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
3343     if (!apte) {
3344         ERR("out of memory\n");
3345         return;
3346     }
3347 
3348     RtlZeroMemory(apte, aptelen);
3349 
3350     apte->Length = sizeof(ATA_PASS_THROUGH_EX);
3351     apte->AtaFlags = ATA_FLAGS_DATA_IN;
3352     apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
3353     apte->TimeOutValue = 3;
3354     apte->DataBufferOffset = apte->Length;
3355     apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
3356 
3357     Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
3358                        apte, aptelen, true, NULL);
3359 
3360     if (!NT_SUCCESS(Status))
3361         TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status);
3362     else {
3363         IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((uint8_t*)apte + sizeof(ATA_PASS_THROUGH_EX));
3364 
3365         if (idd->CommandSetSupport.FlushCache) {
3366             dev->can_flush = true;
3367             TRACE("FLUSH CACHE supported\n");
3368         } else
3369             TRACE("FLUSH CACHE not supported\n");
3370     }
3371 
3372     ExFreePool(apte);
3373 
3374 #ifdef DEBUG_TRIM_EMULATION
3375     dev->trim = true;
3376     Vcb->trim = true;
3377 #else
3378     spq.PropertyId = StorageDeviceTrimProperty;
3379     spq.QueryType = PropertyStandardQuery;
3380     spq.AdditionalParameters[0] = 0;
3381 
3382     Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
3383                        &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), true, NULL);
3384 
3385     if (NT_SUCCESS(Status)) {
3386         if (dtd.TrimEnabled) {
3387             dev->trim = true;
3388             Vcb->trim = true;
3389             TRACE("TRIM supported\n");
3390         } else
3391             TRACE("TRIM not supported\n");
3392     }
3393 #endif
3394 
3395     RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3396 }
3397 
3398 static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3399     traverse_ptr tp, next_tp;
3400     KEY searchkey;
3401     bool b;
3402     chunk* c;
3403     NTSTATUS Status;
3404 
3405     searchkey.obj_id = 0;
3406     searchkey.obj_type = 0;
3407     searchkey.offset = 0;
3408 
3409     Vcb->data_flags = 0;
3410     Vcb->metadata_flags = 0;
3411     Vcb->system_flags = 0;
3412 
3413     Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp);
3414     if (!NT_SUCCESS(Status)) {
3415         ERR("error - find_item returned %08x\n", Status);
3416         return Status;
3417     }
3418 
3419     do {
3420         TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3421 
3422         if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
3423             if (tp.item->size < sizeof(DEV_ITEM)) {
3424                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
3425             } else {
3426                 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
3427                 LIST_ENTRY* le;
3428                 bool done = false;
3429 
3430                 le = Vcb->devices.Flink;
3431                 while (le != &Vcb->devices) {
3432                     device* dev = CONTAINING_RECORD(le, device, list_entry);
3433 
3434                     if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3435                         RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3436 
3437                         if (le != Vcb->devices.Flink)
3438                             init_device(Vcb, dev, true);
3439 
3440                         done = true;
3441                         break;
3442                     }
3443 
3444                     le = le->Flink;
3445                 }
3446 
3447                 if (!done && Vcb->vde) {
3448                     volume_device_extension* vde = Vcb->vde;
3449                     pdo_device_extension* pdode = vde->pdode;
3450 
3451                     ExAcquireResourceSharedLite(&pdode->child_lock, true);
3452 
3453                     if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3454                         le = pdode->children.Flink;
3455 
3456                         while (le != &pdode->children) {
3457                             volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
3458 
3459                             if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3460                                 device* dev;
3461 
3462                                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3463                                 if (!dev) {
3464                                     ExReleaseResourceLite(&pdode->child_lock);
3465                                     ERR("out of memory\n");
3466                                     return STATUS_INSUFFICIENT_RESOURCES;
3467                                 }
3468 
3469                                 RtlZeroMemory(dev, sizeof(device));
3470 
3471                                 dev->devobj = vc->devobj;
3472                                 dev->fileobj = vc->fileobj;
3473                                 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3474                                 dev->seeding = vc->seeding;
3475                                 init_device(Vcb, dev, false);
3476 
3477                                 if (dev->devitem.num_bytes > vc->size) {
3478                                     WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp.item->key.offset,
3479                                          dev->devitem.num_bytes, vc->size);
3480 
3481                                     dev->devitem.num_bytes = vc->size;
3482                                 }
3483 
3484                                 dev->disk_num = vc->disk_num;
3485                                 dev->part_num = vc->part_num;
3486                                 add_device_to_list(Vcb, dev);
3487                                 Vcb->devices_loaded++;
3488 
3489                                 done = true;
3490                                 break;
3491                             }
3492 
3493                             le = le->Flink;
3494                         }
3495 
3496                         if (!done) {
3497                             if (!Vcb->options.allow_degraded) {
3498                                 ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
3499                                     di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
3500                                     di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
3501                             } else {
3502                                 device* dev;
3503 
3504                                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3505                                 if (!dev) {
3506                                     ExReleaseResourceLite(&pdode->child_lock);
3507                                     ERR("out of memory\n");
3508                                     return STATUS_INSUFFICIENT_RESOURCES;
3509                                 }
3510 
3511                                 RtlZeroMemory(dev, sizeof(device));
3512 
3513                                 // Missing device, so we keep dev->devobj as NULL
3514                                 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3515                                 InitializeListHead(&dev->trim_list);
3516 
3517                                 add_device_to_list(Vcb, dev);
3518                                 Vcb->devices_loaded++;
3519                             }
3520                         }
3521                     } else
3522                         ERR("unexpected device %I64x found\n", tp.item->key.offset);
3523 
3524                     ExReleaseResourceLite(&pdode->child_lock);
3525                 }
3526             }
3527         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
3528             if (tp.item->size < sizeof(CHUNK_ITEM)) {
3529                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
3530             } else {
3531                 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
3532 
3533                 if (!c) {
3534                     ERR("out of memory\n");
3535                     return STATUS_INSUFFICIENT_RESOURCES;
3536                 }
3537 
3538                 c->size = tp.item->size;
3539                 c->offset = tp.item->key.offset;
3540                 c->used = c->oldused = 0;
3541                 c->cache = c->old_cache = NULL;
3542                 c->created = false;
3543                 c->readonly = false;
3544                 c->reloc = false;
3545                 c->cache_loaded = false;
3546                 c->changed = false;
3547                 c->space_changed = false;
3548                 c->balance_num = 0;
3549 
3550                 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
3551 
3552                 if (!c->chunk_item) {
3553                     ERR("out of memory\n");
3554                     ExFreePool(c);
3555                     return STATUS_INSUFFICIENT_RESOURCES;
3556                 }
3557 
3558                 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3559 
3560                 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
3561                     Vcb->data_flags = c->chunk_item->type;
3562 
3563                 if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
3564                     Vcb->metadata_flags = c->chunk_item->type;
3565 
3566                 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
3567                     Vcb->system_flags = c->chunk_item->type;
3568 
3569                 if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
3570                     if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
3571                         ERR("chunk %I64x: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
3572                         ExFreePool(c->chunk_item);
3573                         ExFreePool(c);
3574                         return STATUS_INTERNAL_ERROR;
3575                     }
3576                 }
3577 
3578                 if (c->chunk_item->num_stripes > 0) {
3579                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3580                     uint16_t i;
3581 
3582                     c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3583 
3584                     if (!c->devices) {
3585                         ERR("out of memory\n");
3586                         ExFreePool(c->chunk_item);
3587                         ExFreePool(c);
3588                         return STATUS_INSUFFICIENT_RESOURCES;
3589                     }
3590 
3591                     for (i = 0; i < c->chunk_item->num_stripes; i++) {
3592                         c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3593                         TRACE("device %I64u = %p\n", i, c->devices[i]);
3594 
3595                         if (!c->devices[i]) {
3596                             ERR("missing device\n");
3597                             ExFreePool(c->chunk_item);
3598                             ExFreePool(c);
3599                             return STATUS_INTERNAL_ERROR;
3600                         }
3601 
3602                         if (c->devices[i]->readonly)
3603                             c->readonly = true;
3604                     }
3605                 } else {
3606                     ERR("chunk %I64x: number of stripes is 0\n", c->offset);
3607                     ExFreePool(c->chunk_item);
3608                     ExFreePool(c);
3609                     return STATUS_INTERNAL_ERROR;
3610                 }
3611 
3612                 ExInitializeResourceLite(&c->lock);
3613                 ExInitializeResourceLite(&c->changed_extents_lock);
3614 
3615                 InitializeListHead(&c->space);
3616                 InitializeListHead(&c->space_size);
3617                 InitializeListHead(&c->deleting);
3618                 InitializeListHead(&c->changed_extents);
3619 
3620                 InitializeListHead(&c->range_locks);
3621                 ExInitializeResourceLite(&c->range_locks_lock);
3622                 KeInitializeEvent(&c->range_locks_event, NotificationEvent, false);
3623 
3624                 InitializeListHead(&c->partial_stripes);
3625                 ExInitializeResourceLite(&c->partial_stripes_lock);
3626 
3627                 c->last_alloc_set = false;
3628 
3629                 c->last_stripe = 0;
3630 
3631                 InsertTailList(&Vcb->chunks, &c->list_entry);
3632 
3633                 c->list_entry_balance.Flink = NULL;
3634             }
3635         }
3636 
3637         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3638 
3639         if (b)
3640             tp = next_tp;
3641     } while (b);
3642 
3643     Vcb->log_to_phys_loaded = true;
3644 
3645     if (Vcb->data_flags == 0)
3646         Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3647 
3648     if (Vcb->metadata_flags == 0)
3649         Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3650 
3651     if (Vcb->system_flags == 0)
3652         Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3653 
3654     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
3655         Vcb->metadata_flags |= BLOCK_FLAG_DATA;
3656         Vcb->data_flags = Vcb->metadata_flags;
3657     }
3658 
3659     return STATUS_SUCCESS;
3660 }
3661 
3662 void protect_superblocks(_Inout_ chunk* c) {
3663     uint16_t i = 0, j;
3664     uint64_t off_start, off_end;
3665 
3666     // The Linux driver also protects all the space before the first superblock.
3667     // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3668     // evidently Linux assumes the chunk at 0 is always SINGLE.
3669     if (c->offset < superblock_addrs[0])
3670         space_list_subtract(c, false, c->offset, superblock_addrs[0] - c->offset, NULL);
3671 
3672     while (superblock_addrs[i] != 0) {
3673         CHUNK_ITEM* ci = c->chunk_item;
3674         CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3675 
3676         if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3677             for (j = 0; j < ci->num_stripes; j++) {
3678                 uint16_t sub_stripes = max(ci->sub_stripes, 1);
3679 
3680                 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3681 #ifdef _DEBUG
3682                     uint64_t startoff;
3683                     uint16_t startoffstripe;
3684 #endif
3685 
3686                     TRACE("cut out superblock in chunk %I64x\n", c->offset);
3687 
3688                     off_start = superblock_addrs[i] - cis[j].offset;
3689                     off_start -= off_start % ci->stripe_length;
3690                     off_start *= ci->num_stripes / sub_stripes;
3691                     off_start += (j / sub_stripes) * ci->stripe_length;
3692 
3693                     off_end = off_start + ci->stripe_length;
3694 
3695 #ifdef _DEBUG
3696                     get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3697                     TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3698                     TRACE("startoff = %I64x, superblock = %I64x\n", startoff + cis[j].offset, superblock_addrs[i]);
3699 #endif
3700 
3701                     space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3702                 }
3703             }
3704         } else if (ci->type & BLOCK_FLAG_RAID5) {
3705             uint64_t stripe_size = ci->size / (ci->num_stripes - 1);
3706 
3707             for (j = 0; j < ci->num_stripes; j++) {
3708                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3709                     TRACE("cut out superblock in chunk %I64x\n", c->offset);
3710 
3711                     off_start = superblock_addrs[i] - cis[j].offset;
3712                     off_start -= off_start % ci->stripe_length;
3713                     off_start *= ci->num_stripes - 1;
3714 
3715                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3716                     off_end *= ci->num_stripes - 1;
3717 
3718                     TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3719 
3720                     space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3721                 }
3722             }
3723         } else if (ci->type & BLOCK_FLAG_RAID6) {
3724             uint64_t stripe_size = ci->size / (ci->num_stripes - 2);
3725 
3726             for (j = 0; j < ci->num_stripes; j++) {
3727                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3728                     TRACE("cut out superblock in chunk %I64x\n", c->offset);
3729 
3730                     off_start = superblock_addrs[i] - cis[j].offset;
3731                     off_start -= off_start % ci->stripe_length;
3732                     off_start *= ci->num_stripes - 2;
3733 
3734                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3735                     off_end *= ci->num_stripes - 2;
3736 
3737                     TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3738 
3739                     space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3740                 }
3741             }
3742         } else { // SINGLE, DUPLICATE, RAID1
3743             for (j = 0; j < ci->num_stripes; j++) {
3744                 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3745                     TRACE("cut out superblock in chunk %I64x\n", c->offset);
3746 
3747                     // The Linux driver protects the whole stripe in which the superblock lives
3748 
3749                     off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3750                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3751 
3752                     space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3753                 }
3754             }
3755         }
3756 
3757         i++;
3758     }
3759 }
3760 
3761 uint64_t chunk_estimate_phys_size(device_extension* Vcb, chunk* c, uint64_t u) {
3762     uint64_t nfactor, dfactor;
3763 
3764     if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE || c->chunk_item->type & BLOCK_FLAG_RAID1 || c->chunk_item->type & BLOCK_FLAG_RAID10) {
3765         nfactor = 1;
3766         dfactor = 2;
3767     } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
3768         nfactor = Vcb->superblock.num_devices - 1;
3769         dfactor = Vcb->superblock.num_devices;
3770     } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
3771         nfactor = Vcb->superblock.num_devices - 2;
3772         dfactor = Vcb->superblock.num_devices;
3773     } else {
3774         nfactor = 1;
3775         dfactor = 1;
3776     }
3777 
3778     return u * dfactor / nfactor;
3779 }
3780 
3781 NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3782     LIST_ENTRY* le = Vcb->chunks.Flink;
3783     chunk* c;
3784     KEY searchkey;
3785     traverse_ptr tp;
3786     BLOCK_GROUP_ITEM* bgi;
3787     NTSTATUS Status;
3788 
3789     searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3790 
3791     Vcb->superblock.bytes_used = 0;
3792 
3793     while (le != &Vcb->chunks) {
3794         c = CONTAINING_RECORD(le, chunk, list_entry);
3795 
3796         searchkey.obj_id = c->offset;
3797         searchkey.offset = c->chunk_item->size;
3798 
3799         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
3800         if (!NT_SUCCESS(Status)) {
3801             ERR("error - find_item returned %08x\n", Status);
3802             return Status;
3803         }
3804 
3805         if (!keycmp(searchkey, tp.item->key)) {
3806             if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3807                 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3808 
3809                 c->used = c->oldused = bgi->used;
3810 
3811                 TRACE("chunk %I64x has %I64x bytes used\n", c->offset, c->used);
3812 
3813                 Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, bgi->used);
3814             } else {
3815                 ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %u\n",
3816                     Vcb->extent_root->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
3817             }
3818         }
3819 
3820         le = le->Flink;
3821     }
3822 
3823     Vcb->chunk_usage_found = true;
3824 
3825     return STATUS_SUCCESS;
3826 }
3827 
3828 static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
3829     KEY key;
3830     ULONG n = Vcb->superblock.n;
3831 
3832     while (n > 0) {
3833         if (n > sizeof(KEY)) {
3834             RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3835             n -= sizeof(KEY);
3836         } else
3837             return STATUS_SUCCESS;
3838 
3839         TRACE("bootstrap: %I64x,%x,%I64x\n", key.obj_id, key.obj_type, key.offset);
3840 
3841         if (key.obj_type == TYPE_CHUNK_ITEM) {
3842             CHUNK_ITEM* ci;
3843             USHORT cisize;
3844             sys_chunk* sc;
3845 
3846             if (n < sizeof(CHUNK_ITEM))
3847                 return STATUS_SUCCESS;
3848 
3849             ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3850             cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3851 
3852             if (n < cisize)
3853                 return STATUS_SUCCESS;
3854 
3855             sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3856 
3857             if (!sc) {
3858                 ERR("out of memory\n");
3859                 return STATUS_INSUFFICIENT_RESOURCES;
3860             }
3861 
3862             sc->key = key;
3863             sc->size = cisize;
3864             sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3865 
3866             if (!sc->data) {
3867                 ERR("out of memory\n");
3868                 ExFreePool(sc);
3869                 return STATUS_INSUFFICIENT_RESOURCES;
3870             }
3871 
3872             RtlCopyMemory(sc->data, ci, sc->size);
3873             InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3874 
3875             n -= cisize;
3876         } else {
3877             ERR("unexpected item %I64x,%x,%I64x in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3878             return STATUS_INTERNAL_ERROR;
3879         }
3880     }
3881 
3882     return STATUS_SUCCESS;
3883 }
3884 
3885 _Ret_maybenull_
3886 static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3887     LIST_ENTRY* le;
3888 
3889     static const char fn[] = "default";
3890     static uint32_t crc32 = 0x8dbfc2d2;
3891 
3892     if (Vcb->options.subvol_id != 0) {
3893         le = Vcb->roots.Flink;
3894         while (le != &Vcb->roots) {
3895             root* r = CONTAINING_RECORD(le, root, list_entry);
3896 
3897             if (r->id == Vcb->options.subvol_id)
3898                 return r;
3899 
3900             le = le->Flink;
3901         }
3902     }
3903 
3904     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3905         NTSTATUS Status;
3906         KEY searchkey;
3907         traverse_ptr tp;
3908         DIR_ITEM* di;
3909 
3910         searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3911         searchkey.obj_type = TYPE_DIR_ITEM;
3912         searchkey.offset = crc32;
3913 
3914         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
3915         if (!NT_SUCCESS(Status)) {
3916             ERR("error - find_item returned %08x\n", Status);
3917             goto end;
3918         }
3919 
3920         if (keycmp(tp.item->key, searchkey)) {
3921             ERR("could not find (%I64x,%x,%I64x) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3922             goto end;
3923         }
3924 
3925         if (tp.item->size < sizeof(DIR_ITEM)) {
3926             ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
3927             goto end;
3928         }
3929 
3930         di = (DIR_ITEM*)tp.item->data;
3931 
3932         if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3933             ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n);
3934             goto end;
3935         }
3936 
3937         if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3938             ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3939             goto end;
3940         }
3941 
3942         if (di->key.obj_type != TYPE_ROOT_ITEM) {
3943             ERR("default root has key (%I64x,%x,%I64x), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3944             goto end;
3945         }
3946 
3947         le = Vcb->roots.Flink;
3948         while (le != &Vcb->roots) {
3949             root* r = CONTAINING_RECORD(le, root, list_entry);
3950 
3951             if (r->id == di->key.obj_id)
3952                 return r;
3953 
3954             le = le->Flink;
3955         }
3956 
3957         ERR("could not find root %I64x, using default instead\n", di->key.obj_id);
3958     }
3959 
3960 end:
3961     le = Vcb->roots.Flink;
3962     while (le != &Vcb->roots) {
3963         root* r = CONTAINING_RECORD(le, root, list_entry);
3964 
3965         if (r->id == BTRFS_ROOT_FSTREE)
3966             return r;
3967 
3968         le = le->Flink;
3969     }
3970 
3971     return NULL;
3972 }
3973 
3974 void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
3975     TRACE("(%p, %p)\n", FileObject, ccfs);
3976 
3977     CcInitializeCacheMap(FileObject, ccfs, false, cache_callbacks, FileObject);
3978 
3979     if (diskacc)
3980         fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
3981 
3982     CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
3983 }
3984 
3985 uint32_t get_num_of_processors() {
3986     KAFFINITY p = KeQueryActiveProcessors();
3987     uint32_t r = 0;
3988 
3989     while (p != 0) {
3990         if (p & 1)
3991             r++;
3992 
3993         p >>= 1;
3994     }
3995 
3996     return r;
3997 }
3998 
3999 static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
4000     device_extension* Vcb = DeviceObject->DeviceExtension;
4001     ULONG i;
4002 
4003     Vcb->calcthreads.num_threads = get_num_of_processors();
4004 
4005     Vcb->calcthreads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads, ALLOC_TAG);
4006     if (!Vcb->calcthreads.threads) {
4007         ERR("out of memory\n");
4008         return STATUS_INSUFFICIENT_RESOURCES;
4009     }
4010 
4011     InitializeListHead(&Vcb->calcthreads.job_list);
4012     ExInitializeResourceLite(&Vcb->calcthreads.lock);
4013     KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, false);
4014 
4015     RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
4016 
4017     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
4018         NTSTATUS Status;
4019 
4020         Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
4021         KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, false);
4022 
4023         Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, NULL, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
4024         if (!NT_SUCCESS(Status)) {
4025             ULONG j;
4026 
4027             ERR("PsCreateSystemThread returned %08x\n", Status);
4028 
4029             for (j = 0; j < i; j++) {
4030                 Vcb->calcthreads.threads[i].quit = true;
4031             }
4032 
4033             KeSetEvent(&Vcb->calcthreads.event, 0, false);
4034 
4035             return Status;
4036         }
4037     }
4038 
4039     return STATUS_SUCCESS;
4040 }
4041 
4042 static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
4043     NTSTATUS Status;
4044     MOUNTDEV_NAME mdn, *mdn2;
4045     ULONG mdnsize;
4046 
4047     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
4048     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
4049         ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
4050         return false;
4051     }
4052 
4053     mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
4054 
4055     mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
4056     if (!mdn2) {
4057         ERR("out of memory\n");
4058         return false;
4059     }
4060 
4061     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
4062     if (!NT_SUCCESS(Status)) {
4063         ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
4064         ExFreePool(mdn2);
4065         return false;
4066     }
4067 
4068     if (mdn2->NameLength > (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) &&
4069         RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) == sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) {
4070         ExFreePool(mdn2);
4071         return true;
4072     }
4073 
4074     ExFreePool(mdn2);
4075 
4076     return false;
4077 }
4078 
4079 static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _In_ const GUID* guid) {
4080     NTSTATUS Status;
4081     WCHAR *list = NULL, *s;
4082 
4083     Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
4084     if (!NT_SUCCESS(Status)) {
4085         ERR("IoGetDeviceInterfaces returned %08x\n", Status);
4086         return Status;
4087     }
4088 
4089     s = list;
4090     while (s[0] != 0) {
4091         PFILE_OBJECT FileObject;
4092         PDEVICE_OBJECT devobj;
4093         UNICODE_STRING name;
4094 
4095         name.Length = name.MaximumLength = (USHORT)wcslen(s) * sizeof(WCHAR);
4096         name.Buffer = s;
4097 
4098         if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
4099             if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
4100                 ObDereferenceObject(FileObject);
4101 
4102                 pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
4103                 if (!pnp_name->Buffer) {
4104                     ERR("out of memory\n");
4105                     Status = STATUS_INSUFFICIENT_RESOURCES;
4106                     goto end;
4107                 }
4108 
4109                 RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
4110                 pnp_name->Length = pnp_name->MaximumLength = name.Length;
4111 
4112                 Status = STATUS_SUCCESS;
4113                 goto end;
4114             }
4115 
4116             ObDereferenceObject(FileObject);
4117         }
4118 
4119         s = &s[wcslen(s) + 1];
4120     }
4121 
4122     pnp_name->Length = pnp_name->MaximumLength = 0;
4123     pnp_name->Buffer = 0;
4124 
4125     Status = STATUS_NOT_FOUND;
4126 
4127 end:
4128     if (list)
4129         ExFreePool(list);
4130 
4131     return Status;
4132 }
4133 
4134 NTSTATUS get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _Out_ const GUID** guid) {
4135     NTSTATUS Status;
4136 
4137     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
4138     if (NT_SUCCESS(Status)) {
4139         *guid = &GUID_DEVINTERFACE_VOLUME;
4140         return Status;
4141     }
4142 
4143     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
4144     if (NT_SUCCESS(Status)) {
4145         *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
4146         return Status;
4147     }
4148 
4149     Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
4150     if (NT_SUCCESS(Status)) {
4151         *guid = &GUID_DEVINTERFACE_DISK;
4152         return Status;
4153     }
4154 
4155     return STATUS_NOT_FOUND;
4156 }
4157 
4158 _Success_(return>=0)
4159 static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool* pno_pnp) {
4160     NTSTATUS Status;
4161     ULONG to_read;
4162     superblock* sb;
4163     uint32_t crc32;
4164     UNICODE_STRING pnp_name;
4165     const GUID* guid;
4166 
4167     to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), DeviceObject->SectorSize);
4168 
4169     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4170     if (!sb) {
4171         ERR("out of memory\n");
4172         return STATUS_INSUFFICIENT_RESOURCES;
4173     }
4174 
4175     Status = sync_read_phys(DeviceObject, NULL, superblock_addrs[0], to_read, (PUCHAR)sb, true);
4176     if (!NT_SUCCESS(Status)) {
4177         ERR("sync_read_phys returned %08x\n", Status);
4178         goto end;
4179     }
4180 
4181     if (sb->magic != BTRFS_MAGIC) {
4182         Status = STATUS_SUCCESS;
4183         goto end;
4184     }
4185 
4186     crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
4187 
4188     if (crc32 != *((uint32_t*)sb->checksum)) {
4189         WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
4190         Status = STATUS_SUCCESS;
4191         goto end;
4192     }
4193 
4194     DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
4195 
4196     pnp_name.Buffer = NULL;
4197 
4198     Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
4199     if (!NT_SUCCESS(Status)) {
4200         WARN("get_device_pnp_name returned %08x\n", Status);
4201         pnp_name.Length = 0;
4202     }
4203 
4204     if (pnp_name.Length == 0)
4205         *pno_pnp = true;
4206     else {
4207         *pno_pnp = false;
4208         volume_arrival(drvobj, &pnp_name);
4209     }
4210 
4211     if (pnp_name.Buffer)
4212         ExFreePool(pnp_name.Buffer);
4213 
4214     Status = STATUS_SUCCESS;
4215 
4216 end:
4217     ExFreePool(sb);
4218 
4219     return Status;
4220 }
4221 
4222 static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj) {
4223     NTSTATUS Status;
4224     ULONG to_read;
4225     superblock* sb;
4226     PDEVICE_OBJECT device2;
4227 
4228     if (!device)
4229         return false;
4230 
4231     to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
4232 
4233     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4234     if (!sb) {
4235         ERR("out of memory\n");
4236         return false;
4237     }
4238 
4239     Status = sync_read_phys(device, fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
4240     if (!NT_SUCCESS(Status)) {
4241         ERR("Failed to read superblock: %08x\n", Status);
4242         ExFreePool(sb);
4243         return false;
4244     }
4245 
4246     if (sb->magic != BTRFS_MAGIC) {
4247         TRACE("not a BTRFS volume\n");
4248         ExFreePool(sb);
4249         return false;
4250     } else {
4251         uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
4252 
4253         if (crc32 != *((uint32_t*)sb->checksum)) {
4254             WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
4255             ExFreePool(sb);
4256             return false;
4257         }
4258     }
4259 
4260     device2 = device;
4261 
4262     do {
4263         device2->Flags &= ~DO_VERIFY_VOLUME;
4264         device2 = IoGetLowerDeviceObject(device2);
4265     } while (device2);
4266 
4267     ExFreePool(sb);
4268     return true;
4269 }
4270 
4271 static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4272     PIO_STACK_LOCATION IrpSp;
4273     PDEVICE_OBJECT NewDeviceObject = NULL;
4274     PDEVICE_OBJECT DeviceToMount, readobj;
4275     PFILE_OBJECT fileobj;
4276     NTSTATUS Status;
4277     device_extension* Vcb = NULL;
4278     LIST_ENTRY *le, batchlist;
4279     KEY searchkey;
4280     traverse_ptr tp;
4281     fcb* root_fcb = NULL;
4282     ccb* root_ccb = NULL;
4283     bool init_lookaside = false;
4284     device* dev;
4285     volume_device_extension* vde = NULL;
4286     pdo_device_extension* pdode = NULL;
4287     volume_child* vc;
4288     uint64_t readobjsize;
4289 
4290     TRACE("(%p, %p)\n", DeviceObject, Irp);
4291 
4292     if (DeviceObject != master_devobj) {
4293         Status = STATUS_INVALID_DEVICE_REQUEST;
4294         goto exit;
4295     }
4296 
4297     IrpSp = IoGetCurrentIrpStackLocation(Irp);
4298     DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
4299 
4300     if (!is_btrfs_volume(DeviceToMount)) {
4301         bool not_pnp = false;
4302 
4303         Status = check_mount_device(DeviceToMount, &not_pnp);
4304         if (!NT_SUCCESS(Status))
4305             WARN("check_mount_device returned %08x\n", Status);
4306 
4307         if (!not_pnp) {
4308             Status = STATUS_UNRECOGNIZED_VOLUME;
4309             goto exit2;
4310         }
4311     } else {
4312         PDEVICE_OBJECT pdo;
4313 
4314         pdo = DeviceToMount;
4315 
4316         while (IoGetLowerDeviceObject(pdo)) {
4317             pdo = IoGetLowerDeviceObject(pdo);
4318         }
4319 
4320         ExAcquireResourceSharedLite(&pdo_list_lock, true);
4321 
4322         le = pdo_list.Flink;
4323         while (le != &pdo_list) {
4324             pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
4325 
4326             if (pdode2->pdo == pdo) {
4327                 vde = pdode2->vde;
4328                 break;
4329             }
4330 
4331             le = le->Flink;
4332         }
4333 
4334         ExReleaseResourceLite(&pdo_list_lock);
4335 
4336         if (!vde || vde->type != VCB_TYPE_VOLUME) {
4337             vde = NULL;
4338             Status = STATUS_UNRECOGNIZED_VOLUME;
4339             goto exit2;
4340         }
4341     }
4342 
4343     if (vde) {
4344         pdode = vde->pdode;
4345 
4346         ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
4347 
4348         le = pdode->children.Flink;
4349         while (le != &pdode->children) {
4350             LIST_ENTRY* le2 = le->Flink;
4351 
4352             vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4353 
4354             if (!still_has_superblock(vc->devobj, vc->fileobj)) {
4355                 remove_volume_child(vde, vc, false);
4356 
4357                 if (pdode->num_children == 0) {
4358                     ERR("error - number of devices is zero\n");
4359                     Status = STATUS_INTERNAL_ERROR;
4360                     goto exit2;
4361                 }
4362 
4363                 Status = STATUS_DEVICE_NOT_READY;
4364                 goto exit2;
4365             }
4366 
4367             le = le2;
4368         }
4369 
4370         if (pdode->num_children == 0 || pdode->children_loaded == 0) {
4371             ERR("error - number of devices is zero\n");
4372             Status = STATUS_INTERNAL_ERROR;
4373             goto exit;
4374         }
4375 
4376         ExConvertExclusiveToSharedLite(&pdode->child_lock);
4377 
4378         vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4379 
4380         readobj = vc->devobj;
4381         fileobj = vc->fileobj;
4382         readobjsize = vc->size;
4383 
4384         vde->device->Characteristics &= ~FILE_DEVICE_SECURE_OPEN;
4385     } else {
4386         GET_LENGTH_INFORMATION gli;
4387 
4388         vc = NULL;
4389         readobj = DeviceToMount;
4390         fileobj = NULL;
4391 
4392         Status = dev_ioctl(readobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
4393                            &gli, sizeof(gli), true, NULL);
4394 
4395         if (!NT_SUCCESS(Status)) {
4396             ERR("error reading length information: %08x\n", Status);
4397             goto exit;
4398         }
4399 
4400         readobjsize = gli.Length.QuadPart;
4401     }
4402 
4403     Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, false, &NewDeviceObject);
4404     if (!NT_SUCCESS(Status)) {
4405         ERR("IoCreateDevice returned %08x\n", Status);
4406         Status = STATUS_UNRECOGNIZED_VOLUME;
4407         goto exit;
4408     }
4409 
4410     NewDeviceObject->Flags |= DO_DIRECT_IO;
4411 
4412     // Some programs seem to expect that the sector size will be 512, for
4413     // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4414     NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
4415 
4416     Vcb = (PVOID)NewDeviceObject->DeviceExtension;
4417     RtlZeroMemory(Vcb, sizeof(device_extension));
4418     Vcb->type = VCB_TYPE_FS;
4419     Vcb->vde = vde;
4420 
4421     ExInitializeResourceLite(&Vcb->tree_lock);
4422     Vcb->need_write = false;
4423 
4424     ExInitializeResourceLite(&Vcb->fcb_lock);
4425     ExInitializeResourceLite(&Vcb->fileref_lock);
4426     ExInitializeResourceLite(&Vcb->chunk_lock);
4427     ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
4428     ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
4429     ExInitializeResourceLite(&Vcb->dirty_subvols_lock);
4430     ExInitializeResourceLite(&Vcb->scrub.stats_lock);
4431 
4432     ExInitializeResourceLite(&Vcb->load_lock);
4433     ExAcquireResourceExclusiveLite(&Vcb->load_lock, true);
4434 
4435     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
4436 
4437     DeviceToMount->Flags |= DO_DIRECT_IO;
4438 
4439     Status = read_superblock(Vcb, readobj, fileobj, readobjsize);
4440     if (!NT_SUCCESS(Status)) {
4441         if (!IoIsErrorUserInduced(Status))
4442             Status = STATUS_UNRECOGNIZED_VOLUME;
4443         else if (Irp->Tail.Overlay.Thread)
4444             IoSetHardErrorOrVerifyDevice(Irp, readobj);
4445 
4446         goto exit;
4447     }
4448 
4449     if (!vde && Vcb->superblock.num_devices > 1) {
4450         ERR("cannot mount multi-device FS with non-PNP device\n");
4451         Status = STATUS_UNRECOGNIZED_VOLUME;
4452         goto exit;
4453     }
4454 
4455     Status = registry_load_volume_options(Vcb);
4456     if (!NT_SUCCESS(Status)) {
4457         ERR("registry_load_volume_options returned %08x\n", Status);
4458         goto exit;
4459     }
4460 
4461     if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
4462         ERR("could not mount as %u device(s) missing\n", pdode->num_children - pdode->children_loaded);
4463         Status = STATUS_DEVICE_NOT_READY;
4464         goto exit;
4465     }
4466 
4467     if (Vcb->options.ignore) {
4468         TRACE("ignoring volume\n");
4469         Status = STATUS_UNRECOGNIZED_VOLUME;
4470         goto exit;
4471     }
4472 
4473     if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
4474         WARN("cannot mount because of unsupported incompat flags (%I64x)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
4475         Status = STATUS_UNRECOGNIZED_VOLUME;
4476         goto exit;
4477     }
4478 
4479     Vcb->readonly = false;
4480     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
4481         WARN("mounting read-only because of unsupported flags (%I64x)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
4482         Vcb->readonly = true;
4483     }
4484 
4485     if (Vcb->options.readonly)
4486         Vcb->readonly = true;
4487 
4488     Vcb->superblock.generation++;
4489     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
4490 
4491     InitializeListHead(&Vcb->devices);
4492     dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
4493     if (!dev) {
4494         ERR("out of memory\n");
4495         Status = STATUS_INSUFFICIENT_RESOURCES;
4496         goto exit;
4497     }
4498 
4499     dev->devobj = readobj;
4500     dev->fileobj = fileobj;
4501     RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
4502 
4503     if (dev->devitem.num_bytes > readobjsize) {
4504         WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", dev->devitem.dev_id,
4505                 dev->devitem.num_bytes, readobjsize);
4506 
4507         dev->devitem.num_bytes = readobjsize;
4508     }
4509 
4510     dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false;
4511 
4512     init_device(Vcb, dev, true);
4513 
4514     InsertTailList(&Vcb->devices, &dev->list_entry);
4515     Vcb->devices_loaded = 1;
4516 
4517     if (DeviceToMount->Flags & DO_SYSTEM_BOOT_PARTITION)
4518         Vcb->disallow_dismount = true;
4519 
4520     TRACE("DeviceToMount = %p\n", DeviceToMount);
4521     TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp->Parameters.MountVolume.Vpb);
4522 
4523     NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
4524     NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
4525 
4526     InitializeListHead(&Vcb->roots);
4527     InitializeListHead(&Vcb->drop_roots);
4528 
4529     Vcb->log_to_phys_loaded = false;
4530 
4531     add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
4532 
4533     if (!Vcb->chunk_root) {
4534         ERR("Could not load chunk root.\n");
4535         Status = STATUS_INTERNAL_ERROR;
4536         goto exit;
4537     }
4538 
4539     InitializeListHead(&Vcb->sys_chunks);
4540     Status = load_sys_chunks(Vcb);
4541     if (!NT_SUCCESS(Status)) {
4542         ERR("load_sys_chunks returned %08x\n", Status);
4543         goto exit;
4544     }
4545 
4546     InitializeListHead(&Vcb->chunks);
4547     InitializeListHead(&Vcb->trees);
4548     InitializeListHead(&Vcb->trees_hash);
4549     InitializeListHead(&Vcb->all_fcbs);
4550     InitializeListHead(&Vcb->dirty_fcbs);
4551     InitializeListHead(&Vcb->dirty_filerefs);
4552     InitializeListHead(&Vcb->dirty_subvols);
4553     InitializeListHead(&Vcb->send_ops);
4554 
4555     ExInitializeFastMutex(&Vcb->trees_list_mutex);
4556 
4557     InitializeListHead(&Vcb->DirNotifyList);
4558     InitializeListHead(&Vcb->scrub.errors);
4559 
4560     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
4561 
4562     ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
4563     ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
4564     ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
4565     ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
4566     ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
4567     ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
4568     ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
4569     ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
4570     ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
4571     init_lookaside = true;
4572 
4573     Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4574 
4575     Status = load_chunk_root(Vcb, Irp);
4576     if (!NT_SUCCESS(Status)) {
4577         ERR("load_chunk_root returned %08x\n", Status);
4578         goto exit;
4579     }
4580 
4581     if (Vcb->superblock.num_devices > 1) {
4582         if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
4583             ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
4584 
4585             IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
4586 
4587             Status = STATUS_INTERNAL_ERROR;
4588             goto exit;
4589         }
4590 
4591         if (dev->readonly && !Vcb->readonly) {
4592             Vcb->readonly = true;
4593 
4594             le = Vcb->devices.Flink;
4595             while (le != &Vcb->devices) {
4596                 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4597 
4598                 if (dev2->readonly && !dev2->seeding)
4599                     break;
4600 
4601                 if (!dev2->readonly) {
4602                     Vcb->readonly = false;
4603                     break;
4604                 }
4605 
4606                 le = le->Flink;
4607             }
4608 
4609             if (Vcb->readonly)
4610                 WARN("setting volume to readonly\n");
4611         }
4612     } else {
4613         if (dev->readonly) {
4614             WARN("setting volume to readonly as device is readonly\n");
4615             Vcb->readonly = true;
4616         }
4617     }
4618 
4619     add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
4620 
4621     if (!Vcb->root_root) {
4622         ERR("Could not load root of roots.\n");
4623         Status = STATUS_INTERNAL_ERROR;
4624         goto exit;
4625     }
4626 
4627     Status = look_for_roots(Vcb, Irp);
4628     if (!NT_SUCCESS(Status)) {
4629         ERR("look_for_roots returned %08x\n", Status);
4630         goto exit;
4631     }
4632 
4633     if (!Vcb->readonly) {
4634         Status = find_chunk_usage(Vcb, Irp);
4635         if (!NT_SUCCESS(Status)) {
4636             ERR("find_chunk_usage returned %08x\n", Status);
4637             goto exit;
4638         }
4639     }
4640 
4641     InitializeListHead(&batchlist);
4642 
4643     // We've already increased the generation by one
4644     if (!Vcb->readonly && (
4645         Vcb->options.clear_cache ||
4646         (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE) && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) ||
4647         (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)))) {
4648         if (Vcb->options.clear_cache)
4649             WARN("ClearCache option was set, clearing cache...\n");
4650         else if (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID))
4651             WARN("clearing free-space tree created by buggy Linux driver\n");
4652         else
4653             WARN("generation was %I64x, free-space cache generation was %I64x; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
4654 
4655         Status = clear_free_space_cache(Vcb, &batchlist, Irp);
4656         if (!NT_SUCCESS(Status)) {
4657             ERR("clear_free_space_cache returned %08x\n", Status);
4658             clear_batch_list(Vcb, &batchlist);
4659             goto exit;
4660         }
4661     }
4662 
4663     Status = commit_batch_list(Vcb, &batchlist, Irp);
4664     if (!NT_SUCCESS(Status)) {
4665         ERR("commit_batch_list returned %08x\n", Status);
4666         goto exit;
4667     }
4668 
4669     Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
4670     if (!Vcb->volume_fcb) {
4671         ERR("out of memory\n");
4672         Status = STATUS_INSUFFICIENT_RESOURCES;
4673         goto exit;
4674     }
4675 
4676     Vcb->volume_fcb->Vcb = Vcb;
4677     Vcb->volume_fcb->sd = NULL;
4678 
4679     Vcb->dummy_fcb = create_fcb(Vcb, NonPagedPool);
4680     if (!Vcb->dummy_fcb) {
4681         ERR("out of memory\n");
4682         Status = STATUS_INSUFFICIENT_RESOURCES;
4683         goto exit;
4684     }
4685 
4686     Vcb->dummy_fcb->Vcb = Vcb;
4687     Vcb->dummy_fcb->type = BTRFS_TYPE_DIRECTORY;
4688     Vcb->dummy_fcb->inode = 2;
4689     Vcb->dummy_fcb->subvol = Vcb->root_root;
4690     Vcb->dummy_fcb->atts = FILE_ATTRIBUTE_DIRECTORY;
4691     Vcb->dummy_fcb->inode_item.st_nlink = 1;
4692     Vcb->dummy_fcb->inode_item.st_mode = __S_IFDIR;
4693 
4694     Vcb->dummy_fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4695     if (!Vcb->dummy_fcb->hash_ptrs) {
4696         ERR("out of memory\n");
4697         Status = STATUS_INSUFFICIENT_RESOURCES;
4698         goto exit;
4699     }
4700 
4701     RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
4702 
4703     Vcb->dummy_fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4704     if (!Vcb->dummy_fcb->hash_ptrs_uc) {
4705         ERR("out of memory\n");
4706         Status = STATUS_INSUFFICIENT_RESOURCES;
4707         goto exit;
4708     }
4709 
4710     RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
4711 
4712     root_fcb = create_fcb(Vcb, NonPagedPool);
4713     if (!root_fcb) {
4714         ERR("out of memory\n");
4715         Status = STATUS_INSUFFICIENT_RESOURCES;
4716         goto exit;
4717     }
4718 
4719     root_fcb->Vcb = Vcb;
4720     root_fcb->inode = SUBVOL_ROOT_INODE;
4721     root_fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&root_fcb->inode, sizeof(uint64_t));
4722     root_fcb->type = BTRFS_TYPE_DIRECTORY;
4723 
4724 #ifdef DEBUG_FCB_REFCOUNTS
4725     WARN("volume FCB = %p\n", Vcb->volume_fcb);
4726     WARN("root FCB = %p\n", root_fcb);
4727 #endif
4728 
4729     root_fcb->subvol = find_default_subvol(Vcb, Irp);
4730 
4731     if (!root_fcb->subvol) {
4732         ERR("could not find top subvol\n");
4733         Status = STATUS_INTERNAL_ERROR;
4734         goto exit;
4735     }
4736 
4737     Status = load_dir_children(Vcb, root_fcb, true, Irp);
4738     if (!NT_SUCCESS(Status)) {
4739         ERR("load_dir_children returned %08x\n", Status);
4740         goto exit;
4741     }
4742 
4743     searchkey.obj_id = root_fcb->inode;
4744     searchkey.obj_type = TYPE_INODE_ITEM;
4745     searchkey.offset = 0xffffffffffffffff;
4746 
4747     Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, false, Irp);
4748     if (!NT_SUCCESS(Status)) {
4749         ERR("error - find_item returned %08x\n", Status);
4750         goto exit;
4751     }
4752 
4753     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
4754         ERR("couldn't find INODE_ITEM for root directory\n");
4755         Status = STATUS_INTERNAL_ERROR;
4756         goto exit;
4757     }
4758 
4759     if (tp.item->size > 0)
4760         RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
4761 
4762     fcb_get_sd(root_fcb, NULL, true, Irp);
4763 
4764     root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, false, false, Irp);
4765 
4766     Vcb->root_fileref = create_fileref(Vcb);
4767     if (!Vcb->root_fileref) {
4768         ERR("out of memory\n");
4769         Status = STATUS_INSUFFICIENT_RESOURCES;
4770         goto exit;
4771     }
4772 
4773     Vcb->root_fileref->fcb = root_fcb;
4774     InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
4775     InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
4776 
4777     root_fcb->subvol->fcbs_ptrs[root_fcb->hash >> 24] = &root_fcb->list_entry;
4778 
4779     root_fcb->fileref = Vcb->root_fileref;
4780 
4781     root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
4782     if (!root_ccb) {
4783         ERR("out of memory\n");
4784         Status = STATUS_INSUFFICIENT_RESOURCES;
4785         goto exit;
4786     }
4787 
4788     Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
4789     Vcb->root_file->FsContext = root_fcb;
4790     Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
4791     Vcb->root_file->Vpb = DeviceObject->Vpb;
4792 
4793     RtlZeroMemory(root_ccb, sizeof(ccb));
4794     root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
4795     root_ccb->NodeSize = sizeof(ccb);
4796 
4797     Vcb->root_file->FsContext2 = root_ccb;
4798 
4799     _SEH2_TRY {
4800         CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), false, cache_callbacks, Vcb->root_file);
4801     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4802         Status = _SEH2_GetExceptionCode();
4803         goto exit;
4804     } _SEH2_END;
4805 
4806     le = Vcb->devices.Flink;
4807     while (le != &Vcb->devices) {
4808         device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4809 
4810         Status = find_disk_holes(Vcb, dev2, Irp);
4811         if (!NT_SUCCESS(Status)) {
4812             ERR("find_disk_holes returned %08x\n", Status);
4813             goto exit;
4814         }
4815 
4816         le = le->Flink;
4817     }
4818 
4819     NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4820     IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
4821     IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
4822     NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
4823     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
4824     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
4825     NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
4826 
4827     KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, false);
4828 
4829     Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
4830     if (!NT_SUCCESS(Status)) {
4831         ERR("PsCreateSystemThread returned %08x\n", Status);
4832         goto exit;
4833     }
4834 
4835     Status = create_calc_threads(NewDeviceObject);
4836     if (!NT_SUCCESS(Status)) {
4837         ERR("create_calc_threads returned %08x\n", Status);
4838         goto exit;
4839     }
4840 
4841     Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
4842     if (!NT_SUCCESS(Status))
4843         WARN("registry_mark_volume_mounted returned %08x\n", Status);
4844 
4845     Status = look_for_balance_item(Vcb);
4846     if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
4847         WARN("look_for_balance_item returned %08x\n", Status);
4848 
4849     Status = STATUS_SUCCESS;
4850 
4851     if (vde)
4852         vde->mounted_device = NewDeviceObject;
4853 
4854     ExInitializeResourceLite(&Vcb->send_load_lock);
4855 
4856 exit:
4857     if (pdode)
4858         ExReleaseResourceLite(&pdode->child_lock);
4859 
4860 exit2:
4861     if (Vcb) {
4862         ExReleaseResourceLite(&Vcb->tree_lock);
4863         ExReleaseResourceLite(&Vcb->load_lock);
4864     }
4865 
4866     if (!NT_SUCCESS(Status)) {
4867         if (Vcb) {
4868             if (init_lookaside) {
4869                 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
4870                 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
4871                 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
4872                 ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
4873                 ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
4874                 ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
4875                 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
4876                 ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
4877                 ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
4878             }
4879 
4880             if (Vcb->root_file)
4881                 ObDereferenceObject(Vcb->root_file);
4882             else if (Vcb->root_fileref)
4883                 free_fileref(Vcb->root_fileref);
4884             else if (root_fcb)
4885                 free_fcb(root_fcb);
4886 
4887             if (root_fcb && root_fcb->refcount == 0)
4888                 reap_fcb(root_fcb);
4889 
4890             if (Vcb->volume_fcb)
4891                 reap_fcb(Vcb->volume_fcb);
4892 
4893             ExDeleteResourceLite(&Vcb->tree_lock);
4894             ExDeleteResourceLite(&Vcb->load_lock);
4895             ExDeleteResourceLite(&Vcb->fcb_lock);
4896             ExDeleteResourceLite(&Vcb->fileref_lock);
4897             ExDeleteResourceLite(&Vcb->chunk_lock);
4898             ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
4899             ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
4900             ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
4901             ExDeleteResourceLite(&Vcb->scrub.stats_lock);
4902 
4903             if (Vcb->devices.Flink) {
4904                 while (!IsListEmpty(&Vcb->devices)) {
4905                     device* dev2 = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
4906 
4907                     ExFreePool(dev2);
4908                 }
4909             }
4910         }
4911 
4912         if (NewDeviceObject)
4913             IoDeleteDevice(NewDeviceObject);
4914     } else {
4915         ExAcquireResourceExclusiveLite(&global_loading_lock, true);
4916         InsertTailList(&VcbList, &Vcb->list_entry);
4917         ExReleaseResourceLite(&global_loading_lock);
4918 
4919         FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
4920     }
4921 
4922     TRACE("mount_vol done (status: %lx)\n", Status);
4923 
4924     return Status;
4925 }
4926 
4927 static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
4928     NTSTATUS Status;
4929     superblock* sb;
4930     uint32_t crc32;
4931     ULONG to_read, cc;
4932 
4933     if (!dev->devobj)
4934         return STATUS_WRONG_VOLUME;
4935 
4936     if (dev->removable) {
4937         IO_STATUS_BLOCK iosb;
4938 
4939         Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
4940 
4941         if (IoIsErrorUserInduced(Status)) {
4942             ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
4943 
4944             if (Vcb->vde) {
4945                 pdo_device_extension* pdode = Vcb->vde->pdode;
4946                 LIST_ENTRY* le2;
4947                 bool changed = false;
4948 
4949                 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
4950 
4951                 le2 = pdode->children.Flink;
4952                 while (le2 != &pdode->children) {
4953                     volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
4954 
4955                     if (vc->devobj == dev->devobj) {
4956                         TRACE("removing device\n");
4957 
4958                         remove_volume_child(Vcb->vde, vc, true);
4959                         changed = true;
4960 
4961                         break;
4962                     }
4963 
4964                     le2 = le2->Flink;
4965                 }
4966 
4967                 if (!changed)
4968                     ExReleaseResourceLite(&pdode->child_lock);
4969             }
4970         } else if (!NT_SUCCESS(Status)) {
4971             ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
4972             return Status;
4973         } else if (iosb.Information < sizeof(ULONG)) {
4974             ERR("iosb.Information was too short\n");
4975             return STATUS_INTERNAL_ERROR;
4976         }
4977 
4978         dev->change_count = cc;
4979     }
4980 
4981     to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), dev->devobj->SectorSize);
4982 
4983     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4984     if (!sb) {
4985         ERR("out of memory\n");
4986         return STATUS_INSUFFICIENT_RESOURCES;
4987     }
4988 
4989     Status = sync_read_phys(dev->devobj, dev->fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
4990     if (!NT_SUCCESS(Status)) {
4991         ERR("Failed to read superblock: %08x\n", Status);
4992         ExFreePool(sb);
4993         return Status;
4994     }
4995 
4996     if (sb->magic != BTRFS_MAGIC) {
4997         ERR("not a BTRFS volume\n");
4998         ExFreePool(sb);
4999         return STATUS_WRONG_VOLUME;
5000     }
5001 
5002     crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
5003     TRACE("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
5004 
5005     if (crc32 != *((uint32_t*)sb->checksum)) {
5006         ERR("checksum error\n");
5007         ExFreePool(sb);
5008         return STATUS_WRONG_VOLUME;
5009     }
5010 
5011     if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
5012         ERR("different UUIDs\n");
5013         ExFreePool(sb);
5014         return STATUS_WRONG_VOLUME;
5015     }
5016 
5017     ExFreePool(sb);
5018 
5019     dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
5020 
5021     return STATUS_SUCCESS;
5022 }
5023 
5024 static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
5025     device_extension* Vcb = devobj->DeviceExtension;
5026     NTSTATUS Status;
5027     LIST_ENTRY* le;
5028     uint64_t failed_devices = 0;
5029     bool locked = false, remove = false;
5030 
5031     if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
5032         return STATUS_WRONG_VOLUME;
5033 
5034     if (!ExIsResourceAcquiredExclusive(&Vcb->tree_lock)) {
5035         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5036         locked = true;
5037     }
5038 
5039     if (Vcb->removing) {
5040         if (locked) ExReleaseResourceLite(&Vcb->tree_lock);
5041         return STATUS_WRONG_VOLUME;
5042     }
5043 
5044     InterlockedIncrement(&Vcb->open_files); // so pnp_surprise_removal doesn't uninit the device while we're still using it
5045 
5046     le = Vcb->devices.Flink;
5047     while (le != &Vcb->devices) {
5048         device* dev = CONTAINING_RECORD(le, device, list_entry);
5049 
5050         Status = verify_device(Vcb, dev);
5051         if (!NT_SUCCESS(Status)) {
5052             failed_devices++;
5053 
5054             if (dev->devobj && Vcb->options.allow_degraded)
5055                 dev->devobj = NULL;
5056         }
5057 
5058         le = le->Flink;
5059     }
5060 
5061     InterlockedDecrement(&Vcb->open_files);
5062 
5063     if (Vcb->removing && Vcb->open_files == 0)
5064         remove = true;
5065 
5066     if (locked)
5067         ExReleaseResourceLite(&Vcb->tree_lock);
5068 
5069     if (remove) {
5070         uninit(Vcb);
5071         return Status;
5072     }
5073 
5074     if (failed_devices == 0 || (Vcb->options.allow_degraded && failed_devices < Vcb->superblock.num_devices)) {
5075         Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
5076 
5077         return STATUS_SUCCESS;
5078     }
5079 
5080     return Status;
5081 }
5082 
5083 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL)
5084 _Function_class_(DRIVER_DISPATCH)
5085 static NTSTATUS __stdcall drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5086     PIO_STACK_LOCATION IrpSp;
5087     NTSTATUS Status;
5088     device_extension* Vcb = DeviceObject->DeviceExtension;
5089     bool top_level;
5090 
5091     FsRtlEnterFileSystem();
5092 
5093     TRACE("file system control\n");
5094 
5095     top_level = is_top_level(Irp);
5096 
5097     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5098         Status = vol_file_system_control(DeviceObject, Irp);
5099         goto end;
5100     } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
5101         Status = STATUS_INVALID_PARAMETER;
5102         goto end;
5103     }
5104 
5105     Status = STATUS_NOT_IMPLEMENTED;
5106 
5107     IrpSp = IoGetCurrentIrpStackLocation( Irp );
5108 
5109     Irp->IoStatus.Information = 0;
5110 
5111     switch (IrpSp->MinorFunction) {
5112         case IRP_MN_MOUNT_VOLUME:
5113             TRACE("IRP_MN_MOUNT_VOLUME\n");
5114 
5115             Status = mount_vol(DeviceObject, Irp);
5116             break;
5117 
5118         case IRP_MN_KERNEL_CALL:
5119             TRACE("IRP_MN_KERNEL_CALL\n");
5120 
5121             Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
5122             break;
5123 
5124         case IRP_MN_USER_FS_REQUEST:
5125             TRACE("IRP_MN_USER_FS_REQUEST\n");
5126 
5127             Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
5128             break;
5129 
5130         case IRP_MN_VERIFY_VOLUME:
5131             TRACE("IRP_MN_VERIFY_VOLUME\n");
5132 
5133             Status = verify_volume(DeviceObject);
5134 
5135             if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
5136                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5137                 Vcb->removing = true;
5138                 ExReleaseResourceLite(&Vcb->tree_lock);
5139             }
5140 
5141             break;
5142 
5143         default:
5144             break;
5145     }
5146 
5147 end:
5148     TRACE("returning %08x\n", Status);
5149 
5150     if (Irp) {
5151         Irp->IoStatus.Status = Status;
5152 
5153         IoCompleteRequest(Irp, IO_NO_INCREMENT);
5154     }
5155 
5156     if (top_level)
5157         IoSetTopLevelIrp(NULL);
5158 
5159     FsRtlExitFileSystem();
5160 
5161     return Status;
5162 }
5163 
5164 _Dispatch_type_(IRP_MJ_LOCK_CONTROL)
5165 _Function_class_(DRIVER_DISPATCH)
5166 static NTSTATUS __stdcall drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5167     NTSTATUS Status;
5168     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5169     fcb* fcb = IrpSp->FileObject->FsContext;
5170     device_extension* Vcb = DeviceObject->DeviceExtension;
5171     bool top_level;
5172 
5173     FsRtlEnterFileSystem();
5174 
5175     top_level = is_top_level(Irp);
5176 
5177     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5178         Status = vol_lock_control(DeviceObject, Irp);
5179 
5180         Irp->IoStatus.Status = Status;
5181         IoCompleteRequest(Irp, IO_NO_INCREMENT);
5182 
5183         goto exit;
5184     }
5185 
5186     TRACE("lock control\n");
5187 
5188     Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
5189 
5190     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
5191 
5192 exit:
5193     TRACE("returning %08x\n", Status);
5194 
5195     if (top_level)
5196         IoSetTopLevelIrp(NULL);
5197 
5198     FsRtlExitFileSystem();
5199 
5200     return Status;
5201 }
5202 
5203 _Dispatch_type_(IRP_MJ_SHUTDOWN)
5204 _Function_class_(DRIVER_DISPATCH)
5205 static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5206     NTSTATUS Status;
5207     bool top_level;
5208     device_extension* Vcb = DeviceObject->DeviceExtension;
5209     LIST_ENTRY* le;
5210 
5211     FsRtlEnterFileSystem();
5212 
5213     TRACE("shutdown\n");
5214 
5215     top_level = is_top_level(Irp);
5216 
5217     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5218         Status = vol_shutdown(DeviceObject, Irp);
5219         goto end;
5220     }
5221 
5222     Status = STATUS_SUCCESS;
5223 
5224     shutting_down = true;
5225     KeSetEvent(&mountmgr_thread_event, 0, false);
5226 
5227     le = VcbList.Flink;
5228     while (le != &VcbList) {
5229         bool open_files;
5230         LIST_ENTRY* le2 = le->Flink;
5231 
5232         Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
5233 
5234         TRACE("shutting down Vcb %p\n", Vcb);
5235 
5236         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5237         Vcb->removing = true;
5238         open_files = Vcb->open_files > 0;
5239 
5240         if (Vcb->need_write && !Vcb->readonly) {
5241             Status = do_write(Vcb, Irp);
5242             if (!NT_SUCCESS(Status))
5243                 ERR("do_write returned %08x\n", Status);
5244         }
5245 
5246         free_trees(Vcb);
5247 
5248         ExReleaseResourceLite(&Vcb->tree_lock);
5249 
5250         if (!open_files)
5251             uninit(Vcb);
5252 
5253         le = le2;
5254     }
5255 
5256 #ifdef _DEBUG
5257     if (comfo) {
5258         ObDereferenceObject(comfo);
5259         comdo = NULL;
5260         comfo = NULL;
5261     }
5262 #endif
5263 
5264 end:
5265     Irp->IoStatus.Status = Status;
5266     Irp->IoStatus.Information = 0;
5267 
5268     IoCompleteRequest( Irp, IO_NO_INCREMENT );
5269 
5270     if (top_level)
5271         IoSetTopLevelIrp(NULL);
5272 
5273     FsRtlExitFileSystem();
5274 
5275     return Status;
5276 }
5277 
5278 _Dispatch_type_(IRP_MJ_POWER)
5279 _Function_class_(DRIVER_DISPATCH)
5280 static NTSTATUS __stdcall drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5281     NTSTATUS Status;
5282     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5283     device_extension* Vcb = DeviceObject->DeviceExtension;
5284     bool top_level;
5285 
5286     FsRtlEnterFileSystem();
5287 
5288     top_level = is_top_level(Irp);
5289 
5290     Irp->IoStatus.Information = 0;
5291 
5292     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5293         volume_device_extension* vde = DeviceObject->DeviceExtension;
5294 
5295         PoStartNextPowerIrp(Irp);
5296         IoSkipCurrentIrpStackLocation(Irp);
5297         Status = PoCallDriver(vde->attached_device, Irp);
5298 
5299         goto exit;
5300     } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
5301         IoSkipCurrentIrpStackLocation(Irp);
5302 
5303         Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
5304 
5305         goto exit;
5306     } else if (Vcb && Vcb->type == VCB_TYPE_BUS) {
5307         bus_device_extension* bde = DeviceObject->DeviceExtension;
5308 
5309         PoStartNextPowerIrp(Irp);
5310         IoSkipCurrentIrpStackLocation(Irp);
5311         Status = PoCallDriver(bde->attached_device, Irp);
5312 
5313         goto exit;
5314     }
5315 
5316     if (IrpSp->MinorFunction == IRP_MN_SET_POWER || IrpSp->MinorFunction == IRP_MN_QUERY_POWER)
5317         Irp->IoStatus.Status = STATUS_SUCCESS;
5318 
5319     Status = Irp->IoStatus.Status;
5320 
5321     PoStartNextPowerIrp(Irp);
5322 
5323     IoCompleteRequest(Irp, IO_NO_INCREMENT);
5324 
5325 exit:
5326     if (top_level)
5327         IoSetTopLevelIrp(NULL);
5328 
5329     FsRtlExitFileSystem();
5330 
5331     return Status;
5332 }
5333 
5334 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
5335 _Function_class_(DRIVER_DISPATCH)
5336 static NTSTATUS __stdcall drv_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5337     NTSTATUS Status;
5338     device_extension* Vcb = DeviceObject->DeviceExtension;
5339     bool top_level;
5340 
5341     FsRtlEnterFileSystem();
5342 
5343     top_level = is_top_level(Irp);
5344 
5345     Irp->IoStatus.Information = 0;
5346 
5347     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5348         volume_device_extension* vde = DeviceObject->DeviceExtension;
5349 
5350         IoSkipCurrentIrpStackLocation(Irp);
5351 
5352         Status = IoCallDriver(vde->attached_device, Irp);
5353 
5354         goto exit;
5355     } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
5356         IoSkipCurrentIrpStackLocation(Irp);
5357 
5358         Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
5359 
5360         goto exit;
5361     } else if (Vcb && Vcb->type == VCB_TYPE_BUS) {
5362         bus_device_extension* bde = DeviceObject->DeviceExtension;
5363 
5364         IoSkipCurrentIrpStackLocation(Irp);
5365 
5366         Status = IoCallDriver(bde->attached_device, Irp);
5367 
5368         goto exit;
5369     }
5370 
5371     Status = Irp->IoStatus.Status;
5372     IoCompleteRequest(Irp, IO_NO_INCREMENT);
5373 
5374 exit:
5375     if (top_level)
5376         IoSetTopLevelIrp(NULL);
5377 
5378     FsRtlExitFileSystem();
5379 
5380     return Status;
5381 }
5382 
5383 bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix) {
5384     ULONG i;
5385 
5386     if (us->Length < sizeof(WCHAR))
5387         return false;
5388 
5389     if (us->Length > 255 * sizeof(WCHAR))
5390         return false;
5391 
5392     for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
5393         if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
5394             (!posix && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
5395             us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
5396             return false;
5397     }
5398 
5399     if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
5400         return false;
5401 
5402     return true;
5403 }
5404 
5405 void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ uint64_t start, _In_ uint64_t length) {
5406     LIST_ENTRY* le;
5407     bool locked;
5408     range_lock* rl;
5409 
5410     rl = ExAllocateFromNPagedLookasideList(&Vcb->range_lock_lookaside);
5411     if (!rl) {
5412         ERR("out of memory\n");
5413         return;
5414     }
5415 
5416     rl->start = start;
5417     rl->length = length;
5418     rl->thread = PsGetCurrentThread();
5419 
5420     while (true) {
5421         locked = false;
5422 
5423         ExAcquireResourceExclusiveLite(&c->range_locks_lock, true);
5424 
5425         le = c->range_locks.Flink;
5426         while (le != &c->range_locks) {
5427             range_lock* rl2 = CONTAINING_RECORD(le, range_lock, list_entry);
5428 
5429             if (rl2->start < start + length && rl2->start + rl2->length > start && rl2->thread != PsGetCurrentThread()) {
5430                 locked = true;
5431                 break;
5432             }
5433 
5434             le = le->Flink;
5435         }
5436 
5437         if (!locked) {
5438             InsertTailList(&c->range_locks, &rl->list_entry);
5439 
5440             ExReleaseResourceLite(&c->range_locks_lock);
5441             return;
5442         }
5443 
5444         KeClearEvent(&c->range_locks_event);
5445 
5446         ExReleaseResourceLite(&c->range_locks_lock);
5447 
5448         KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, false, NULL);
5449     }
5450 }
5451 
5452 void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ uint64_t start, _In_ uint64_t length) {
5453     LIST_ENTRY* le;
5454 
5455     ExAcquireResourceExclusiveLite(&c->range_locks_lock, true);
5456 
5457     le = c->range_locks.Flink;
5458     while (le != &c->range_locks) {
5459         range_lock* rl = CONTAINING_RECORD(le, range_lock, list_entry);
5460 
5461         if (rl->start == start && rl->length == length) {
5462             RemoveEntryList(&rl->list_entry);
5463             ExFreeToNPagedLookasideList(&Vcb->range_lock_lookaside, rl);
5464             break;
5465         }
5466 
5467         le = le->Flink;
5468     }
5469 
5470     KeSetEvent(&c->range_locks_event, 0, false);
5471 
5472     ExReleaseResourceLite(&c->range_locks_lock);
5473 }
5474 
5475 void log_device_error(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ int error) {
5476     dev->stats[error]++;
5477     dev->stats_changed = true;
5478     Vcb->stats_changed = true;
5479 }
5480 
5481 #ifdef _DEBUG
5482 _Function_class_(KSTART_ROUTINE)
5483 static void __stdcall serial_thread(void* context) {
5484     LARGE_INTEGER due_time;
5485     KTIMER timer;
5486 
5487     UNUSED(context);
5488 
5489     KeInitializeTimer(&timer);
5490 
5491     due_time.QuadPart = (uint64_t)-10000000;
5492 
5493     KeSetTimer(&timer, due_time, NULL);
5494 
5495     while (true) {
5496         KeWaitForSingleObject(&timer, Executive, KernelMode, false, NULL);
5497 
5498         init_serial(false);
5499 
5500         if (comdo)
5501             break;
5502 
5503         KeSetTimer(&timer, due_time, NULL);
5504     }
5505 
5506     KeCancelTimer(&timer);
5507 
5508     PsTerminateSystemThread(STATUS_SUCCESS);
5509 
5510     serial_thread_handle = NULL;
5511 }
5512 
5513 static void init_serial(bool first_time) {
5514     NTSTATUS Status;
5515 
5516     Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
5517     if (!NT_SUCCESS(Status)) {
5518         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
5519 
5520         if (first_time) {
5521             Status = PsCreateSystemThread(&serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, NULL);
5522             if (!NT_SUCCESS(Status)) {
5523                 ERR("PsCreateSystemThread returned %08x\n", Status);
5524                 return;
5525             }
5526         }
5527     }
5528 }
5529 #endif
5530 
5531 #ifndef __REACTOS__
5532 static void check_cpu() {
5533     unsigned int cpuInfo[4];
5534 #ifndef _MSC_VER
5535     __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
5536     have_sse42 = cpuInfo[2] & bit_SSE4_2;
5537     have_sse2 = cpuInfo[3] & bit_SSE2;
5538 #else
5539    __cpuid(cpuInfo, 1);
5540    have_sse42 = cpuInfo[2] & (1 << 20);
5541    have_sse2 = cpuInfo[3] & (1 << 26);
5542 #endif
5543 
5544     if (have_sse42)
5545         TRACE("SSE4.2 is supported\n");
5546     else
5547         TRACE("SSE4.2 not supported\n");
5548 
5549     if (have_sse2)
5550         TRACE("SSE2 is supported\n");
5551     else
5552         TRACE("SSE2 is not supported\n");
5553 }
5554 #endif
5555 
5556 #ifdef _DEBUG
5557 static void init_logging() {
5558     ExAcquireResourceExclusiveLite(&log_lock, true);
5559 
5560     if (log_device.Length > 0)
5561         init_serial(true);
5562     else if (log_file.Length > 0) {
5563         NTSTATUS Status;
5564         OBJECT_ATTRIBUTES oa;
5565         IO_STATUS_BLOCK iosb;
5566         char* dateline;
5567         LARGE_INTEGER time;
5568         TIME_FIELDS tf;
5569 
5570         InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
5571 
5572         Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
5573                               FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
5574 
5575         if (!NT_SUCCESS(Status)) {
5576             ERR("ZwCreateFile returned %08x\n", Status);
5577             goto end;
5578         }
5579 
5580         if (iosb.Information == FILE_OPENED) { // already exists
5581             FILE_STANDARD_INFORMATION fsi;
5582             FILE_POSITION_INFORMATION fpi;
5583 
5584             static const char delim[] = "\n---\n";
5585 
5586             // move to end of file
5587 
5588             Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
5589 
5590             if (!NT_SUCCESS(Status)) {
5591                 ERR("ZwQueryInformationFile returned %08x\n", Status);
5592                 goto end;
5593             }
5594 
5595             fpi.CurrentByteOffset = fsi.EndOfFile;
5596 
5597             Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
5598 
5599             if (!NT_SUCCESS(Status)) {
5600                 ERR("ZwSetInformationFile returned %08x\n", Status);
5601                 goto end;
5602             }
5603 
5604             Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, (void*)delim, sizeof(delim) - 1, NULL, NULL);
5605 
5606             if (!NT_SUCCESS(Status)) {
5607                 ERR("ZwWriteFile returned %08x\n", Status);
5608                 goto end;
5609             }
5610         }
5611 
5612         dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
5613 
5614         if (!dateline) {
5615             ERR("out of memory\n");
5616             goto end;
5617         }
5618 
5619         KeQuerySystemTime(&time);
5620 
5621         RtlTimeToTimeFields(&time, &tf);
5622 
5623         sprintf(dateline, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
5624 
5625         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, (ULONG)strlen(dateline), NULL, NULL);
5626 
5627         ExFreePool(dateline);
5628 
5629         if (!NT_SUCCESS(Status)) {
5630             ERR("ZwWriteFile returned %08x\n", Status);
5631             goto end;
5632         }
5633     }
5634 
5635 end:
5636     ExReleaseResourceLite(&log_lock);
5637 }
5638 #endif
5639 
5640 _Function_class_(KSTART_ROUTINE)
5641 static void __stdcall degraded_wait_thread(_In_ void* context) {
5642     KTIMER timer;
5643     LARGE_INTEGER delay;
5644 
5645     UNUSED(context);
5646 
5647     KeInitializeTimer(&timer);
5648 
5649     delay.QuadPart = -30000000; // wait three seconds
5650     KeSetTimer(&timer, delay, NULL);
5651     KeWaitForSingleObject(&timer, Executive, KernelMode, false, NULL);
5652 
5653     TRACE("timer expired\n");
5654 
5655     degraded_wait = false;
5656 
5657     ZwClose(degraded_wait_handle);
5658     degraded_wait_handle = NULL;
5659 
5660     PsTerminateSystemThread(STATUS_SUCCESS);
5661 }
5662 
5663 _Function_class_(DRIVER_ADD_DEVICE)
5664 NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
5665     LIST_ENTRY* le;
5666     NTSTATUS Status;
5667     UNICODE_STRING volname;
5668     ULONG i, j;
5669     pdo_device_extension* pdode = NULL;
5670     PDEVICE_OBJECT voldev;
5671     volume_device_extension* vde;
5672 
5673     TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
5674 
5675     ExAcquireResourceSharedLite(&pdo_list_lock, true);
5676 
5677     le = pdo_list.Flink;
5678     while (le != &pdo_list) {
5679         pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
5680 
5681         if (pdode2->pdo == PhysicalDeviceObject) {
5682             pdode = pdode2;
5683             break;
5684         }
5685 
5686         le = le->Flink;
5687     }
5688 
5689     if (!pdode) {
5690         WARN("unrecognized PDO %p\n", PhysicalDeviceObject);
5691         Status = STATUS_NOT_SUPPORTED;
5692         goto end;
5693     }
5694 
5695     ExAcquireResourceSharedLite(&pdode->child_lock, true);
5696 
5697     volname.Length = volname.MaximumLength = (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) + ((36 + 1) * sizeof(WCHAR));
5698     volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
5699 
5700     if (!volname.Buffer) {
5701         ERR("out of memory\n");
5702         Status = STATUS_INSUFFICIENT_RESOURCES;
5703         goto end2;
5704     }
5705 
5706     RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
5707 
5708     j = (sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1;
5709     for (i = 0; i < 16; i++) {
5710         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] >> 4); j++;
5711         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] & 0xf); j++;
5712 
5713         if (i == 3 || i == 5 || i == 7 || i == 9) {
5714             volname.Buffer[j] = '-';
5715             j++;
5716         }
5717     }
5718 
5719     volname.Buffer[j] = '}';
5720 
5721     Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
5722                             WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, false, &voldev);
5723     if (!NT_SUCCESS(Status)) {
5724         ERR("IoCreateDevice returned %08x\n", Status);
5725         goto end2;
5726     }
5727 
5728     voldev->SectorSize = PhysicalDeviceObject->SectorSize;
5729     voldev->Flags |= DO_DIRECT_IO;
5730 
5731     vde = voldev->DeviceExtension;
5732     vde->type = VCB_TYPE_VOLUME;
5733     vde->name = volname;
5734     vde->device = voldev;
5735     vde->mounted_device = NULL;
5736     vde->pdo = PhysicalDeviceObject;
5737     vde->pdode = pdode;
5738     vde->removing = false;
5739     vde->open_count = 0;
5740 
5741     Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
5742     if (!NT_SUCCESS(Status))
5743         WARN("IoRegisterDeviceInterface returned %08x\n", Status);
5744 
5745     vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
5746 
5747     pdode->vde = vde;
5748 
5749     if (pdode->removable)
5750         voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
5751 
5752     voldev->Flags &= ~DO_DEVICE_INITIALIZING;
5753 
5754     Status = IoSetDeviceInterfaceState(&vde->bus_name, true);
5755     if (!NT_SUCCESS(Status))
5756         WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
5757 
5758     Status = STATUS_SUCCESS;
5759 
5760 end2:
5761     ExReleaseResourceLite(&pdode->child_lock);
5762 
5763 end:
5764     ExReleaseResourceLite(&pdo_list_lock);
5765 
5766     return Status;
5767 }
5768 
5769 _Function_class_(DRIVER_INITIALIZE)
5770 NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
5771     NTSTATUS Status;
5772     PDEVICE_OBJECT DeviceObject;
5773     UNICODE_STRING device_nameW;
5774     UNICODE_STRING dosdevice_nameW;
5775     control_device_extension* cde;
5776     bus_device_extension* bde;
5777     HANDLE regh;
5778     OBJECT_ATTRIBUTES oa;
5779     ULONG dispos;
5780 
5781     InitializeListHead(&uid_map_list);
5782     InitializeListHead(&gid_map_list);
5783 
5784 #ifdef _DEBUG
5785     ExInitializeResourceLite(&log_lock);
5786 #endif
5787     ExInitializeResourceLite(&mapping_lock);
5788 
5789     log_device.Buffer = NULL;
5790     log_device.Length = log_device.MaximumLength = 0;
5791     log_file.Buffer = NULL;
5792     log_file.Length = log_file.MaximumLength = 0;
5793 
5794     registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
5795     registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
5796 
5797     if (!registry_path.Buffer) {
5798         ERR("out of memory\n");
5799         return STATUS_INSUFFICIENT_RESOURCES;
5800     }
5801 
5802     RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
5803 
5804     read_registry(&registry_path, false);
5805 
5806 #ifdef _DEBUG
5807     if (debug_log_level > 0)
5808         init_logging();
5809 
5810     log_started = true;
5811 #endif
5812 
5813     TRACE("DriverEntry\n");
5814 
5815 #ifndef __REACTOS__
5816     check_cpu();
5817 #endif
5818 
5819     if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8)) {
5820         UNICODE_STRING name;
5821         tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled;
5822 
5823         RtlInitUnicodeString(&name, L"PsIsDiskCountersEnabled");
5824         fPsIsDiskCountersEnabled = (tPsIsDiskCountersEnabled)MmGetSystemRoutineAddress(&name);
5825 
5826         if (fPsIsDiskCountersEnabled) {
5827             diskacc = fPsIsDiskCountersEnabled();
5828 
5829             RtlInitUnicodeString(&name, L"PsUpdateDiskCounters");
5830             fPsUpdateDiskCounters = (tPsUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
5831 
5832             if (!fPsUpdateDiskCounters)
5833                 diskacc = false;
5834 
5835             RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
5836             fFsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
5837         }
5838 
5839         RtlInitUnicodeString(&name, L"CcCopyReadEx");
5840         fCcCopyReadEx = (tCcCopyReadEx)MmGetSystemRoutineAddress(&name);
5841 
5842         RtlInitUnicodeString(&name, L"CcCopyWriteEx");
5843         fCcCopyWriteEx = (tCcCopyWriteEx)MmGetSystemRoutineAddress(&name);
5844 
5845         RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
5846         fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
5847     } else {
5848         fPsUpdateDiskCounters = NULL;
5849         fCcCopyReadEx = NULL;
5850         fCcCopyWriteEx = NULL;
5851         fCcSetAdditionalCacheAttributesEx = NULL;
5852         fFsRtlUpdateDiskCounters = NULL;
5853     }
5854 
5855     if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN7)) {
5856         UNICODE_STRING name;
5857 
5858         RtlInitUnicodeString(&name, L"IoUnregisterPlugPlayNotificationEx");
5859         fIoUnregisterPlugPlayNotificationEx = (tIoUnregisterPlugPlayNotificationEx)MmGetSystemRoutineAddress(&name);
5860     } else
5861         fIoUnregisterPlugPlayNotificationEx = NULL;
5862 
5863     if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_VISTA)) {
5864         UNICODE_STRING name;
5865 
5866         RtlInitUnicodeString(&name, L"FsRtlGetEcpListFromIrp");
5867         fFsRtlGetEcpListFromIrp = (tFsRtlGetEcpListFromIrp)MmGetSystemRoutineAddress(&name);
5868 
5869         RtlInitUnicodeString(&name, L"FsRtlGetNextExtraCreateParameter");
5870         fFsRtlGetNextExtraCreateParameter = (tFsRtlGetNextExtraCreateParameter)MmGetSystemRoutineAddress(&name);
5871 
5872         RtlInitUnicodeString(&name, L"FsRtlValidateReparsePointBuffer");
5873         fFsRtlValidateReparsePointBuffer = (tFsRtlValidateReparsePointBuffer)MmGetSystemRoutineAddress(&name);
5874     } else {
5875         fFsRtlGetEcpListFromIrp = NULL;
5876         fFsRtlGetNextExtraCreateParameter = NULL;
5877         fFsRtlValidateReparsePointBuffer = compat_FsRtlValidateReparsePointBuffer;
5878     }
5879 
5880     drvobj = DriverObject;
5881 
5882     DriverObject->DriverUnload = DriverUnload;
5883 
5884     DriverObject->DriverExtension->AddDevice = AddDevice;
5885 
5886     DriverObject->MajorFunction[IRP_MJ_CREATE]                   = drv_create;
5887     DriverObject->MajorFunction[IRP_MJ_CLOSE]                    = drv_close;
5888     DriverObject->MajorFunction[IRP_MJ_READ]                     = drv_read;
5889     DriverObject->MajorFunction[IRP_MJ_WRITE]                    = drv_write;
5890     DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION]        = drv_query_information;
5891     DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION]          = drv_set_information;
5892     DriverObject->MajorFunction[IRP_MJ_QUERY_EA]                 = drv_query_ea;
5893     DriverObject->MajorFunction[IRP_MJ_SET_EA]                   = drv_set_ea;
5894     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]            = drv_flush_buffers;
5895     DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = drv_query_volume_information;
5896     DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION]   = drv_set_volume_information;
5897     DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL]        = drv_directory_control;
5898     DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL]      = drv_file_system_control;
5899     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]           = drv_device_control;
5900     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]                 = drv_shutdown;
5901     DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = drv_lock_control;
5902     DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = drv_cleanup;
5903     DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY]           = drv_query_security;
5904     DriverObject->MajorFunction[IRP_MJ_SET_SECURITY]             = drv_set_security;
5905     DriverObject->MajorFunction[IRP_MJ_POWER]                    = drv_power;
5906     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]           = drv_system_control;
5907     DriverObject->MajorFunction[IRP_MJ_PNP]                      = drv_pnp;
5908 
5909     init_fast_io_dispatch(&DriverObject->FastIoDispatch);
5910 
5911     device_nameW.Buffer = (WCHAR*)device_name;
5912     device_nameW.Length = device_nameW.MaximumLength = sizeof(device_name) - sizeof(WCHAR);
5913     dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
5914     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
5915 
5916     Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
5917                             FILE_DEVICE_SECURE_OPEN, false, &DeviceObject);
5918     if (!NT_SUCCESS(Status)) {
5919         ERR("IoCreateDevice returned %08x\n", Status);
5920         return Status;
5921     }
5922 
5923     master_devobj = DeviceObject;
5924     cde = (control_device_extension*)master_devobj->DeviceExtension;
5925 
5926     RtlZeroMemory(cde, sizeof(control_device_extension));
5927 
5928     cde->type = VCB_TYPE_CONTROL;
5929 
5930     DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
5931 
5932     Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
5933     if (!NT_SUCCESS(Status)) {
5934         ERR("IoCreateSymbolicLink returned %08x\n", Status);
5935         return Status;
5936     }
5937 
5938     Status = init_cache();
5939     if (!NT_SUCCESS(Status)) {
5940         ERR("init_cache returned %08x\n", Status);
5941         return Status;
5942     }
5943 
5944     InitializeListHead(&VcbList);
5945     ExInitializeResourceLite(&global_loading_lock);
5946     ExInitializeResourceLite(&pdo_list_lock);
5947 
5948     InitializeListHead(&pdo_list);
5949 
5950     InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
5951     Status = ZwCreateKey(&regh, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
5952     if (!NT_SUCCESS(Status)) {
5953         ERR("ZwCreateKey returned %08x\n", Status);
5954         return Status;
5955     }
5956 
5957     watch_registry(regh);
5958 
5959     Status = IoCreateDevice(DriverObject, sizeof(bus_device_extension), NULL, FILE_DEVICE_UNKNOWN,
5960                             FILE_DEVICE_SECURE_OPEN, false, &busobj);
5961     if (!NT_SUCCESS(Status)) {
5962         ERR("IoCreateDevice returned %08x\n", Status);
5963         return Status;
5964     }
5965 
5966     bde = (bus_device_extension*)busobj->DeviceExtension;
5967 
5968     RtlZeroMemory(bde, sizeof(bus_device_extension));
5969 
5970     bde->type = VCB_TYPE_BUS;
5971 
5972     Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
5973                                     NULL, NULL, 0, &bde->buspdo);
5974     if (!NT_SUCCESS(Status)) {
5975         ERR("IoReportDetectedDevice returned %08x\n", Status);
5976         return Status;
5977     }
5978 
5979     Status = IoRegisterDeviceInterface(bde->buspdo, &BtrfsBusInterface, NULL, &bde->bus_name);
5980     if (!NT_SUCCESS(Status))
5981         WARN("IoRegisterDeviceInterface returned %08x\n", Status);
5982 
5983     bde->attached_device = IoAttachDeviceToDeviceStack(busobj, bde->buspdo);
5984 
5985     busobj->Flags &= ~DO_DEVICE_INITIALIZING;
5986 
5987     Status = IoSetDeviceInterfaceState(&bde->bus_name, true);
5988     if (!NT_SUCCESS(Status))
5989         WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
5990 
5991     IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
5992 
5993     Status = PsCreateSystemThread(&degraded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL);
5994     if (!NT_SUCCESS(Status))
5995         WARN("PsCreateSystemThread returned %08x\n", Status);
5996 
5997     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
5998                                             (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
5999     if (!NT_SUCCESS(Status))
6000         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
6001 
6002     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
6003                                             (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
6004     if (!NT_SUCCESS(Status))
6005         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
6006 
6007     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
6008                                             (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
6009     if (!NT_SUCCESS(Status))
6010         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
6011 
6012     finished_probing = true;
6013 
6014     KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, false);
6015 
6016     Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, NULL, NULL, NULL, mountmgr_thread, NULL);
6017     if (!NT_SUCCESS(Status))
6018         WARN("PsCreateSystemThread returned %08x\n", Status);
6019 
6020     IoRegisterFileSystem(DeviceObject);
6021 
6022     IoRegisterBootDriverReinitialization(DriverObject, check_system_root, NULL);
6023 
6024     return STATUS_SUCCESS;
6025 }
6026