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