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