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