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