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