[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / btrfs.c
index 54723e2..0e3e917 100644 (file)
@@ -1,17 +1,17 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * This file is part of WinBtrfs.
- * 
+ *
  * WinBtrfs is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public Licence as published by
  * the Free Software Foundation, either version 3 of the Licence, or
  * (at your option) any later version.
- * 
+ *
  * WinBtrfs is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public Licence for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public Licence
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <intrin.h>
 #endif
 #endif
+#include <ntddscsi.h>
 #include "btrfs.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#else
-#include <rtlfuncs.h>
+#include <ata.h>
+
+#ifndef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
 #endif
-#include <mountdev.h>
 
-#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
-                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
-#define COMPAT_RO_SUPPORTED 0
+#include <ntdddisk.h>
+#include <ntddvol.h>
+
+#ifdef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
+#endif
+
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
+                            BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
+                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
+#define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
 
 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
 static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
 
+DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
+
 PDRIVER_OBJECT drvobj;
-PDEVICE_OBJECT devobj;
+PDEVICE_OBJECT master_devobj;
 #ifndef __REACTOS__
-BOOL have_sse42 = FALSE;
+BOOL have_sse42 = FALSE, have_sse2 = FALSE;
 #endif
 UINT64 num_reads = 0;
-LIST_ENTRY uid_map_list;
-LIST_ENTRY volumes;
+LIST_ENTRY uid_map_list, gid_map_list;
 LIST_ENTRY VcbList;
 ERESOURCE global_loading_lock;
 UINT32 debug_log_level = 0;
+UINT32 mount_compress = 0;
+UINT32 mount_compress_force = 0;
+UINT32 mount_compress_type = 0;
+UINT32 mount_zlib_level = 3;
+UINT32 mount_flush_interval = 30;
+UINT32 mount_max_inline = 2048;
+UINT32 mount_skip_balance = 0;
+UINT32 mount_no_barrier = 0;
+UINT32 mount_no_trim = 0;
+UINT32 mount_clear_cache = 0;
+UINT32 mount_allow_degraded = 0;
+UINT32 mount_readonly = 0;
+UINT32 no_pnp = 0;
 BOOL log_started = FALSE;
 UNICODE_STRING log_device, log_file, registry_path;
+tPsUpdateDiskCounters fPsUpdateDiskCounters;
+tCcCopyReadEx fCcCopyReadEx;
+tCcCopyWriteEx fCcCopyWriteEx;
+tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx;
+tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
+BOOL diskacc = FALSE;
+void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
+ERESOURCE pdo_list_lock, mapping_lock;
+LIST_ENTRY pdo_list;
+BOOL finished_probing = FALSE;
+HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
+BOOL degraded_wait = TRUE;
+KEVENT mountmgr_thread_event;
+BOOL shutting_down = FALSE;
 
 #ifdef _DEBUG
 PFILE_OBJECT comfo = NULL;
 PDEVICE_OBJECT comdo = NULL;
 HANDLE log_handle = NULL;
-#endif
+ERESOURCE log_lock;
+HANDLE serial_thread_handle = NULL;
 
-int __security_cookie = __LINE__;
+static void init_serial(BOOL first_time);
+#endif
 
-static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
+static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp);
 
 typedef struct {
     KEVENT Event;
@@ -72,49 +114,51 @@ typedef struct {
 } read_context;
 
 #ifdef _DEBUG
-static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+_Function_class_(IO_COMPLETION_ROUTINE)
+static NTSTATUS dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
     read_context* context = conptr;
-    
-//     DbgPrint("dbg_completion\n");
-    
+
+    UNUSED(DeviceObject);
+
     context->iosb = Irp->IoStatus;
     KeSetEvent(&context->Event, 0, FALSE);
-    
-//     return STATUS_SUCCESS;
+
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
 #ifdef DEBUG_LONG_MESSAGES
-void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
+void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
 #else
-void STDCALL _debug_message(const char* func, char* s, ...) {
+void _debug_message(_In_ const char* func, _In_ char* s, ...) {
 #endif
     LARGE_INTEGER offset;
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
     PIRP Irp;
     va_list ap;
-    char *buf2 = NULL, *buf;
-    read_context* context = NULL;
+    char *buf2, *buf;
+    read_context context;
     UINT32 length;
-    
+
     buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
-    
+
     if (!buf2) {
         DbgPrint("Couldn't allocate buffer in debug_message\n");
         return;
     }
-    
+
 #ifdef DEBUG_LONG_MESSAGES
-    sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+    sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThread(), func, file, line);
 #else
-    sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+    sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func);
 #endif
     buf = &buf2[strlen(buf2)];
-    
+
     va_start(ap, s);
     vsprintf(buf, s, ap);
-    
+
+    ExAcquireResourceSharedLite(&log_lock, TRUE);
+
     if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
         DbgPrint(buf2);
     } else if (log_device.Length > 0) {
@@ -123,34 +167,26 @@ void STDCALL _debug_message(const char* func, char* s, ...) {
             DbgPrint(buf2);
             goto exit2;
         }
-        
+
         length = (UINT32)strlen(buf2);
-        
+
         offset.u.LowPart = 0;
         offset.u.HighPart = 0;
-        
-        context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
-        if (!context) {
-            DbgPrint("Couldn't allocate context in debug_message\n");
-            return;
-        }
-        
-        RtlZeroMemory(context, sizeof(read_context));
-        
-        KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
 
-    //     status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
-        
+        RtlZeroMemory(&context, sizeof(read_context));
+
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+
         Irp = IoAllocateIrp(comdo->StackSize, FALSE);
-        
+
         if (!Irp) {
             DbgPrint("IoAllocateIrp failed\n");
             goto exit2;
         }
-        
+
         IrpSp = IoGetNextIrpStackLocation(Irp);
         IrpSp->MajorFunction = IRP_MJ_WRITE;
-        
+
         if (comdo->Flags & DO_BUFFERED_IO) {
             Irp->AssociatedIrp.SystemBuffer = buf2;
 
@@ -161,104 +197,61 @@ void STDCALL _debug_message(const char* func, char* s, ...) {
                 DbgPrint("IoAllocateMdl failed\n");
                 goto exit;
             }
-            
-            MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+
+            MmBuildMdlForNonPagedPool(Irp->MdlAddress);
         } else {
             Irp->UserBuffer = buf2;
         }
 
         IrpSp->Parameters.Write.Length = length;
         IrpSp->Parameters.Write.ByteOffset = offset;
-        
-        Irp->UserIosb = &context->iosb;
 
-        Irp->UserEvent = &context->Event;
+        Irp->UserIosb = &context.iosb;
 
-        IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
+        Irp->UserEvent = &context.Event;
+
+        IoSetCompletionRoutine(Irp, dbg_completion, &context, TRUE, TRUE, TRUE);
 
         Status = IoCallDriver(comdo, Irp);
 
         if (Status == STATUS_PENDING) {
-            KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-            Status = context->iosb.Status;
+            KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+            Status = context.iosb.Status;
         }
-        
-        if (comdo->Flags & DO_DIRECT_IO) {
-            MmUnlockPages(Irp->MdlAddress);
+
+        if (comdo->Flags & DO_DIRECT_IO)
             IoFreeMdl(Irp->MdlAddress);
-        }
-        
+
         if (!NT_SUCCESS(Status)) {
             DbgPrint("failed to write to COM1 - error %08x\n", Status);
             goto exit;
         }
-        
+
 exit:
         IoFreeIrp(Irp);
     } else if (log_handle != NULL) {
         IO_STATUS_BLOCK iosb;
-        
+
         length = (UINT32)strlen(buf2);
-        
+
         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
-        
+
         if (!NT_SUCCESS(Status)) {
             DbgPrint("failed to write to file - error %08x\n", Status);
         }
     }
-    
+
 exit2:
+    ExReleaseResourceLite(&log_lock);
+
     va_end(ap);
-    
-    if (context)
-        ExFreePool(context);
-    
+
     if (buf2)
         ExFreePool(buf2);
 }
 #endif
 
-UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment )
-{
-    if( Alignment & ( Alignment - 1 ) )
-    {
-        //
-        //  Alignment not a power of 2
-        //  Just returning
-        //
-        return NumberToBeAligned;
-    }
-    if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
-    {
-        NumberToBeAligned = NumberToBeAligned + Alignment;
-        NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
-    }
-    return NumberToBeAligned;
-}
-
-int keycmp(const KEY* key1, const KEY* key2) {
-    if (key1->obj_id < key2->obj_id) {
-        return -1;
-    } else if (key1->obj_id > key2->obj_id) {
-        return 1;
-    }
-    
-    if (key1->obj_type < key2->obj_type) {
-        return -1;
-    } else if (key1->obj_type > key2->obj_type) {
-        return 1;
-    }
-    
-    if (key1->offset < key2->offset) {
-        return -1;
-    } else if (key1->offset > key2->offset) {
-        return 1;
-    }
-    
-    return 0;
-}
-
-BOOL is_top_level(PIRP Irp) {
+BOOL is_top_level(_In_ PIRP Irp) {
     if (!IoGetTopLevelIrp()) {
         IoSetTopLevelIrp(Irp);
         return TRUE;
@@ -267,144 +260,162 @@ BOOL is_top_level(PIRP Irp) {
     return FALSE;
 }
 
-static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
+_Function_class_(DRIVER_UNLOAD)
+#ifdef __REACTOS__
+static void NTAPI DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
+#else
+static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
+#endif
     UNICODE_STRING dosdevice_nameW;
 
     ERR("DriverUnload\n");
-    
+
     free_cache();
-    
+
     IoUnregisterFileSystem(DriverObject->DeviceObject);
-   
+
+    if (notification_entry2)
+#ifdef __REACTOS__
+        IoUnregisterPlugPlayNotification(notification_entry2);
+#else
+        IoUnregisterPlugPlayNotificationEx(notification_entry2);
+#endif
+
+    if (notification_entry3)
+#ifdef __REACTOS__
+        IoUnregisterPlugPlayNotification(notification_entry3);
+#else
+        IoUnregisterPlugPlayNotificationEx(notification_entry3);
+#endif
+
+    if (notification_entry)
+#ifdef __REACTOS__
+        IoUnregisterPlugPlayNotification(notification_entry);
+#else
+        IoUnregisterPlugPlayNotificationEx(notification_entry);
+#endif
+
     dosdevice_nameW.Buffer = dosdevice_name;
     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
 
     IoDeleteSymbolicLink(&dosdevice_nameW);
     IoDeleteDevice(DriverObject->DeviceObject);
-    
+
     while (!IsListEmpty(&uid_map_list)) {
         LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
         uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
-        
+
         ExFreePool(um->sid);
 
         ExFreePool(um);
     }
-    
+
+    while (!IsListEmpty(&gid_map_list)) {
+        gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
+
+        ExFreePool(gm->sid);
+        ExFreePool(gm);
+    }
+
     // FIXME - free volumes and their devpaths
-    
+
 #ifdef _DEBUG
     if (comfo)
         ObDereferenceObject(comfo);
-    
+
     if (log_handle)
         ZwClose(log_handle);
 #endif
-    
+
     ExDeleteResourceLite(&global_loading_lock);
-    
+    ExDeleteResourceLite(&pdo_list_lock);
+
     if (log_device.Buffer)
         ExFreePool(log_device.Buffer);
-    
+
     if (log_file.Buffer)
         ExFreePool(log_file.Buffer);
-    
+
     if (registry_path.Buffer)
         ExFreePool(registry_path.Buffer);
+
+#ifdef _DEBUG
+    ExDeleteResourceLite(&log_lock);
+#endif
+    ExDeleteResourceLite(&mapping_lock);
 }
 
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+static BOOL get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_opt_ PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, prev_tp;
     NTSTATUS Status;
-    
+
     // get last entry
     searchkey.obj_id = 0xffffffffffffffff;
     searchkey.obj_type = 0xff;
     searchkey.offset = 0xffffffffffffffff;
-    
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
     }
-    
-    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+
+    if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
+        r->lastinode = tp.item->key.obj_id;
+        TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
+        return TRUE;
+    }
+
+    while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
         tp = prev_tp;
-        
+
         TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        
+
         if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
             r->lastinode = tp.item->key.obj_id;
             TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
             return TRUE;
         }
     }
-    
+
     r->lastinode = SUBVOL_ROOT_INODE;
-    
+
     WARN("no INODE_ITEMs in tree %llx\n", r->id);
-    
+
     return TRUE;
 }
 
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
-    KEY searchkey;
-    traverse_ptr tp;
-    DIR_ITEM* xa;
-    ULONG size, xasize;
-    NTSTATUS Status;
-    
-    TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return FALSE;
-    }
-    
-    if (keycmp(&tp.item->key, &searchkey)) {
-        TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-        return FALSE;
-    }
-    
-    if (tp.item->size < sizeof(DIR_ITEM)) {
-        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));
-        return FALSE;
-    }
-    
-    xa = (DIR_ITEM*)tp.item->data;
-    size = tp.item->size;
-    
+_Success_(return)
+static BOOL extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ UINT8** data, _Out_ UINT16* datalen) {
+    DIR_ITEM* xa = (DIR_ITEM*)item;
+    USHORT xasize;
+
     while (TRUE) {
         if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
-            WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+            WARN("DIR_ITEM is truncated\n");
             return FALSE;
         }
-        
+
         if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
-            TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-            
+            TRACE("found xattr %s\n", name);
+
             *datalen = xa->m;
-            
+
             if (xa->m > 0) {
                 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
                 if (!*data) {
                     ERR("out of memory\n");
                     return FALSE;
                 }
-                
+
                 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
             } else
                 *data = NULL;
-            
+
             return TRUE;
         }
-        
+
         xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
 
         if (size > xasize) {
@@ -413,168 +424,96 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
         } else
             break;
     }
-    
-    TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-    
+
+    TRACE("xattr %s not found\n", name);
+
     return FALSE;
 }
 
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
+_Success_(return)
+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,
+               _Out_ UINT8** data, _Out_ UINT16* datalen, _In_opt_ PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
-    UINT8* di2;
     NTSTATUS Status;
-    
+
+    TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
     searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_DIR_ITEM;
+    searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
-        return Status;
+        return FALSE;
     }
-    
-    if (!keycmp(&tp.item->key, &searchkey)) {
-        ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
-        
-        if (tp.item->size + disize > maxlen) {
-            WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
-            return STATUS_INTERNAL_ERROR;
-        }
-        
-        di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
-        if (!di2) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-        
-        if (tp.item->size > 0)
-            RtlCopyMemory(di2, tp.item->data, tp.item->size);
-        
-        RtlCopyMemory(di2 + tp.item->size, di, disize);
-        
-        delete_tree_item(Vcb, &tp, rollback);
-        
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
-        
-        ExFreePool(di);
-    } else {
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
+
+    if (keycmp(tp.item->key, searchkey)) {
+        TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+        return FALSE;
     }
-    
-    return STATUS_SUCCESS;
+
+    if (tp.item->size < sizeof(DIR_ITEM)) {
+        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));
+        return FALSE;
+    }
+
+    return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
 }
 
-static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_CLOSE)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp;
     device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
-    TRACE("close\n");
-    
     FsRtlEnterFileSystem();
 
+    TRACE("close\n");
+
     top_level = is_top_level(Irp);
-    
-    if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
+
+    if (DeviceObject == master_devobj) {
         TRACE("Closing file system\n");
         Status = STATUS_SUCCESS;
-        goto exit;
+        goto end;
+    } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_close(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
-    
+
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    
+
     // FIXME - unmount if called for volume
     // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
-    
-    Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
-
-exit:
-    Irp->IoStatus.Status = Status;
-    Irp->IoStatus.Information = 0;
-    
-    IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-    
-    if (top_level) 
-        IoSetTopLevelIrp(NULL);
-    
-    FsRtlExitFileSystem();
-    
-    TRACE("returning %08x\n", Status);
-
-    return Status;
-}
-
-static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    BOOL top_level;
-    device_extension* Vcb = DeviceObject->DeviceExtension;
 
-    FsRtlEnterFileSystem();
+    Status = close_file(IrpSp->FileObject, Irp);
 
-    top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
-    }
-    
-    FIXME("STUB: query ea\n");
-    Status = STATUS_NOT_IMPLEMENTED;
-    
+end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
 
-    IoCompleteRequest( Irp, IO_NO_INCREMENT );
+    IoCompleteRequest( Irp, IO_DISK_INCREMENT );
 
-exit:
-    if (top_level) 
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
-    FsRtlExitFileSystem();
-
-    return Status;
-}
-
-static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    device_extension* Vcb = DeviceObject->DeviceExtension;
-    BOOL top_level;
-
-    FsRtlEnterFileSystem();
 
-    top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
-    }
-    
-    FIXME("STUB: set ea\n");
-    Status = STATUS_NOT_IMPLEMENTED;
-    
-    if (Vcb->readonly)
-        Status = STATUS_MEDIA_WRITE_PROTECTED;
-    
-    // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
-    
-    Irp->IoStatus.Status = Status;
-    Irp->IoStatus.Information = 0;
+    TRACE("returning %08x\n", Status);
 
-    IoCompleteRequest( Irp, IO_NO_INCREMENT );
-    
-exit:
-    if (top_level) 
-        IoSetTopLevelIrp(NULL);
-    
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
     PFILE_OBJECT FileObject = IrpSp->FileObject;
@@ -582,180 +521,327 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR
     device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
-    TRACE("flush buffers\n");
-    
     FsRtlEnterFileSystem();
 
+    TRACE("flush buffers\n");
+
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_flush_buffers(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
+
+    if (!fcb) {
+        ERR("fcb was NULL\n");
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
+
+    if (fcb == Vcb->volume_fcb) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
-    
+
+    Irp->IoStatus.Information = 0;
+
+    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+
     Status = STATUS_SUCCESS;
     Irp->IoStatus.Status = Status;
-    Irp->IoStatus.Information = 0;
-    
+
     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
         CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
-        
+
         if (fcb->Header.PagingIoResource) {
             ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
             ExReleaseResourceLite(fcb->Header.PagingIoResource);
         }
-        
+
         Status = Irp->IoStatus.Status;
     }
-    
+
+end:
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
-    
-exit:
-    if (top_level) 
+
+    TRACE("returning %08x\n", Status);
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-static void calculate_total_space(device_extension* Vcb, LONGLONG* totalsize, LONGLONG* freespace) {
-    UINT8 factor;
-    
-    if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10)
-        factor = 2;
-    else
-        factor = 1;
-    
-    *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) / factor;
-    *freespace = ((Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size) / factor;
+static void calculate_total_space(_In_ device_extension* Vcb, _Out_ UINT64* totalsize, _Out_ UINT64* freespace) {
+    UINT64 nfactor, dfactor, sectors_used;
+
+    if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
+        nfactor = 1;
+        dfactor = 2;
+    } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
+        nfactor = Vcb->superblock.num_devices - 1;
+        dfactor = Vcb->superblock.num_devices;
+    } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
+        nfactor = Vcb->superblock.num_devices - 2;
+        dfactor = Vcb->superblock.num_devices;
+    } else {
+        nfactor = 1;
+        dfactor = 1;
+    }
+
+    sectors_used = Vcb->superblock.bytes_used / Vcb->superblock.sector_size;
+
+    *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
+    *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
 }
 
-static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    PIO_STACK_LOCATION IrpSp;
-    NTSTATUS Status;
-    ULONG BytesCopied = 0;
-    device_extension* Vcb = DeviceObject->DeviceExtension;
-    BOOL top_level;
-    
 #ifndef __REACTOS__
-    // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
-    // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
-    // which prevents UAC from working.
-    // FIXME - only lie if we detect that we're being called by mpr.dll
-    
-    WCHAR* fs_name = L"NTFS";
-    ULONG fs_name_len = 4 * sizeof(WCHAR);
-#else
-    WCHAR* fs_name = L"Btrfs";
-    ULONG fs_name_len = 5 * sizeof(WCHAR);
-#endif
+// This function exists because we have to lie about our FS type in certain situations.
+// MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
+// it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
+// The command mklink refuses to create hard links on anything other than NTFS, so we have to
+// blacklist cmd.exe too.
+
+static BOOL lie_about_fs_type() {
+    NTSTATUS Status;
+    PROCESS_BASIC_INFORMATION pbi;
+    PPEB peb;
+    LIST_ENTRY* le;
+    ULONG retlen;
+
+    static WCHAR mpr[] = L"MPR.DLL";
+    static WCHAR cmd[] = L"CMD.EXE";
+    static WCHAR fsutil[] = L"FSUTIL.EXE";
+    UNICODE_STRING mprus, cmdus, fsutilus;
+
+    mprus.Buffer = mpr;
+    mprus.Length = mprus.MaximumLength = (USHORT)(wcslen(mpr) * sizeof(WCHAR));
+    cmdus.Buffer = cmd;
+    cmdus.Length = cmdus.MaximumLength = (USHORT)(wcslen(cmd) * sizeof(WCHAR));
+    fsutilus.Buffer = fsutil;
+    fsutilus.Length = fsutilus.MaximumLength = (USHORT)(wcslen(fsutil) * sizeof(WCHAR));
+
+    if (!PsGetCurrentProcess())
+        return FALSE;
+
+    Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
+
+    if (!NT_SUCCESS(Status)) {
+        ERR("ZwQueryInformationProcess returned %08x\n", Status);
+        return FALSE;
+    }
+
+    if (!pbi.PebBaseAddress)
+        return FALSE;
+
+    peb = pbi.PebBaseAddress;
+
+    if (!peb->Ldr)
+        return FALSE;
+
+    le = peb->Ldr->InMemoryOrderModuleList.Flink;
+    while (le != &peb->Ldr->InMemoryOrderModuleList) {
+        LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+        BOOL blacklist = FALSE;
+
+        if (entry->FullDllName.Length >= mprus.Length) {
+            UNICODE_STRING name;
+
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = mprus.Length;
+
+            blacklist = FsRtlAreNamesEqual(&name, &mprus, TRUE, NULL);
+        }
+
+        if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
+            UNICODE_STRING name;
+
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = cmdus.Length;
+
+            blacklist = FsRtlAreNamesEqual(&name, &cmdus, TRUE, NULL);
+        }
+
+        if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) {
+            UNICODE_STRING name;
+
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - fsutilus.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = fsutilus.Length;
+
+            blacklist = FsRtlAreNamesEqual(&name, &fsutilus, TRUE, NULL);
+        }
+
+        if (blacklist) {
+            void** frames;
+            ULONG i, num_frames;
+
+            frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
+            if (!frames) {
+                ERR("out of memory\n");
+                return FALSE;
+            }
+
+            num_frames = RtlWalkFrameChain(frames, 256, 1);
+
+            for (i = 0; i < num_frames; i++) {
+                // entry->Reserved3[1] appears to be the image size
+                if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
+                    ExFreePool(frames);
+                    return TRUE;
+                }
+            }
+
+            ExFreePool(frames);
+        }
+
+        le = le->Flink;
+    }
+
+    return FALSE;
+}
+#endif
+
+_Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp;
+    NTSTATUS Status;
+    ULONG BytesCopied = 0;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    BOOL top_level;
+
+    FsRtlEnterFileSystem();
+
+    TRACE("query volume information\n");
+    top_level = is_top_level(Irp);
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_query_volume_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
+
+    IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+    Status = STATUS_NOT_IMPLEMENTED;
+
+    switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
+        case FileFsAttributeInformation:
+        {
+            FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+            BOOL overflow = FALSE;
+#ifndef __REACTOS__
+            WCHAR* fs_name = (Irp->RequestorMode == UserMode && lie_about_fs_type()) ? L"NTFS" : L"Btrfs";
+            ULONG fs_name_len = (ULONG)wcslen(fs_name) * sizeof(WCHAR);
+#else
+            WCHAR* fs_name = L"Btrfs";
+            ULONG fs_name_len = 5 * sizeof(WCHAR);
+#endif
+            ULONG orig_fs_name_len = fs_name_len;
+
+            TRACE("FileFsAttributeInformation\n");
+
+            if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
+                if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
+                    fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
+                else
+                    fs_name_len = 0;
+
+                overflow = TRUE;
+            }
 
-    TRACE("query volume information\n");
-    
-    FsRtlEnterFileSystem();
-    top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
-    }    
-    
-    IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    
-    Status = STATUS_NOT_IMPLEMENTED;
-    
-    switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
-        case FileFsAttributeInformation:
-        {
-            FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
-            BOOL overflow = FALSE;
-            ULONG orig_fs_name_len = fs_name_len;
-            
-            TRACE("FileFsAttributeInformation\n");
-            
-            if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
-                if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
-                    fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
-                else
-                    fs_name_len = 0;
-                
-                overflow = TRUE;
-            }
-            
             data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
                                          FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
-                                         FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS;
+                                         FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
+                                         FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING;
             if (Vcb->readonly)
                 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
-                                         
+
             // should also be FILE_FILE_COMPRESSION when supported
             data->MaximumComponentNameLength = 255; // FIXME - check
             data->FileSystemNameLength = orig_fs_name_len;
             RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
-            
+
             BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
             break;
         }
 
-        case FileFsControlInformation:
-            FIXME("STUB: FileFsControlInformation\n");
-            break;
-
         case FileFsDeviceInformation:
-            FIXME("STUB: FileFsDeviceInformation\n");
-            break;
+        {
+            FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
+
+            TRACE("FileFsDeviceInformation\n");
+
+            ffdi->DeviceType = FILE_DEVICE_DISK;
+
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+            ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
+            ExReleaseResourceLite(&Vcb->tree_lock);
+
+            if (Vcb->readonly)
+                ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
+            else
+                ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
+
+            BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
+            Status = STATUS_SUCCESS;
 
-        case FileFsDriverPathInformation:
-            FIXME("STUB: FileFsDriverPathInformation\n");
             break;
+        }
 
         case FileFsFullSizeInformation:
         {
             FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
-            
+
             TRACE("FileFsFullSizeInformation\n");
-            
-            calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->ActualAvailableAllocationUnits.QuadPart);
+
+            calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->ActualAvailableAllocationUnits.QuadPart);
             ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
             ffsi->SectorsPerAllocationUnit = 1;
             ffsi->BytesPerSector = Vcb->superblock.sector_size;
-            
+
             BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
             Status = STATUS_SUCCESS;
-            
+
             break;
         }
 
         case FileFsObjectIdInformation:
         {
             FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
-            
+
             TRACE("FileFsObjectIdInformation\n");
-            
+
             RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
             RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
-            
+
             BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
             Status = STATUS_SUCCESS;
-            
+
             break;
         }
 
         case FileFsSizeInformation:
         {
             FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
-            
+
             TRACE("FileFsSizeInformation\n");
-            
-            calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->AvailableAllocationUnits.QuadPart);
+
+            calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->AvailableAllocationUnits.QuadPart);
             ffsi->SectorsPerAllocationUnit = 1;
             ffsi->BytesPerSector = Vcb->superblock.sector_size;
-            
+
             BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
             Status = STATUS_SUCCESS;
-            
+
             break;
         }
 
@@ -765,42 +851,52 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             FILE_FS_VOLUME_INFORMATION ffvi;
             BOOL overflow = FALSE;
             ULONG label_len, orig_label_len;
-            
+
             TRACE("FileFsVolumeInformation\n");
             TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
-            
+
             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-            
-//             orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
-            RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+
+            Status = RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+                ExReleaseResourceLite(&Vcb->tree_lock);
+                break;
+            }
+
             orig_label_len = label_len;
-            
+
             if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
                 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
                     label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
                 else
                     label_len = 0;
-                
+
                 overflow = TRUE;
             }
-            
+
             TRACE("label_len = %u\n", label_len);
-            
+
             ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
             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];
             ffvi.VolumeLabelLength = orig_label_len;
             ffvi.SupportsObjects = FALSE;
-            
+
             RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
-            
+
             if (label_len > 0) {
                 ULONG bytecount;
-                
-//                 RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
-                RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+
+                Status = RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+                if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+                    ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+                    ExReleaseResourceLite(&Vcb->tree_lock);
+                    break;
+                }
+
                 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
             }
-            
+
             ExReleaseResourceLite(&Vcb->tree_lock);
 
             BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
@@ -808,136 +904,94 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             break;
         }
 
+#ifndef __REACTOS__
+#ifdef _MSC_VER // not in mingw yet
+        case FileFsSectorSizeInformation:
+        {
+            FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+
+            data->LogicalBytesPerSector = Vcb->superblock.sector_size;
+            data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
+            data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
+            data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
+            data->ByteOffsetForSectorAlignment = 0;
+            data->ByteOffsetForPartitionAlignment = 0;
+
+            data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
+
+            if (Vcb->trim && !Vcb->options.no_trim)
+                data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
+
+            BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
+
+            break;
+        }
+#endif
+#endif /* __REACTOS__ */
+
         default:
             Status = STATUS_INVALID_PARAMETER;
             WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
             break;
     }
-    
-//     if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
-//         WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
-//         BytesCopied = IrpSp->Parameters.QueryVolume.Length;
-//         Status = STATUS_BUFFER_OVERFLOW;
-//     }
 
-    Irp->IoStatus.Status = Status;
-    
     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
         Irp->IoStatus.Information = 0;
     else
         Irp->IoStatus.Information = BytesCopied;
-    
+
+end:
+    Irp->IoStatus.Status = Status;
+
     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-    
-exit:
-    if (top_level) 
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
-    FsRtlExitFileSystem();
-    
+
     TRACE("query volume information returning %08x\n", Status);
 
+    FsRtlExitFileSystem();
+
     return Status;
 }
 
-static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+_Function_class_(IO_COMPLETION_ROUTINE)
+#ifdef __REACTOS__
+static NTSTATUS NTAPI read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
+#else
+static NTSTATUS read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
+#endif
     read_context* context = conptr;
-    
-//     DbgPrint("read_completion\n");
-    
+
+    UNUSED(DeviceObject);
+
     context->iosb = Irp->IoStatus;
     KeSetEvent(&context->Event, 0, FALSE);
-    
-//     return STATUS_SUCCESS;
+
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
-// static void test_tree_deletion(device_extension* Vcb) {
-//     KEY searchkey/*, endkey*/;
-//     traverse_ptr tp, next_tp;
-//     root* r;
-//     
-//     searchkey.obj_id = 0x100;
-//     searchkey.obj_type = 0x54;
-//     searchkey.offset = 0xca4ab2f5;
-//     
-// //     endkey.obj_id = 0x100;
-// //     endkey.obj_type = 0x60;
-// //     endkey.offset = 0x15a;
-//     
-//     r = Vcb->roots;
-//     while (r && r->id != 0x102)
-//         r = r->next;
-//     
-//     if (!r) {
-//         ERR("error - could not find root\n");
-//         return;
-//     }
-//     
-//     if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
-//         ERR("error - could not find key\n");
-//         return;
-//     }
-//     
-//     while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
-//         tp.item->ignore = TRUE;
-//         add_to_tree_cache(tc, tp.tree);
-//         
-//         if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
-//             free_traverse_ptr(&tp);
-//             tp = next_tp;
-//         } else
-//             break;
-//     }
-//     
-//     free_traverse_ptr(&tp);
-// }
-
-// static void test_tree_splitting(device_extension* Vcb) {
-//     int i;
-//     
-//     for (i = 0; i < 1000; i++) {
-//         char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
-//         
-//         insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
-//     }
-// }
-
-// static void test_dropping_tree(device_extension* Vcb) {
-//     LIST_ENTRY* le = Vcb->roots.Flink;
-//     
-//     while (le != &Vcb->roots) {
-//         root* r = CONTAINING_RECORD(le, root, list_entry);
-//         
-//         if (r->id == 0x101) {
-//             RemoveEntryList(&r->list_entry);
-//             InsertTailList(&Vcb->drop_roots, &r->list_entry);
-//             return;
-//         }
-//         
-//         le = le->Flink;
-//     }
-// }
-
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
+NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ UINT64 id,
+                     _Out_ root** rootptr, _In_ BOOL no_tree, _In_ UINT64 offset, _In_opt_ PIRP Irp) {
+    NTSTATUS Status;
     root* r;
-    tree* t;
+    tree* t = NULL;
     ROOT_ITEM* ri;
     traverse_ptr tp;
-    
+
     r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
     if (!r) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
     if (!r->nonpaged) {
         ERR("out of memory\n");
         ExFreePool(r);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     if (!no_tree) {
         t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
         if (!t) {
@@ -946,52 +1000,62 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
             ExFreePool(r);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
+
+        t->is_unique = TRUE;
+        t->uniqueness_determined = TRUE;
+        t->buf = NULL;
     }
-    
+
     ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
     if (!ri) {
         ERR("out of memory\n");
-        
-        if (!no_tree)
+
+        if (t)
             ExFreePool(t);
-        
+
         ExFreePool(r->nonpaged);
         ExFreePool(r);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     r->id = id;
     r->treeholder.address = 0;
     r->treeholder.generation = Vcb->superblock.generation;
-    r->treeholder.tree = no_tree ? NULL : t;
+    r->treeholder.tree = t;
     r->lastinode = 0;
-    r->path.Buffer = NULL;
+    r->dirty = FALSE;
+    r->received = FALSE;
+    r->reserved = NULL;
+    r->parent = 0;
+    r->send_ops = 0;
     RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
     r->root_item.num_references = 1;
     InitializeListHead(&r->fcbs);
-    
+
     RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
-    
+
     // We ask here for a traverse_ptr to the item we're inserting, so we can
     // copy some of the tree's variables
-    
-    if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) {
-        ERR("insert_tree_item failed\n");
+
+    Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(ri);
-        
-        if (!no_tree)
+
+        if (t)
             ExFreePool(t);
-        
+
         ExFreePool(r->nonpaged);
         ExFreePool(r);
-        return STATUS_INTERNAL_ERROR;
+        return Status;
     }
-        
+
     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
-    
+
     InsertTailList(&Vcb->roots, &r->list_entry);
-    
+
     if (!no_tree) {
+        RtlZeroMemory(&t->header, sizeof(tree_header));
         t->header.fs_uuid = tp.tree->header.fs_uuid;
         t->header.address = 0;
         t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
@@ -1007,193 +1071,72 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
         t->parent = NULL;
         t->paritem = NULL;
         t->root = r;
-        
+
         InitializeListHead(&t->itemlist);
-    
+
         t->new_address = 0;
         t->has_new_address = FALSE;
-        t->flags = tp.tree->flags;
-        
+        t->updated_extents = FALSE;
+
         InsertTailList(&Vcb->trees, &t->list_entry);
-        
+        t->list_entry_hash.Flink = NULL;
+
         t->write = TRUE;
         Vcb->need_write = TRUE;
     }
-    
+
     *rootptr = r;
 
     return STATUS_SUCCESS;
 }
 
-// static void test_creating_root(device_extension* Vcb) {
-//     NTSTATUS Status;
-//     LIST_ENTRY rollback;
-//     UINT64 id;
-//     root* r;
-//     
-//     InitializeListHead(&rollback);
-//     
-//     if (Vcb->root_root->lastinode == 0)
-//         get_last_inode(Vcb, Vcb->root_root);
-//     
-//     id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
-//     Status = create_root(Vcb, id, &r, &rollback);
-//     
-//     if (!NT_SUCCESS(Status)) {
-//         ERR("create_root returned %08x\n", Status);
-//         do_rollback(Vcb, &rollback);
-//     } else {
-//         Vcb->root_root->lastinode = id;
-//         clear_rollback(&rollback);
-//     }
-// }
-
-// static void test_alloc_chunk(device_extension* Vcb) {
-//     LIST_ENTRY rollback;
-//     chunk* c;
-//     
-//     InitializeListHead(&rollback);
-//     
-//     c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
-//     if (!c) {
-//         ERR("alloc_chunk failed\n");
-//         do_rollback(Vcb, &rollback);
-//     } else {
-//         clear_rollback(&rollback);
-//     }
-// }
-
-// static void test_space_list(device_extension* Vcb) {
-//     chunk* c;
-//     int i, j;
-//     LIST_ENTRY* le;
-//     
-//     typedef struct {
-//         UINT64 address;
-//         UINT64 length;
-//         BOOL add;
-//     } space_test;
-//     
-//     static const space_test entries[] = {
-//         { 0x1000, 0x1000 },
-//         { 0x3000, 0x2000 },
-//         { 0x6000, 0x1000 },
-//         { 0, 0 }
-//     };
-//     
-//     static const space_test tests[] = {
-//         { 0x0, 0x800, TRUE }, 
-//         { 0x1800, 0x400, TRUE }, 
-//         { 0x800, 0x2000, TRUE }, 
-//         { 0x1000, 0x2000, TRUE }, 
-//         { 0x2000, 0x3800, TRUE }, 
-//         { 0x800, 0x1000, TRUE }, 
-//         { 0x1800, 0x1000, TRUE }, 
-//         { 0x5000, 0x800, TRUE }, 
-//         { 0x5000, 0x1000, TRUE }, 
-//         { 0x7000, 0x1000, TRUE }, 
-//         { 0x8000, 0x1000, TRUE },
-//         { 0x800, 0x800, TRUE }, 
-//         { 0x0, 0x3800, TRUE }, 
-//         { 0x1000, 0x2800, TRUE },
-//         { 0x1000, 0x1000, FALSE },
-//         { 0x800, 0x2000, FALSE },
-//         { 0x0, 0x3800, FALSE },
-//         { 0x2800, 0x1000, FALSE },
-//         { 0x1800, 0x2000, FALSE },
-//         { 0x3800, 0x1000, FALSE },
-//         { 0, 0, FALSE }
-//     };
-//     
-//     c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
-//     
-//     i = 0;
-//     while (tests[i].length > 0) {
-//         InitializeListHead(&c->space);
-//         InitializeListHead(&c->space_size);
-//         ERR("test %u\n", i);
-//         
-//         j = 0;
-//         while (entries[j].length > 0) {
-//             space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-//             s->address = entries[j].address;
-//             s->size = entries[j].length;
-//             InsertTailList(&c->space, &s->list_entry);
-//             
-//             order_space_entry(s, &c->space_size);
-//             
-//             j++;
-//         }
-//         
-//         if (tests[i].add)
-//             space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
-//         else
-//             space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
-//         
-//         le = c->space.Flink;
-//         while (le != &c->space) {
-//             space* s = CONTAINING_RECORD(le, space, list_entry);
-//             
-//             ERR("(%llx,%llx)\n", s->address, s->size);
-//             
-//             le = le->Flink;
-//         }
-//         
-//         ERR("--\n");
-//         
-//         le = c->space_size.Flink;
-//         while (le != &c->space_size) {
-//             space* s = CONTAINING_RECORD(le, space, list_entry_size);
-//             
-//             ERR("(%llx,%llx)\n", s->address, s->size);
-//             
-//             le = le->Flink;
-//         }
-//         
-//         i++;
-//     }
-//     
-//     int3;
-// }
-
-static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
+static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMATION* ffli) {
     ULONG utf8len;
     NTSTATUS Status;
-    
+    ULONG vollen, i;
+
     TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
-    
-    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto end;
-    
-    if (utf8len > MAX_LABEL_SIZE) {
-        Status = STATUS_INVALID_VOLUME_LABEL;
-        goto end;
+
+    vollen = ffli->VolumeLabelLength;
+
+    for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
+        if (ffli->VolumeLabel[i] == 0) {
+            vollen = i * sizeof(WCHAR);
+            break;
+        } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
     }
-    
-    // FIXME - check for '/' and '\\' and reject
-    
-//     utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
-    
-    Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto release;
-    
+
+    if (vollen == 0) {
+        utf8len = 0;
+    } else {
+        Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto end;
+
+        if (utf8len > MAX_LABEL_SIZE) {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
+    }
+
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-    
-    if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
-        RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
-    
-//     test_tree_deletion(Vcb); // TESTING
-//     test_tree_splitting(Vcb);
-//     test_dropping_tree(Vcb);
-//     test_creating_root(Vcb);
-//     test_alloc_chunk(Vcb);
-//     test_space_list(Vcb);
-    
+
+    if (utf8len > 0) {
+        Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto release;
+    } else
+        Status = STATUS_SUCCESS;
+
+    if (utf8len < MAX_LABEL_SIZE)
+        RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
+
     Vcb->need_write = TRUE;
-    
-release:  
+
+release:
     ExReleaseResourceLite(&Vcb->tree_lock);
 
 end:
@@ -1202,35 +1145,40 @@ end:
     return Status;
 }
 
-static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     device_extension* Vcb = DeviceObject->DeviceExtension;
     NTSTATUS Status;
     BOOL top_level;
 
-    TRACE("set volume information\n");
-    
     FsRtlEnterFileSystem();
 
+    TRACE("set volume information\n");
+
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_set_volume_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
-    
+
     Status = STATUS_NOT_IMPLEMENTED;
-    
+
     if (Vcb->readonly) {
         Status = STATUS_MEDIA_WRITE_PROTECTED;
         goto end;
     }
-    
+
     if (Vcb->removing || Vcb->locked) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
-    
+
     switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
         case FileFsControlInformation:
             FIXME("STUB: FileFsControlInformation\n");
@@ -1238,7 +1186,7 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec
 
         case FileFsLabelInformation:
             TRACE("FileFsLabelInformation\n");
-    
+
             Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
             break;
 
@@ -1250,565 +1198,367 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec
             WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
             break;
     }
-    
+
 end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
 
+    TRACE("returning %08x\n", Status);
+
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
-    
-exit:
-    if (top_level) 
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
-    KEY searchkey;
-    traverse_ptr tp;
+static WCHAR* file_desc_fcb(_In_ fcb* fcb) {
+    char s[60];
     NTSTATUS Status;
-    
-    searchkey.obj_id = parinode;
-    searchkey.obj_type = TYPE_DIR_ITEM;
-    searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!keycmp(&searchkey, &tp.item->key)) {
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            WARN("(%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));
-        } else {
-            DIR_ITEM* di;
-            LONG len;
-            
-            di = (DIR_ITEM*)tp.item->data;
-            len = tp.item->size;
-            
-            do {
-                if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
-                    ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    
-                    if (newlen == 0) {
-                        TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    } else {
-                        UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
-                        
-                        if (!newdi) {
-                            ERR("out of memory\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
-                        if ((UINT8*)di > tp.item->data) {
-                            RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
-                            dioff = newdi + ((UINT8*)di - tp.item->data);
-                        } else {
-                            dioff = newdi;
-                        }
-                        
-                        if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
-                            RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
-                        
-                        insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
-                    }
-                    
-                    break;
-                }
-                
-                len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
-                di = (DIR_ITEM*)&di->name[di->n + di->m];
-            } while (len > 0);
-        }
-    } else {
-        WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
-    }
-    
-    return STATUS_SUCCESS;
-}
+    UNICODE_STRING us;
+    ANSI_STRING as;
 
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
-    KEY searchkey;
-    traverse_ptr tp;
-    BOOL changed = FALSE;
-    NTSTATUS Status;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_REF;
-    searchkey.offset = parinode;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!keycmp(&searchkey, &tp.item->key)) {
-        if (tp.item->size < sizeof(INODE_REF)) {
-            WARN("(%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(INODE_REF));
-        } else {
-            INODE_REF* ir;
-            ULONG len;
-            
-            ir = (INODE_REF*)tp.item->data;
-            len = tp.item->size;
-            
-            do {
-                ULONG itemlen;
-                
-                if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    break;
-                }
-                
-                itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
-                
-                if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
-                    ULONG newlen = tp.item->size - itemlen;
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    changed = TRUE;
-                    
-                    if (newlen == 0) {
-                        TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    } else {
-                        UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
-                        
-                        if (!newir) {
-                            ERR("out of memory\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
-                        if ((UINT8*)ir > tp.item->data) {
-                            RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
-                            iroff = newir + ((UINT8*)ir - tp.item->data);
-                        } else {
-                            iroff = newir;
-                        }
-                        
-                        if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
-                            RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
-                        
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
-                    }
-                    
-                    break;
-                }
-                
-                if (len > itemlen) {
-                    len -= itemlen;
-                    ir = (INODE_REF*)&ir->name[ir->n];
-                } else
-                    break;
-            } while (len > 0);
-            
-            if (!changed) {
-                WARN("found INODE_REF entry, but couldn't find filename\n");
-            }
-        }
-    } else {
-        WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
-    }
-    
-    if (changed)
-        return STATUS_SUCCESS;
-    
-    if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
-        return STATUS_INTERNAL_ERROR;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_EXTREF;
-    searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!keycmp(&searchkey, &tp.item->key)) {
-        if (tp.item->size < sizeof(INODE_EXTREF)) {
-            WARN("(%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(INODE_EXTREF));
-        } else {
-            INODE_EXTREF* ier;
-            ULONG len;
-            
-            ier = (INODE_EXTREF*)tp.item->data;
-            len = tp.item->size;
-            
-            do {
-                ULONG itemlen;
-                
-                if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    break;
-                }
-                
-                itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
-                
-                if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
-                    ULONG newlen = tp.item->size - itemlen;
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    changed = TRUE;
-                    
-                    if (newlen == 0) {
-                        TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    } else {
-                        UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
-                        
-                        if (!newier) {
-                            ERR("out of memory\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
-                        if ((UINT8*)ier > tp.item->data) {
-                            RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
-                            ieroff = newier + ((UINT8*)ier - tp.item->data);
-                        } else {
-                            ieroff = newier;
-                        }
-                        
-                        if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
-                            RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
-                        
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
-                    }
-                    
-                    break;
-                }
-                
-                if (len > itemlen) {
-                    len -= itemlen;
-                    ier = (INODE_EXTREF*)&ier->name[ier->n];
-                } else
-                    break;
-            } while (len > 0);
-        }
-    } else {
-        WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
-    }
-    
-    return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
-}
-
-static WCHAR* file_desc_fcb(fcb* fcb) {
-    char s[60];
-    UNICODE_STRING us;
-    ANSI_STRING as;
-    
     if (fcb->debug_desc)
         return fcb->debug_desc;
-    
+
     if (fcb == fcb->Vcb->volume_fcb)
         return L"volume FCB";
-    
+
     fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
     if (!fcb->debug_desc)
         return L"(memory error)";
-    
+
     // I know this is pretty hackish...
     // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
     // without the CRT, which breaks drivers.
-    
+
     sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
-    
+
     as.Buffer = s;
-    as.Length = as.MaximumLength = strlen(s);
-    
+    as.Length = as.MaximumLength = (USHORT)strlen(s);
+
     us.Buffer = fcb->debug_desc;
     us.MaximumLength = 60 * sizeof(WCHAR);
     us.Length = 0;
-    
-    RtlAnsiStringToUnicodeString(&us, &as, FALSE);
-    
+
+    Status = RtlAnsiStringToUnicodeString(&us, &as, FALSE);
+    if (!NT_SUCCESS(Status))
+        return L"(RtlAnsiStringToUnicodeString error)";
+
     us.Buffer[us.Length / sizeof(WCHAR)] = 0;
-    
+
     return fcb->debug_desc;
 }
 
-WCHAR* file_desc_fileref(file_ref* fileref) {
+WCHAR* file_desc_fileref(_In_ file_ref* fileref) {
     NTSTATUS Status;
     UNICODE_STRING fn;
-    
+    ULONG reqlen;
+
     if (fileref->debug_desc)
         return fileref->debug_desc;
-    
-    Status = fileref_get_filename(fileref, &fn, NULL);
-    if (!NT_SUCCESS(Status)) {
+
+    fn.Length = fn.MaximumLength = 0;
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (Status != STATUS_BUFFER_OVERFLOW)
         return L"ERROR";
-    }
-    
-    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG);
-    if (!fileref->debug_desc) {
-        ExFreePool(fn.Buffer);
+
+    if (reqlen > 0xffff - sizeof(WCHAR))
+        return L"(too long)";
+
+    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
+    if (!fileref->debug_desc)
         return L"(memory error)";
+
+    fn.Buffer = fileref->debug_desc;
+    fn.Length = 0;
+    fn.MaximumLength = (USHORT)(reqlen + sizeof(WCHAR));
+
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (!NT_SUCCESS(Status)) {
+        ExFreePool(fileref->debug_desc);
+        fileref->debug_desc = NULL;
+        return L"ERROR";
     }
-    
-    RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length);
+
     fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
-    
-    ExFreePool(fn.Buffer);
-    
+
     return fileref->debug_desc;
 }
 
-WCHAR* file_desc(PFILE_OBJECT FileObject) {
+_Ret_z_
+WCHAR* file_desc(_In_ PFILE_OBJECT FileObject) {
     fcb* fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
     file_ref* fileref = ccb ? ccb->fileref : NULL;
-    
+
     if (fileref)
         return file_desc_fileref(fileref);
     else
         return file_desc_fcb(fcb);
 }
 
-void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
+void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
     UNICODE_STRING fn;
     NTSTATUS Status;
+    ULONG reqlen;
     USHORT name_offset;
     fcb* fcb = fileref->fcb;
-    
-    Status = fileref_get_filename(fileref, &fn, &name_offset);
+
+    fn.Length = fn.MaximumLength = 0;
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (Status != STATUS_BUFFER_OVERFLOW) {
+        ERR("fileref_get_filename returned %08x\n", Status);
+        return;
+    }
+
+    if (reqlen > 0xffff) {
+        WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
+        return;
+    }
+
+    fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+    if (!fn.Buffer) {
+        ERR("out of memory\n");
+        return;
+    }
+
+    fn.MaximumLength = (USHORT)reqlen;
+    fn.Length = 0;
+
+    Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
     if (!NT_SUCCESS(Status)) {
         ERR("fileref_get_filename returned %08x\n", Status);
+        ExFreePool(fn.Buffer);
         return;
     }
-    
+
     FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
-                                  NULL, NULL, filter_match, action, NULL, NULL);
+                                  (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
     ExFreePool(fn.Buffer);
 }
 
-void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) {
+void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
     fcb* fcb = fileref->fcb;
     LIST_ENTRY* le;
     NTSTATUS Status;
-    
+
     // no point looking for hardlinks if st_nlink == 1
     if (fileref->fcb->inode_item.st_nlink == 1) {
-        send_notification_fileref(fileref, filter_match, action);
+        send_notification_fileref(fileref, filter_match, action, stream);
         return;
     }
-    
+
     ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
-    
+
     le = fcb->hardlinks.Flink;
     while (le != &fcb->hardlinks) {
         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
         file_ref* parfr;
-        
-        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
-        
-        if (!NT_SUCCESS(Status)) {
+
+        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
+
+        if (!NT_SUCCESS(Status))
             ERR("open_fileref_by_inode returned %08x\n", Status);
-        } else if (!parfr->deleted) {
-            LIST_ENTRY* le2;
-            BOOL found = FALSE, deleted = FALSE;
-            UNICODE_STRING* fn;
-            
-            le2 = parfr->children.Flink;
-            while (le2 != &parfr->children) {
-                file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
-                
-                if (fr2->index == hl->index) {
-                    found = TRUE;
-                    deleted = fr2->deleted;
-                    
-                    if (!deleted)
-                        fn = &fr2->filepart;
-                    
-                    break;
-                }
-                
-                le2 = le2->Flink;
+        else if (!parfr->deleted) {
+            UNICODE_STRING fn;
+            ULONG pathlen;
+
+            fn.Length = fn.MaximumLength = 0;
+            Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
+            if (Status != STATUS_BUFFER_OVERFLOW) {
+                ERR("fileref_get_filename returned %08x\n", Status);
+                free_fileref(fcb->Vcb, parfr);
+                break;
             }
-            
-            if (!found)
-                fn = &hl->name;
-            
-            if (!deleted) {
-                UNICODE_STRING path;
-                
-                Status = fileref_get_filename(parfr, &path, NULL);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("fileref_get_filename returned %08x\n", Status);
-                } else {
-                    UNICODE_STRING fn2;
-                    ULONG name_offset;
-                    
-                    name_offset = path.Length;
-                    if (parfr != fileref->fcb->Vcb->root_fileref) name_offset += sizeof(WCHAR);
-                    
-                    fn2.Length = fn2.MaximumLength = fn->Length + name_offset;
-                    fn2.Buffer = ExAllocatePoolWithTag(PagedPool, fn2.MaximumLength, ALLOC_TAG);
-                    
-                    RtlCopyMemory(fn2.Buffer, path.Buffer, path.Length);
-                    if (parfr != fileref->fcb->Vcb->root_fileref) fn2.Buffer[path.Length / sizeof(WCHAR)] = '\\';
-                    RtlCopyMemory(&fn2.Buffer[name_offset / sizeof(WCHAR)], fn->Buffer, fn->Length);
-                    
-                    TRACE("%.*S\n", fn2.Length / sizeof(WCHAR), fn2.Buffer);
-                    
-                    FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn2, name_offset,
-                                                  NULL, NULL, filter_match, action, NULL, NULL);
-                    
-                    ExFreePool(fn2.Buffer);
-                    ExFreePool(path.Buffer);
-                }
+
+            if (parfr != fcb->Vcb->root_fileref)
+                pathlen += sizeof(WCHAR);
+
+            if (pathlen + hl->name.Length > 0xffff) {
+                WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
+                free_fileref(fcb->Vcb, parfr);
+                break;
+            }
+
+            fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
+            fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
+            if (!fn.Buffer) {
+                ERR("out of memory\n");
+                free_fileref(fcb->Vcb, parfr);
+                break;
+            }
+
+            Status = fileref_get_filename(parfr, &fn, NULL, NULL);
+            if (!NT_SUCCESS(Status)) {
+                ERR("fileref_get_filename returned %08x\n", Status);
+                free_fileref(fcb->Vcb, parfr);
+                ExFreePool(fn.Buffer);
+                break;
+            }
+
+            if (parfr != fcb->Vcb->root_fileref) {
+                fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
+                fn.Length += sizeof(WCHAR);
             }
-            
-            free_fileref(parfr);
+
+            RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
+            fn.Length += hl->name.Length;
+
+            FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
+                                          (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
+
+            ExFreePool(fn.Buffer);
+
+            free_fileref(fcb->Vcb, parfr);
         }
-        
+
         le = le->Flink;
     }
-    
+
     ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
 }
 
-void mark_fcb_dirty(fcb* fcb) {
+void mark_fcb_dirty(_In_ fcb* fcb) {
     if (!fcb->dirty) {
 #ifdef DEBUG_FCB_REFCOUNTS
         LONG rc;
 #endif
-        dirty_fcb* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fcb), ALLOC_TAG);
-        
-        if (!dirt) {
-            ExFreePool("out of memory\n");
-            return;
-        }
-        
         fcb->dirty = TRUE;
-        
+
 #ifdef DEBUG_FCB_REFCOUNTS
         rc = InterlockedIncrement(&fcb->refcount);
         WARN("fcb %p: refcount now %i\n", fcb, rc);
 #else
         InterlockedIncrement(&fcb->refcount);
 #endif
-        
-        dirt->fcb = fcb;
-        
-        ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock);
+
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, TRUE);
+        InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
+        ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
     }
-    
+
     fcb->Vcb->need_write = TRUE;
 }
 
-void mark_fileref_dirty(file_ref* fileref) {
+void mark_fileref_dirty(_In_ file_ref* fileref) {
     if (!fileref->dirty) {
-        dirty_fileref* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fileref), ALLOC_TAG);
-        
-        if (!dirt) {
-            ExFreePool("out of memory\n");
-            return;
-        }
-        
         fileref->dirty = TRUE;
         increase_fileref_refcount(fileref);
-        
-        dirt->fileref = fileref;
-        
-        ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock);
+
+        ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, TRUE);
+        InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
+        ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
     }
-    
+
     fileref->fcb->Vcb->need_write = TRUE;
 }
 
-void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
+#ifdef DEBUG_FCB_REFCOUNTS
+void _free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb, _In_ const char* func) {
+#else
+void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb) {
+#endif
     LONG rc;
 
     rc = InterlockedDecrement(&fcb->refcount);
-    
+
 #ifdef DEBUG_FCB_REFCOUNTS
-//     WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
 #ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #else
-    _debug_message(func, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #endif
 #endif
-    
+
     if (rc > 0)
         return;
-    
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
-    
+
     if (fcb->list_entry.Flink)
         RemoveEntryList(&fcb->list_entry);
-    
+
     if (fcb->list_entry_all.Flink)
         RemoveEntryList(&fcb->list_entry_all);
-    
-    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
-   
+
     ExDeleteResourceLite(&fcb->nonpaged->resource);
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
-    ExDeleteResourceLite(&fcb->nonpaged->index_lock);
-    ExFreePool(fcb->nonpaged);
-    
+    ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
+
+    ExFreeToNPagedLookasideList(&Vcb->fcb_np_lookaside, fcb->nonpaged);
+
     if (fcb->sd)
         ExFreePool(fcb->sd);
-    
+
     if (fcb->adsxattr.Buffer)
         ExFreePool(fcb->adsxattr.Buffer);
-    
+
     if (fcb->reparse_xattr.Buffer)
         ExFreePool(fcb->reparse_xattr.Buffer);
-    
+
+    if (fcb->ea_xattr.Buffer)
+        ExFreePool(fcb->ea_xattr.Buffer);
+
     if (fcb->adsdata.Buffer)
         ExFreePool(fcb->adsdata.Buffer);
-    
+
     if (fcb->debug_desc)
         ExFreePool(fcb->debug_desc);
-    
+
     while (!IsListEmpty(&fcb->extents)) {
         LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        ExFreePool(ext->data);
+
+        if (ext->csum)
+            ExFreePool(ext->csum);
+
         ExFreePool(ext);
     }
-    
-    while (!IsListEmpty(&fcb->index_list)) {
-        LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
-        index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
 
-        if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-        if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
-        ExFreePool(ie);
-    }
-    
     while (!IsListEmpty(&fcb->hardlinks)) {
         LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
-        
+
         if (hl->name.Buffer)
             ExFreePool(hl->name.Buffer);
-        
+
         if (hl->utf8.Buffer)
             ExFreePool(hl->utf8.Buffer);
 
         ExFreePool(hl);
     }
-    
+
+    while (!IsListEmpty(&fcb->xattrs)) {
+        xattr* xa = CONTAINING_RECORD(RemoveHeadList(&fcb->xattrs), xattr, list_entry);
+
+        ExFreePool(xa);
+    }
+
+    while (!IsListEmpty(&fcb->dir_children_index)) {
+        LIST_ENTRY* le = RemoveHeadList(&fcb->dir_children_index);
+        dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc->name.Buffer);
+        ExFreePool(dc->name_uc.Buffer);
+        ExFreePool(dc);
+    }
+
+    if (fcb->hash_ptrs)
+        ExFreePool(fcb->hash_ptrs);
+
+    if (fcb->hash_ptrs_uc)
+        ExFreePool(fcb->hash_ptrs_uc);
+
     FsRtlUninitializeFileLock(&fcb->lock);
-    
-    ExFreePool(fcb);
+
+    if (fcb->pool_type == NonPagedPool)
+        ExFreePool(fcb);
+    else
+        ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
+
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
     _debug_message(func, file, line, "freeing fcb %p\n", fcb);
@@ -1818,341 +1568,466 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
 #endif
 }
 
-void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
+void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ file_ref* fr) {
     LONG rc;
 
     rc = InterlockedDecrement(&fr->refcount);
-    
+
 #ifdef DEBUG_FCB_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "fileref %p: refcount now %i\n", fr, rc);
-#else
-    _debug_message(func, "fileref %p: refcount now %i\n", fr, rc);
+    ERR("fileref %p: refcount now %i\n", fr, rc);
 #endif
-#endif
-    
+
 #ifdef _DEBUG
     if (rc < 0) {
         ERR("fileref %p: refcount now %i\n", fr, rc);
         int3;
     }
 #endif
-    
+
     if (rc > 0)
         return;
-        
+
     if (fr->parent)
         ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
-    
+
     // FIXME - do we need a file_ref lock?
-    
+
     // FIXME - do delete if needed
-    
-    if (fr->filepart.Buffer)
-        ExFreePool(fr->filepart.Buffer);
-    
-    if (fr->filepart_uc.Buffer)
-        ExFreePool(fr->filepart_uc.Buffer);
-    
-    if (fr->utf8.Buffer)
-        ExFreePool(fr->utf8.Buffer);
-    
+
     if (fr->debug_desc)
         ExFreePool(fr->debug_desc);
-    
+
     ExDeleteResourceLite(&fr->nonpaged->children_lock);
-    
-    ExFreePool(fr->nonpaged);
-    
+    ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
+
+    ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
+
     // FIXME - throw error if children not empty
-    
+
     if (fr->fcb->fileref == fr)
         fr->fcb->fileref = NULL;
-    
+
+    if (fr->dc) {
+        if (fr->fcb->ads)
+            fr->dc->size = fr->fcb->adsdata.Length;
+
+        fr->dc->fileref = NULL;
+    }
+
     if (fr->list_entry.Flink)
         RemoveEntryList(&fr->list_entry);
-    
+
     if (fr->parent) {
         ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
-        free_fileref(fr->parent);
+        free_fileref(Vcb, fr->parent);
     }
-    
-    free_fcb(fr->fcb);
-    ExFreePool(fr);
+
+    free_fcb(Vcb, fr->fcb);
+
+    ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
 }
 
-static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
+static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
     fcb* fcb;
     ccb* ccb;
     file_ref* fileref = NULL;
-    
+    LONG open_files;
+    device_extension* Vcb;
+
+    UNUSED(Irp);
+
     TRACE("FileObject = %p\n", FileObject);
-    
+
     fcb = FileObject->FsContext;
     if (!fcb) {
         TRACE("FCB was NULL, returning success\n");
         return STATUS_SUCCESS;
     }
-    
+
+    open_files = InterlockedDecrement(&fcb->Vcb->open_files);
+
     ccb = FileObject->FsContext2;
-    
+
     TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
-    
+
     // FIXME - make sure notification gets sent if file is being deleted
-    
-    if (ccb) {    
+
+    if (ccb) {
         if (ccb->query_string.Buffer)
             RtlFreeUnicodeString(&ccb->query_string);
-        
+
         if (ccb->filename.Buffer)
             ExFreePool(ccb->filename.Buffer);
-        
+
         // FIXME - use refcounts for fileref
         fileref = ccb->fileref;
-        
+
+        if (fcb->Vcb->running_sends > 0) {
+            BOOL send_cancelled = FALSE;
+
+            ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
+
+            if (ccb->send) {
+                ccb->send->cancelling = TRUE;
+                send_cancelled = TRUE;
+                KeSetEvent(&ccb->send->cleared_event, 0, FALSE);
+            }
+
+            ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
+
+            if (send_cancelled) {
+                while (ccb->send) {
+                    ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
+                    ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
+                }
+            }
+        }
+
         ExFreePool(ccb);
     }
-    
+
     CcUninitializeCacheMap(FileObject, NULL, NULL);
-    
+
+    if (open_files == 0 && fcb->Vcb->removing) {
+        uninit(fcb->Vcb, FALSE);
+        return STATUS_SUCCESS;
+    }
+
+    if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
+        return STATUS_SUCCESS;
+
+    Vcb = fcb->Vcb;
+
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-    
+
     if (fileref)
-        free_fileref(fileref);
+        free_fileref(fcb->Vcb, fileref);
     else
-        free_fcb(fcb);
-    
+        free_fcb(Vcb, fcb);
+
     ExReleaseResourceLite(&Vcb->fcb_lock);
-    
+
     return STATUS_SUCCESS;
 }
 
-void STDCALL uninit(device_extension* Vcb, BOOL flush) {
-    space* s;
+void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
     UINT64 i;
-    LIST_ENTRY rollback;
     NTSTATUS Status;
     LIST_ENTRY* le;
     LARGE_INTEGER time;
-    
+
+    if (!Vcb->removing) {
+        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+        Vcb->removing = TRUE;
+        ExReleaseResourceLite(&Vcb->tree_lock);
+    }
+
     RemoveEntryList(&Vcb->list_entry);
-    
+
+    if (Vcb->balance.thread) {
+        Vcb->balance.paused = FALSE;
+        Vcb->balance.stopping = TRUE;
+        KeSetEvent(&Vcb->balance.event, 0, FALSE);
+        KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, FALSE, NULL);
+    }
+
+    if (Vcb->scrub.thread) {
+        Vcb->scrub.paused = FALSE;
+        Vcb->scrub.stopping = TRUE;
+        KeSetEvent(&Vcb->scrub.event, 0, FALSE);
+        KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, FALSE, NULL);
+    }
+
+    if (Vcb->running_sends != 0) {
+        BOOL send_cancelled = FALSE;
+
+        ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
+
+        le = Vcb->send_ops.Flink;
+        while (le != &Vcb->send_ops) {
+            send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
+
+            if (!send->cancelling) {
+                send->cancelling = TRUE;
+                send_cancelled = TRUE;
+                send->ccb = NULL;
+                KeSetEvent(&send->cleared_event, 0, FALSE);
+            }
+
+            le = le->Flink;
+        }
+
+        ExReleaseResourceLite(&Vcb->send_load_lock);
+
+        if (send_cancelled) {
+            while (Vcb->running_sends != 0) {
+                ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
+                ExReleaseResourceLite(&Vcb->send_load_lock);
+            }
+        }
+    }
+
     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
-    if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
         WARN("registry_mark_volume_unmounted returned %08x\n", Status);
-    
+
     if (flush) {
-        InitializeListHead(&rollback);
-        
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-        if (Vcb->need_write)
-            do_write(Vcb, &rollback);
-        
+        if (Vcb->need_write && !Vcb->readonly) {
+            Status = do_write(Vcb, NULL);
+            if (!NT_SUCCESS(Status))
+                ERR("do_write returned %08x\n", Status);
+        }
+
         free_trees(Vcb);
-        
-        clear_rollback(&rollback);
 
         ExReleaseResourceLite(&Vcb->tree_lock);
     }
-    
-    for (i = 0; i < Vcb->threads.num_threads; i++) {
-        Vcb->threads.threads[i].quit = TRUE;
-        KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
-        
-        KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
-        
-        ZwClose(Vcb->threads.threads[i].handle);
-    }
-    
-    ExFreePool(Vcb->threads.threads);
-    
-    Vcb->removing = TRUE;
-    
+
+    for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+        Vcb->calcthreads.threads[i].quit = TRUE;
+    }
+
+    KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
+
+    for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+        KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
+
+        ZwClose(Vcb->calcthreads.threads[i].handle);
+    }
+
+    ExDeleteResourceLite(&Vcb->calcthreads.lock);
+    ExFreePool(Vcb->calcthreads.threads);
+
     time.QuadPart = 0;
     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
-    
-    free_fcb(Vcb->volume_fcb);
-    
+
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    free_fcb(Vcb, Vcb->volume_fcb);
+    free_fcb(Vcb, Vcb->dummy_fcb);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
+
     if (Vcb->root_file)
         ObDereferenceObject(Vcb->root_file);
-    
+
     le = Vcb->chunks.Flink;
     while (le != &Vcb->chunks) {
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
-        
+
         if (c->cache) {
-            free_fcb(c->cache);
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fcb(Vcb, c->cache);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             c->cache = NULL;
         }
-        
+
         le = le->Flink;
     }
 
     while (!IsListEmpty(&Vcb->roots)) {
-        LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
-        root* r = CONTAINING_RECORD(le, root, list_entry);
+        root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
 
         ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
         ExFreePool(r->nonpaged);
         ExFreePool(r);
     }
-    
+
     while (!IsListEmpty(&Vcb->chunks)) {
-        chunk* c;
-        
-        le = RemoveHeadList(&Vcb->chunks);
-        c = CONTAINING_RECORD(le, chunk, list_entry);
-        
+        chunk* c = CONTAINING_RECORD(RemoveHeadList(&Vcb->chunks), chunk, list_entry);
+
         while (!IsListEmpty(&c->space)) {
             LIST_ENTRY* le2 = RemoveHeadList(&c->space);
-            s = CONTAINING_RECORD(le2, space, list_entry);
-            
+            space* s = CONTAINING_RECORD(le2, space, list_entry);
+
             ExFreePool(s);
         }
-        
+
         while (!IsListEmpty(&c->deleting)) {
             LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
-            s = CONTAINING_RECORD(le2, space, list_entry);
-            
+            space* s = CONTAINING_RECORD(le2, space, list_entry);
+
             ExFreePool(s);
         }
-        
+
         if (c->devices)
             ExFreePool(c->devices);
-        
-        if (c->cache)
-            free_fcb(c->cache);
-        
-        ExDeleteResourceLite(&c->nonpaged->lock);
-        ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
-        
-        ExFreePool(c->nonpaged);
+
+        if (c->cache) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fcb(Vcb, c->cache);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        }
+
+        ExDeleteResourceLite(&c->range_locks_lock);
+        ExDeleteResourceLite(&c->partial_stripes_lock);
+        ExDeleteResourceLite(&c->lock);
+        ExDeleteResourceLite(&c->changed_extents_lock);
+
         ExFreePool(c->chunk_item);
         ExFreePool(c);
     }
-    
+
     // FIXME - free any open fcbs?
-    
-    while (!IsListEmpty(&Vcb->sector_checksums)) {
-        LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums);
-        changed_sector* cs = (changed_sector*)le;
-        
-        ExFreePool(cs);
-    }
-    
-    for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        while (!IsListEmpty(&Vcb->devices[i].space)) {
-            LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space);
-            space* s = CONTAINING_RECORD(le, space, list_entry);
-            
+
+    while (!IsListEmpty(&Vcb->devices)) {
+        device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
+
+        while (!IsListEmpty(&dev->space)) {
+            LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
+            space* s = CONTAINING_RECORD(le2, space, list_entry);
+
             ExFreePool(s);
         }
+
+        ExFreePool(dev);
+    }
+
+    ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+    while (!IsListEmpty(&Vcb->scrub.errors)) {
+        scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
+
+        ExFreePool(err);
     }
-    
-    ExFreePool(Vcb->devices);
-    
+    ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+
     ExDeleteResourceLite(&Vcb->fcb_lock);
     ExDeleteResourceLite(&Vcb->load_lock);
     ExDeleteResourceLite(&Vcb->tree_lock);
-    ExDeleteResourceLite(&Vcb->checksum_lock);
     ExDeleteResourceLite(&Vcb->chunk_lock);
-    
+    ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+    ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+    ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
+    ExDeleteResourceLite(&Vcb->scrub.stats_lock);
+    ExDeleteResourceLite(&Vcb->send_load_lock);
+
+    ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
+    ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
+    ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+    ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+    ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+    ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
+    ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+    ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+    ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
+
     ZwClose(Vcb->flush_thread_handle);
 }
 
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
     LARGE_INTEGER newlength, time;
     BTRFS_TIME now;
     NTSTATUS Status;
+    ULONG utf8len = 0;
 
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
 
     ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
-    
+
     if (fileref->deleted) {
         ExReleaseResourceLite(fileref->fcb->Header.Resource);
         return STATUS_SUCCESS;
     }
-    
+
+    if (fileref->fcb->subvol->send_ops > 0) {
+        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+        return STATUS_ACCESS_DENIED;
+    }
+
     fileref->deleted = TRUE;
     mark_fileref_dirty(fileref);
-    
+
     // delete INODE_ITEM (0x1)
 
     TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
-    
+
     if (!fileref->fcb->ads) {
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
             LIST_ENTRY* le;
-            
+
             mark_fcb_dirty(fileref->fcb);
-            
+
+            fileref->fcb->inode_item_changed = TRUE;
+
             if (fileref->fcb->inode_item.st_nlink > 1) {
                 fileref->fcb->inode_item.st_nlink--;
                 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
                 fileref->fcb->inode_item.sequence++;
                 fileref->fcb->inode_item.st_ctime = now;
             } else {
-                fileref->fcb->deleted = TRUE;
-            
                 // excise extents
-                
+
                 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
-                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
+                    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);
                     if (!NT_SUCCESS(Status)) {
                         ERR("excise_extents returned %08x\n", Status);
                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
                         return Status;
                     }
                 }
-                
+
                 fileref->fcb->Header.AllocationSize.QuadPart = 0;
                 fileref->fcb->Header.FileSize.QuadPart = 0;
                 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
-                
+
                 if (FileObject) {
                     CC_FILE_SIZES ccfs;
-                    
+
                     ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
                     ccfs.FileSize = fileref->fcb->Header.FileSize;
                     ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
-                    
-                    CcSetFileSizes(FileObject, &ccfs);
+
+                    Status = STATUS_SUCCESS;
+
+                    _SEH2_TRY {
+                        CcSetFileSizes(FileObject, &ccfs);
+                    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+                        Status = _SEH2_GetExceptionCode();
+                    } _SEH2_END;
+
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("CcSetFileSizes threw exception %08x\n", Status);
+                        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+                        return Status;
+                    }
                 }
+
+                fileref->fcb->deleted = TRUE;
             }
-                
-            le = fileref->fcb->hardlinks.Flink;
-            while (le != &fileref->fcb->hardlinks) {
-                hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
-                
-                if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) {
-                    RemoveEntryList(&hl->list_entry);
-                    
-                    if (hl->name.Buffer)
-                        ExFreePool(hl->name.Buffer);
-                    
-                    if (hl->utf8.Buffer)
-                        ExFreePool(hl->utf8.Buffer);
-                    
-                    ExFreePool(hl);
-                    break;
+
+            if (fileref->dc) {
+                le = fileref->fcb->hardlinks.Flink;
+                while (le != &fileref->fcb->hardlinks) {
+                    hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+
+                    if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
+                        RemoveEntryList(&hl->list_entry);
+
+                        if (hl->name.Buffer)
+                            ExFreePool(hl->name.Buffer);
+
+                        if (hl->utf8.Buffer)
+                            ExFreePool(hl->utf8.Buffer);
+
+                        ExFreePool(hl);
+                        break;
+                    }
+
+                    le = le->Flink;
                 }
-                
-                le = le->Flink;
             }
-        } else { // subvolume
+        } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
             if (fileref->fcb->subvol->root_item.num_references > 1) {
                 fileref->fcb->subvol->root_item.num_references--;
-                
+
                 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
             } else {
                 // FIXME - we need a lock here
-                
+
                 RemoveEntryList(&fileref->fcb->subvol->list_entry);
-                
+
                 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
             }
         }
@@ -2160,123 +2035,200 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY*
         fileref->fcb->deleted = TRUE;
         mark_fcb_dirty(fileref->fcb);
     }
-    
+
+    // remove dir_child from parent
+
+    if (fileref->dc) {
+        TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
+
+        ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
+        RemoveEntryList(&fileref->dc->list_entry_index);
+
+        if (!fileref->fcb->ads)
+            remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
+
+        ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
+
+        if (!fileref->oldutf8.Buffer)
+            fileref->oldutf8 = fileref->dc->utf8;
+        else
+            ExFreePool(fileref->dc->utf8.Buffer);
+
+        utf8len = fileref->dc->utf8.Length;
+
+        fileref->oldindex = fileref->dc->index;
+
+        ExFreePool(fileref->dc->name.Buffer);
+        ExFreePool(fileref->dc->name_uc.Buffer);
+        ExFreePool(fileref->dc);
+
+        fileref->dc = NULL;
+    }
+
     // update INODE_ITEM of parent
-    
-    TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer);
+
     ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
-    TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
-    fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2;
-    TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+
     fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
     fileref->parent->fcb->inode_item.sequence++;
     fileref->parent->fcb->inode_item.st_ctime = now;
-    fileref->parent->fcb->inode_item.st_mtime = now;
+
+    if (!fileref->fcb->ads) {
+        TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+        fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
+        TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+        fileref->parent->fcb->inode_item.st_mtime = now;
+    }
+
+    fileref->parent->fcb->inode_item_changed = TRUE;
     ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
 
+    if (!fileref->fcb->ads && fileref->parent->dc)
+        send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+
     mark_fcb_dirty(fileref->parent->fcb);
-    
-    send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
-    
+
     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
     fileref->fcb->subvol->root_item.ctime = now;
-    
-    if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject)
-        CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE);
-    
+
     newlength.QuadPart = 0;
-    
+
     if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
         TRACE("CcUninitializeCacheMap failed\n");
 
     ExReleaseResourceLite(fileref->fcb->Header.Resource);
-    
+
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_CLEANUP)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     PFILE_OBJECT FileObject = IrpSp->FileObject;
     device_extension* Vcb = DeviceObject->DeviceExtension;
-    fcb* fcb;
+    fcb* fcb = FileObject->FsContext;
     BOOL top_level;
 
-    TRACE("cleanup\n");
-    
     FsRtlEnterFileSystem();
 
+    TRACE("cleanup\n");
+
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit2;
-    }
-    
-    if (DeviceObject == devobj) {
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_cleanup(DeviceObject, Irp);
+        goto exit;
+    } else if (DeviceObject == master_devobj) {
         TRACE("closing file system\n");
         Status = STATUS_SUCCESS;
         goto exit;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
+    }
+
+    if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
+        TRACE("FileObject %p already cleaned up\n", FileObject);
+        Status = STATUS_SUCCESS;
+        goto exit;
+    }
+
+    if (!fcb) {
+        ERR("fcb was NULL\n");
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
     }
-    
+
+    // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
+    // messages belonging to other devices.
+
     if (FileObject && FileObject->FsContext) {
         LONG oc;
         ccb* ccb;
         file_ref* fileref;
-        
-        fcb = FileObject->FsContext;
+        BOOL locked = TRUE;
+
         ccb = FileObject->FsContext2;
         fileref = ccb ? ccb->fileref : NULL;
-        
+
         TRACE("cleanup called for FileObject %p\n", FileObject);
-        TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
-        
+        TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
+
+        ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+
+        ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+
         IoRemoveShareAccess(FileObject, &fcb->share_access);
-        
-        FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);    
-        
-        oc = InterlockedDecrement(&fcb->open_count);
+
+        if (ccb)
+            FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
+
+        if (fileref) {
+            oc = InterlockedDecrement(&fileref->open_count);
 #ifdef DEBUG_FCB_REFCOUNTS
-        ERR("fcb %p: open_count now %i\n", fcb, oc);
+            ERR("fileref %p: open_count now %i\n", fileref, oc);
 #endif
-        
+        }
+
         if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
             fileref->delete_on_close = TRUE;
-        
-        if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
+
+        if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
             fileref->delete_on_close = FALSE;
-        
-        if (Vcb->locked && Vcb->locked_fileobj == FileObject) {
+
+        if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
             TRACE("unlocking volume\n");
-            do_unlock_volume(Vcb);
+            do_unlock_volume(fcb->Vcb);
             FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
         }
-        
-        if (oc == 0) {
-            if (!Vcb->removing) {
-                LIST_ENTRY rollback;
-        
-                InitializeListHead(&rollback);
-            
+
+        if (ccb && ccb->reserving) {
+            fcb->subvol->reserved = NULL;
+            ccb->reserving = FALSE;
+            // FIXME - flush all of subvol's fcbs
+        }
+
+        if (fileref && oc == 0) {
+            if (!fcb->Vcb->removing) {
                 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
-                    send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
-                    
-                    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
-                    
-                    Status = delete_fileref(fileref, FileObject, &rollback);
+                    LIST_ENTRY rollback;
+
+                    InitializeListHead(&rollback);
+
+                    if (!fileref->fcb->ads || fileref->dc) {
+                        if (fileref->fcb->ads) {
+                            send_notification_fileref(fileref->parent, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+                                                      FILE_ACTION_REMOVED, &fileref->dc->name);
+                        } else
+                            send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
+                    }
+
+                    ExReleaseResourceLite(fcb->Header.Resource);
+                    locked = FALSE;
+
+                    // fcb_lock needs to be acquired before fcb->Header.Resource
+                    ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+
+                    Status = delete_fileref(fileref, FileObject, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("delete_fileref returned %08x\n", Status);
-                        do_rollback(Vcb, &rollback);
+                        do_rollback(fcb->Vcb, &rollback);
+                        ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
                         goto exit;
                     }
-                    
-                    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
+                    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+
+                    locked = FALSE;
+
                     clear_rollback(&rollback);
                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
                     IO_STATUS_BLOCK iosb;
                     CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
-                    
+
                     if (!NT_SUCCESS(iosb.Status)) {
                         ERR("CcFlushCache returned %08x\n", iosb.Status);
                     }
@@ -2287,268 +2239,312 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
                     }
 
                     CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
-                    
+
                     TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
                         FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
                 }
             }
-            
+
             if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
                 CcUninitializeCacheMap(FileObject, NULL, NULL);
         }
-        
+
+        if (locked)
+            ExReleaseResourceLite(fcb->Header.Resource);
+
+        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
         FileObject->Flags |= FO_CLEANUP_COMPLETE;
     }
-    
+
     Status = STATUS_SUCCESS;
 
 exit:
+    TRACE("returning %08x\n", Status);
+
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
-    
+
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
-    
-exit2:
-    if (top_level) 
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+_Success_(return)
+BOOL get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ UINT16 len, _Out_ ULONG* atts) {
+    if (len > 2 && val[0] == '0' && val[1] == 'x') {
+        int i;
+        ULONG dosnum = 0;
+
+        for (i = 2; i < len; i++) {
+            dosnum *= 0x10;
+
+            if (val[i] >= '0' && val[i] <= '9')
+                dosnum |= val[i] - '0';
+            else if (val[i] >= 'a' && val[i] <= 'f')
+                dosnum |= val[i] + 10 - 'a';
+            else if (val[i] >= 'A' && val[i] <= 'F')
+                dosnum |= val[i] + 10 - 'a';
+        }
+
+        TRACE("DOSATTRIB: %08x\n", dosnum);
+
+        *atts = dosnum;
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_ UINT64 inode,
+                          _In_ UINT8 type, _In_ BOOL dotfile, _In_ BOOL ignore_xa, _In_opt_ PIRP Irp) {
     ULONG att;
     char* eaval;
     UINT16 ealen;
-    
-    // ii can be NULL
-    
-    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
-        if (ealen > 2) {
-            if (eaval[0] == '0' && eaval[1] == 'x') {
-                int i;
-                ULONG dosnum = 0;
-
-                for (i = 2; i < ealen; i++) {
-                    dosnum *= 0x10;
-                    
-                    if (eaval[i] >= '0' && eaval[i] <= '9')
-                        dosnum |= eaval[i] - '0';
-                    else if (eaval[i] >= 'a' && eaval[i] <= 'f')
-                        dosnum |= eaval[i] + 10 - 'a';
-                    else if (eaval[i] >= 'A' && eaval[i] <= 'F')
-                        dosnum |= eaval[i] + 10 - 'a';
-                }
-                
-                TRACE("DOSATTRIB: %08x\n", dosnum);
-
-                ExFreePool(eaval);
-                
-                if (type == BTRFS_TYPE_DIRECTORY)
-                    dosnum |= FILE_ATTRIBUTE_DIRECTORY;
-                else if (type == BTRFS_TYPE_SYMLINK)
-                    dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
-                
-                return dosnum;
+
+    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
+        ULONG dosnum = 0;
+
+        if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
+            ExFreePool(eaval);
+
+            if (type == BTRFS_TYPE_DIRECTORY)
+                dosnum |= FILE_ATTRIBUTE_DIRECTORY;
+            else if (type == BTRFS_TYPE_SYMLINK)
+                dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+            if (type != BTRFS_TYPE_DIRECTORY)
+                dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+            if (inode == SUBVOL_ROOT_INODE) {
+                if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
+                    dosnum |= FILE_ATTRIBUTE_READONLY;
+                else
+                    dosnum &= ~FILE_ATTRIBUTE_READONLY;
             }
+
+            return dosnum;
         }
-        
+
         ExFreePool(eaval);
     }
-    
+
     switch (type) {
         case BTRFS_TYPE_DIRECTORY:
             att = FILE_ATTRIBUTE_DIRECTORY;
             break;
-            
+
         case BTRFS_TYPE_SYMLINK:
             att = FILE_ATTRIBUTE_REPARSE_POINT;
             break;
-           
+
         default:
             att = 0;
             break;
     }
-    
+
     if (dotfile) {
         att |= FILE_ATTRIBUTE_HIDDEN;
     }
-    
+
     att |= FILE_ATTRIBUTE_ARCHIVE;
-    
+
+    if (inode == SUBVOL_ROOT_INODE) {
+        if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
+            att |= FILE_ATTRIBUTE_READONLY;
+        else
+            att &= ~FILE_ATTRIBUTE_READONLY;
+    }
+
     // FIXME - get READONLY from ii->st_mode
     // FIXME - return SYSTEM for block/char devices?
-    
+
     if (att == 0)
         att = FILE_ATTRIBUTE_NORMAL;
-    
+
     return att;
 }
 
-NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
-    IO_STATUS_BLOCK* IoStatus;
+NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ UINT64 StartingOffset, _In_ ULONG Length,
+                        _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ BOOL override) {
+    IO_STATUS_BLOCK IoStatus;
     LARGE_INTEGER Offset;
     PIRP Irp;
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
-    read_context* context;
-    
+    read_context context;
+
     num_reads++;
-    
-    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
-    if (!context) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlZeroMemory(context, sizeof(read_context));
-    KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
-    
-    IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
-    if (!IoStatus) {
-        ERR("out of memory\n");
-        ExFreePool(context);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
 
-    Offset.QuadPart = StartingOffset;
+    RtlZeroMemory(&context, sizeof(read_context));
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+
+    Offset.QuadPart = (LONGLONG)StartingOffset;
 
-//     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
-    
+
     if (!Irp) {
         ERR("IoAllocateIrp failed\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto exit;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
+    Irp->Flags |= IRP_NOCACHE;
     IrpSp = IoGetNextIrpStackLocation(Irp);
     IrpSp->MajorFunction = IRP_MJ_READ;
-    
+
+    if (override)
+        IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
     if (DeviceObject->Flags & DO_BUFFERED_IO) {
-        FIXME("FIXME - buffered IO\n");
+        Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
+        if (!Irp->AssociatedIrp.SystemBuffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+        Irp->UserBuffer = Buffer;
     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
-//         TRACE("direct IO\n");
-        
         Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
         if (!Irp->MdlAddress) {
             ERR("IoAllocateMdl failed\n");
             Status = STATUS_INSUFFICIENT_RESOURCES;
-    //         IoFreeIrp(Irp);
             goto exit;
-//         } else {
-//             TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
         }
-        
-        MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
-    } else {
-//         TRACE("neither buffered nor direct IO\n");
+
+        Status = STATUS_SUCCESS;
+
+        _SEH2_TRY {
+            MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+        } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+            Status = _SEH2_GetExceptionCode();
+        } _SEH2_END;
+
+        if (!NT_SUCCESS(Status)) {
+            ERR("MmProbeAndLockPages threw exception %08x\n", Status);
+            IoFreeMdl(Irp->MdlAddress);
+            goto exit;
+        }
+    } else
         Irp->UserBuffer = Buffer;
-    }
 
     IrpSp->Parameters.Read.Length = Length;
     IrpSp->Parameters.Read.ByteOffset = Offset;
-    
-    Irp->UserIosb = IoStatus;
-//     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
-    
-    Irp->UserEvent = &context->Event;
-
-//     IoQueueThreadIrp(Irp);
-    
-    IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
-
-//     if (Override)
-//     {
-//         Stack = IoGetNextIrpStackLocation(Irp);
-//         Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
-//     }
-
-//     TRACE("Calling IO Driver... with irp %p\n", Irp);
+
+    Irp->UserIosb = &IoStatus;
+
+    Irp->UserEvent = &context.Event;
+
+    IoSetCompletionRoutine(Irp, read_completion, &context, TRUE, TRUE, TRUE);
+
     Status = IoCallDriver(DeviceObject, Irp);
 
-//     TRACE("Waiting for IO Operation for %p\n", Irp);
     if (Status == STATUS_PENDING) {
-//         TRACE("Operation pending\n");
-        KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-//         TRACE("Getting IO Status... for %p\n", Irp);
-        Status = context->iosb.Status;
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        Status = context.iosb.Status;
     }
-    
+
     if (DeviceObject->Flags & DO_DIRECT_IO) {
         MmUnlockPages(Irp->MdlAddress);
         IoFreeMdl(Irp->MdlAddress);
     }
-    
+
 exit:
     IoFreeIrp(Irp);
 
-    ExFreePool(IoStatus);
-    ExFreePool(context);
-    
     return Status;
 }
 
-static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) {
+static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ UINT64 length) {
     NTSTATUS Status;
     superblock* sb;
-    unsigned int i, to_read;
-    UINT32 crc32;
-    
-    to_read = sector_align(sizeof(superblock), device->SectorSize);
-    
+    ULONG i, to_read;
+    UINT8 valid_superblocks;
+
+    to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
+
     sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
     if (!sb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
+    if (superblock_addrs[0] + to_read > length) {
+        WARN("device was too short to have any superblock\n");
+        ExFreePool(sb);
+        return STATUS_UNRECOGNIZED_VOLUME;
+    }
+
     i = 0;
-    
+    valid_superblocks = 0;
+
     while (superblock_addrs[i] > 0) {
-        if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
+        UINT32 crc32;
+
+        if (i > 0 && superblock_addrs[i] + to_read > length)
             break;
-        
-        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+
+        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
         if (!NT_SUCCESS(Status)) {
             ERR("Failed to read superblock %u: %08x\n", i, Status);
             ExFreePool(sb);
             return Status;
         }
-        
-        // FIXME - check checksum before accepting?
-        
-        TRACE("got superblock %u!\n", i);
-
-        if (i == 0 || sb->generation > Vcb->superblock.generation)
-            RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
-        
+
+        if (sb->magic != BTRFS_MAGIC) {
+            if (i == 0) {
+                TRACE("not a BTRFS volume\n");
+                ExFreePool(sb);
+                return STATUS_UNRECOGNIZED_VOLUME;
+            }
+        } else {
+            TRACE("got superblock %u!\n", i);
+
+            crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+            if (crc32 != *((UINT32*)sb->checksum))
+                WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+            else if (sb->sector_size == 0)
+                WARN("superblock sector size was 0\n");
+            else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
+                WARN("invalid node size %x\n", sb->node_size);
+            else if ((sb->node_size % sb->sector_size) != 0)
+                WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
+            else if (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation) {
+                RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
+                valid_superblocks++;
+            }
+        }
+
         i++;
     }
-    
+
     ExFreePool(sb);
-    
-    crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
-    crc32 = ~crc32;
-    TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
-    
-    if (crc32 != *((UINT32*)Vcb->superblock.checksum))
-        return STATUS_INTERNAL_ERROR; // FIXME - correct error?
-    
+
+    if (valid_superblocks == 0) {
+        ERR("could not find any valid superblocks\n");
+        return STATUS_INTERNAL_ERROR;
+    }
+
     TRACE("label is %s\n", Vcb->superblock.label);
-//     utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
-    
+
     return STATUS_SUCCESS;
 }
 
-NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
-                           PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
-{
+NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
+                   _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ BOOLEAN Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
     PIRP Irp;
     KEVENT Event;
     NTSTATUS Status;
-    PIO_STACK_LOCATION Stack;
+    PIO_STACK_LOCATION IrpSp;
     IO_STATUS_BLOCK IoStatus;
 
     KeInitializeEvent(&Event, NotificationEvent, FALSE);
@@ -2566,8 +2562,8 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID
     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
 
     if (Override) {
-        Stack = IoGetNextIrpStackLocation(Irp);
-        Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+        IrpSp = IoGetNextIrpStackLocation(Irp);
+        IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
     }
 
     Status = IoCallDriver(DeviceObject, Irp);
@@ -2576,24 +2572,31 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID
         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
         Status = IoStatus.Status;
     }
-    
+
     if (iosb)
         *iosb = IoStatus;
 
     return Status;
 }
 
-static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
+_Requires_exclusive_lock_held_(Vcb->tree_lock)
+static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ UINT64 id, _In_ UINT64 addr,
+                         _In_ UINT64 generation, _In_opt_ traverse_ptr* tp) {
     root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
     if (!r) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     r->id = id;
-    r->path.Buffer = NULL;
+    r->dirty = FALSE;
+    r->received = FALSE;
+    r->reserved = NULL;
     r->treeholder.address = addr;
     r->treeholder.tree = NULL;
+    r->treeholder.generation = generation;
+    r->parent = 0;
+    r->send_ops = 0;
     InitializeListHead(&r->fcbs);
 
     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
@@ -2602,118 +2605,222 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr,
         ExFreePool(r);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
-    
+
     r->lastinode = 0;
-    
+
     if (tp) {
         RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
         if (tp->item->size < sizeof(ROOT_ITEM))
             RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
+    } else
+        RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
+
+    if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
+        // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
+        get_last_inode(Vcb, r, NULL);
+
+        if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
+            r->lastinode = 0x100;
     }
-    
+
     InsertTailList(&Vcb->roots, &r->list_entry);
-    
+
     switch (r->id) {
         case BTRFS_ROOT_ROOT:
             Vcb->root_root = r;
             break;
-            
+
         case BTRFS_ROOT_EXTENT:
             Vcb->extent_root = r;
             break;
-            
+
         case BTRFS_ROOT_CHUNK:
             Vcb->chunk_root = r;
             break;
-            
+
         case BTRFS_ROOT_DEVTREE:
             Vcb->dev_root = r;
             break;
-            
+
         case BTRFS_ROOT_CHECKSUM:
             Vcb->checksum_root = r;
             break;
-            
+
         case BTRFS_ROOT_UUID:
             Vcb->uuid_root = r;
             break;
+
+        case BTRFS_ROOT_FREE_SPACE:
+            Vcb->space_root = r;
+            break;
+
+        case BTRFS_ROOT_DATA_RELOC:
+            Vcb->data_reloc_root = r;
+            break;
     }
-    
+
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
+static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock) _In_ device_extension* Vcb, _In_opt_ PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
     NTSTATUS Status;
-    
+
     searchkey.obj_id = 0;
     searchkey.obj_type = 0;
     searchkey.offset = 0;
-    
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_tree returned %08x\n", Status);
+        ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
-    
+
     do {
         TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        
+
         if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
-            
+
             if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
                 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));
             } else {
                 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
-                
-                Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
+
+                Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("add_root returned %08x\n", Status);
                     return Status;
                 }
             }
+        } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
+            root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
+
+            if (lastroot->id == tp.item->key.obj_id)
+                lastroot->parent = tp.item->key.offset;
         }
-    
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-        
+
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
         if (b)
             tp = next_tp;
     } while (b);
-    
+
+    if (!Vcb->readonly && !Vcb->data_reloc_root) {
+        root* reloc_root;
+        INODE_ITEM* ii;
+        UINT16 irlen;
+        INODE_REF* ir;
+        LARGE_INTEGER time;
+        BTRFS_TIME now;
+
+        WARN("data reloc root doesn't exist, creating it\n");
+
+        Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, FALSE, 0, Irp);
+
+        if (!NT_SUCCESS(Status)) {
+            ERR("create_root returned %08x\n", Status);
+            return Status;
+        }
+
+        reloc_root->root_item.inode.generation = 1;
+        reloc_root->root_item.inode.st_size = 3;
+        reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
+        reloc_root->root_item.inode.st_nlink = 1;
+        reloc_root->root_item.inode.st_mode = 040755;
+        reloc_root->root_item.inode.flags = 0xffffffff80000000;
+        reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
+        reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
+
+        ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+        if (!ii) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        KeQuerySystemTime(&time);
+        win_time_to_unix(time, &now);
+
+        RtlZeroMemory(ii, sizeof(INODE_ITEM));
+        ii->generation = Vcb->superblock.generation;
+        ii->st_blocks = Vcb->superblock.node_size;
+        ii->st_nlink = 1;
+        ii->st_mode = 040755;
+        ii->st_atime = now;
+        ii->st_ctime = now;
+        ii->st_mtime = now;
+
+        Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            ExFreePool(ii);
+            return Status;
+        }
+
+        irlen = (UINT16)offsetof(INODE_REF, name[0]) + 2;
+        ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
+        if (!ir) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        ir->index = 0;
+        ir->n = 2;
+        ir->name[0] = '.';
+        ir->name[1] = '.';
+
+        Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            ExFreePool(ir);
+            return Status;
+        }
+
+        Vcb->data_reloc_root = reloc_root;
+        Vcb->need_write = TRUE;
+    }
+
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
+static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ device* dev, _In_opt_ PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     BOOL b;
     UINT64 lastaddr;
     NTSTATUS Status;
-    
+
     InitializeListHead(&dev->space);
-    
+
+    searchkey.obj_id = 0;
+    searchkey.obj_type = TYPE_DEV_STATS;
+    searchkey.offset = dev->devitem.dev_id;
+
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
+    if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
+        RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(UINT64) * 5, tp.item->size));
+
     searchkey.obj_id = dev->devitem.dev_id;
     searchkey.obj_type = TYPE_DEV_EXTENT;
     searchkey.offset = 0;
-    
-    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_tree returned %08x\n", Status);
+        ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
-    
+
     lastaddr = 0;
-    
+
     do {
         if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
             if (tp.item->size >= sizeof(DEV_EXTENT)) {
                 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
-                
+
                 if (tp.item->key.offset > lastaddr) {
                     Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
                     if (!NT_SUCCESS(Status)) {
@@ -2727,16 +2834,16 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
                 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));
             }
         }
-    
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-        
+
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
         if (b) {
             tp = next_tp;
             if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
                 break;
         }
     } while (b);
-    
+
     if (lastaddr < dev->devitem.num_bytes) {
         Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
         if (!NT_SUCCESS(Status)) {
@@ -2744,793 +2851,1267 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
             return Status;
         }
     }
-    
+
+    // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
+
+    space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
+
     return STATUS_SUCCESS;
 }
 
-device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
-    UINT64 i;
-    
-    for (i = 0; i < Vcb->devices_loaded; i++) {
-        TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
-            Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7],
-            Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]);
-        
-        if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-            TRACE("returning device %llx\n", i);
-            return &Vcb->devices[i];
+static void add_device_to_list(_In_ device_extension* Vcb, _In_ device* dev) {
+    LIST_ENTRY* le;
+
+    le = Vcb->devices.Flink;
+
+    while (le != &Vcb->devices) {
+        device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+        if (dev2->devitem.dev_id > dev->devitem.dev_id) {
+            InsertHeadList(le->Blink, &dev->list_entry);
+            return;
         }
+
+        le = le->Flink;
     }
-    
-    if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
-        LIST_ENTRY* le = volumes.Flink;
-        
-        while (le != &volumes) {
-            volume* v = CONTAINING_RECORD(le, volume, list_entry);
-            
-            if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-                RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
-            ) {
-                NTSTATUS Status;
-                PFILE_OBJECT FileObject;
-                PDEVICE_OBJECT DeviceObject;
-                
-                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
+
+    InsertTailList(&Vcb->devices, &dev->list_entry);
+}
+
+_Ret_maybenull_
+device* find_device_from_uuid(_In_ device_extension* Vcb, _In_ BTRFS_UUID* uuid) {
+    volume_device_extension* vde;
+    pdo_device_extension* pdode;
+    LIST_ENTRY* le;
+
+    le = Vcb->devices.Flink;
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+        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,
+            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],
+            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]);
+
+        if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+            TRACE("returning device %llx\n", dev->devitem.dev_id);
+            return dev;
+        }
+
+        le = le->Flink;
+    }
+
+    vde = Vcb->vde;
+
+    if (!vde)
+        goto end;
+
+    pdode = vde->pdode;
+
+    ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+    if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+        le = pdode->children.Flink;
+
+        while (le != &pdode->children) {
+            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+
+            if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                device* dev;
+
+                dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+                if (!dev) {
+                    ExReleaseResourceLite(&pdode->child_lock);
+                    ERR("out of memory\n");
                     return NULL;
                 }
-                
-                DeviceObject = FileObject->DeviceObject;
-                
-                ObReferenceObject(DeviceObject);
-                ObDereferenceObject(FileObject);
-                
-                Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
-                Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid;
+
+                RtlZeroMemory(dev, sizeof(device));
+                dev->devobj = vc->devobj;
+                dev->devitem.device_uuid = *uuid;
+                dev->devitem.dev_id = vc->devid;
+                dev->devitem.num_bytes = vc->size;
+                dev->seeding = vc->seeding;
+                dev->readonly = dev->seeding;
+                dev->reloc = FALSE;
+                dev->removable = FALSE;
+                dev->disk_num = vc->disk_num;
+                dev->part_num = vc->part_num;
+                dev->num_trim_entries = 0;
+                InitializeListHead(&dev->trim_list);
+
+                add_device_to_list(Vcb, dev);
                 Vcb->devices_loaded++;
-                
-                return &Vcb->devices[Vcb->devices_loaded - 1];
+
+                ExReleaseResourceLite(&pdode->child_lock);
+
+                return dev;
             }
-            
+
             le = le->Flink;
         }
     }
-    
+
+    ExReleaseResourceLite(&pdode->child_lock);
+
+end:
     WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
          uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
          uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
-    
+
     return NULL;
 }
 
-static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
+static BOOL is_device_removable(_In_ PDEVICE_OBJECT devobj) {
     NTSTATUS Status;
     STORAGE_HOTPLUG_INFO shi;
-    
+
     Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
-    
+
     if (!NT_SUCCESS(Status)) {
         ERR("dev_ioctl returned %08x\n", Status);
         return FALSE;
     }
-    
+
     return shi.MediaRemovable != 0 ? TRUE : FALSE;
 }
 
-static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
+static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
     NTSTATUS Status;
     ULONG cc;
     IO_STATUS_BLOCK iosb;
-    
+
     Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
-    
+
     if (!NT_SUCCESS(Status)) {
         ERR("dev_ioctl returned %08x\n", Status);
         return 0;
     }
-    
+
     if (iosb.Information < sizeof(ULONG)) {
         ERR("iosb.Information was too short\n");
         return 0;
     }
-    
+
     return cc;
 }
 
-static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
+void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ BOOL get_nums) {
     NTSTATUS Status;
-    GET_LENGTH_INFORMATION gli;
-    
+    ULONG aptelen;
+    ATA_PASS_THROUGH_EX* apte;
+    STORAGE_PROPERTY_QUERY spq;
+    DEVICE_TRIM_DESCRIPTOR dtd;
+
     dev->removable = is_device_removable(dev->devobj);
     dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
-    
-    if (get_length) {
-        Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
-                        &gli, sizeof(gli), TRUE, NULL);
+
+    if (get_nums) {
+        STORAGE_DEVICE_NUMBER sdn;
+
+        Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+                           &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
+
         if (!NT_SUCCESS(Status)) {
-            ERR("error reading length information: %08x\n", Status);
+            WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
+            dev->disk_num = 0xffffffff;
+            dev->part_num = 0xffffffff;
+        } else {
+            dev->disk_num = sdn.DeviceNumber;
+            dev->part_num = sdn.PartitionNumber;
         }
-        
-        dev->length = gli.Length.QuadPart;
     }
+
+    dev->trim = FALSE;
+    dev->readonly = dev->seeding;
+    dev->reloc = FALSE;
+    dev->num_trim_entries = 0;
+    dev->stats_changed = FALSE;
+    InitializeListHead(&dev->trim_list);
+
+    if (!dev->readonly) {
+        Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
+                        NULL, 0, TRUE, NULL);
+        if (Status == STATUS_MEDIA_WRITE_PROTECTED)
+            dev->readonly = TRUE;
+    }
+
+    aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
+    apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
+    if (!apte) {
+        ERR("out of memory\n");
+        return;
+    }
+
+    RtlZeroMemory(apte, aptelen);
+
+    apte->Length = sizeof(ATA_PASS_THROUGH_EX);
+    apte->AtaFlags = ATA_FLAGS_DATA_IN;
+    apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
+    apte->TimeOutValue = 3;
+    apte->DataBufferOffset = apte->Length;
+    apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
+
+    Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
+                       apte, aptelen, TRUE, NULL);
+
+    if (!NT_SUCCESS(Status))
+        TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status);
+    else {
+        IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((UINT8*)apte + sizeof(ATA_PASS_THROUGH_EX));
+
+        if (idd->CommandSetSupport.FlushCache) {
+            dev->can_flush = TRUE;
+            TRACE("FLUSH CACHE supported\n");
+        } else
+            TRACE("FLUSH CACHE not supported\n");
+    }
+
+    ExFreePool(apte);
+
+    spq.PropertyId = StorageDeviceTrimProperty;
+    spq.QueryType = PropertyStandardQuery;
+    spq.AdditionalParameters[0] = 0;
+
+    Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
+                       &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), TRUE, NULL);
+
+    if (NT_SUCCESS(Status)) {
+        if (dtd.TrimEnabled) {
+            dev->trim = TRUE;
+            Vcb->trim = TRUE;
+            TRACE("TRIM supported\n");
+        } else
+            TRACE("TRIM not supported\n");
+    }
+
+    RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
 }
 
-static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
+static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
     chunk* c;
-    UINT64 i;
     NTSTATUS Status;
 
     searchkey.obj_id = 0;
     searchkey.obj_type = 0;
     searchkey.offset = 0;
-    
+
     Vcb->data_flags = 0;
-    
-    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+    Vcb->metadata_flags = 0;
+    Vcb->system_flags = 0;
+
+    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
-    
+
     do {
         TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        
+
         if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
             if (tp.item->size < sizeof(DEV_ITEM)) {
                 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));
             } else {
                 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
+                LIST_ENTRY* le;
                 BOOL done = FALSE;
-                
-                for (i = 0; i < Vcb->devices_loaded; i++) {
-                    if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-                        RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
-                        
-                        if (i > 0)
-                            init_device(Vcb, &Vcb->devices[i], TRUE);
-                        
+
+                le = Vcb->devices.Flink;
+                while (le != &Vcb->devices) {
+                    device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+                    if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                        RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+
+                        if (le != Vcb->devices.Flink)
+                            init_device(Vcb, dev, TRUE);
+
                         done = TRUE;
                         break;
                     }
+
+                    le = le->Flink;
                 }
-                
-                if (!done) {
-                    if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
-                        LIST_ENTRY* le = volumes.Flink;
-                        
-                        while (le != &volumes) {
-                            volume* v = CONTAINING_RECORD(le, volume, list_entry);
-            
-                            if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-                                RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
-                            ) {
-                                PFILE_OBJECT FileObject;
-                                PDEVICE_OBJECT DeviceObject;
-                                
-                                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
-                                if (!NT_SUCCESS(Status)) {
-                                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
-                                    return Status;
+
+                if (!done && Vcb->vde) {
+                    volume_device_extension* vde = Vcb->vde;
+                    pdo_device_extension* pdode = vde->pdode;
+
+                    ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+                    if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+                        le = pdode->children.Flink;
+
+                        while (le != &pdode->children) {
+                            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+
+                            if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                                device* dev;
+
+                                dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+                                if (!dev) {
+                                    ExReleaseResourceLite(&pdode->child_lock);
+                                    ERR("out of memory\n");
+                                    return STATUS_INSUFFICIENT_RESOURCES;
+                                }
+
+                                RtlZeroMemory(dev, sizeof(device));
+
+                                dev->devobj = vc->devobj;
+                                RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
+                                dev->seeding = vc->seeding;
+                                init_device(Vcb, dev, FALSE);
+
+                                if (dev->devitem.num_bytes > vc->size) {
+                                    WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp.item->key.offset,
+                                         dev->devitem.num_bytes, vc->size);
+
+                                    dev->devitem.num_bytes = vc->size;
                                 }
-                                
-                                DeviceObject = FileObject->DeviceObject;
-                                
-                                ObReferenceObject(DeviceObject);
-                                ObDereferenceObject(FileObject);
-                                
-                                Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
-                                RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
-                                init_device(Vcb, &Vcb->devices[i], FALSE);
-                                Vcb->devices[i].length = v->length;
+
+                                dev->disk_num = vc->disk_num;
+                                dev->part_num = vc->part_num;
+                                add_device_to_list(Vcb, dev);
                                 Vcb->devices_loaded++;
 
                                 done = TRUE;
                                 break;
                             }
-                            
+
                             le = le->Flink;
                         }
-                        
+
                         if (!done) {
-                            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,
-                            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],
-                            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]);
+                            if (!Vcb->options.allow_degraded) {
+                                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,
+                                    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],
+                                    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]);
+                            } else {
+                                device* dev;
+
+                                dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+                                if (!dev) {
+                                    ExReleaseResourceLite(&pdode->child_lock);
+                                    ERR("out of memory\n");
+                                    return STATUS_INSUFFICIENT_RESOURCES;
+                                }
+
+                                RtlZeroMemory(dev, sizeof(device));
+
+                                // Missing device, so we keep dev->devobj as NULL
+                                RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
+                                InitializeListHead(&dev->trim_list);
+
+                                add_device_to_list(Vcb, dev);
+                                Vcb->devices_loaded++;
+                            }
                         }
                     } else
                         ERR("unexpected device %llx found\n", tp.item->key.offset);
+
+                    ExReleaseResourceLite(&pdode->child_lock);
                 }
             }
         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
             if (tp.item->size < sizeof(CHUNK_ITEM)) {
                 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));
-            } else {            
-                c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
-                
+            } else {
+                c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
+
                 if (!c) {
                     ERR("out of memory\n");
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
-                
-                c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
-                
-                if (!c->nonpaged) {
-                    ERR("out of memory\n");
-                    ExFreePool(c);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
+
                 c->size = tp.item->size;
                 c->offset = tp.item->key.offset;
                 c->used = c->oldused = 0;
-                c->cache = NULL;
+                c->cache = c->old_cache = NULL;
                 c->created = FALSE;
-                
-                c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
-                
+                c->readonly = FALSE;
+                c->reloc = FALSE;
+                c->cache_loaded = FALSE;
+                c->changed = FALSE;
+                c->space_changed = FALSE;
+                c->balance_num = 0;
+
+                c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
+
                 if (!c->chunk_item) {
                     ERR("out of memory\n");
                     ExFreePool(c);
-                    ExFreePool(c->nonpaged);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
-            
+
                 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
-                
+
                 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
                     Vcb->data_flags = c->chunk_item->type;
-                
+
+                if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
+                    Vcb->metadata_flags = c->chunk_item->type;
+
+                if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
+                    Vcb->system_flags = c->chunk_item->type;
+
+                if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
+                    if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
+                        ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
+                        ExFreePool(c->chunk_item);
+                        ExFreePool(c);
+                        return STATUS_INTERNAL_ERROR;
+                    }
+                }
+
                 if (c->chunk_item->num_stripes > 0) {
                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-                    
-                    c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
-                    
+                    UINT16 i;
+
+                    c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+
                     if (!c->devices) {
                         ERR("out of memory\n");
-                        ExFreePool(c);
-                        ExFreePool(c->nonpaged);
                         ExFreePool(c->chunk_item);
+                        ExFreePool(c);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
-                    
+
                     for (i = 0; i < c->chunk_item->num_stripes; i++) {
                         c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
                         TRACE("device %llu = %p\n", i, c->devices[i]);
+
+                        if (!c->devices[i]) {
+                            ERR("missing device\n");
+                            ExFreePool(c->chunk_item);
+                            ExFreePool(c);
+                            return STATUS_INTERNAL_ERROR;
+                        }
+
+                        if (c->devices[i]->readonly)
+                            c->readonly = TRUE;
                     }
-                } else
-                    c->devices = NULL;
-                
-                ExInitializeResourceLite(&c->nonpaged->lock);
-                ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
-                
+                } else {
+                    ERR("chunk %llx: number of stripes is 0\n", c->offset);
+                    ExFreePool(c->chunk_item);
+                    ExFreePool(c);
+                    return STATUS_INTERNAL_ERROR;
+                }
+
+                ExInitializeResourceLite(&c->lock);
+                ExInitializeResourceLite(&c->changed_extents_lock);
+
                 InitializeListHead(&c->space);
                 InitializeListHead(&c->space_size);
                 InitializeListHead(&c->deleting);
                 InitializeListHead(&c->changed_extents);
 
+                InitializeListHead(&c->range_locks);
+                ExInitializeResourceLite(&c->range_locks_lock);
+                KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
+
+                InitializeListHead(&c->partial_stripes);
+                ExInitializeResourceLite(&c->partial_stripes_lock);
+
+                c->last_alloc_set = FALSE;
+
+                c->last_stripe = 0;
+
                 InsertTailList(&Vcb->chunks, &c->list_entry);
-                
-                c->list_entry_changed.Flink = NULL;
+
+                c->list_entry_balance.Flink = NULL;
             }
         }
-    
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-        
+
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
         if (b)
             tp = next_tp;
     } while (b);
-    
+
     Vcb->log_to_phys_loaded = TRUE;
-    
+
     if (Vcb->data_flags == 0)
         Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
-    
+
+    if (Vcb->metadata_flags == 0)
+        Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
+
+    if (Vcb->system_flags == 0)
+        Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
+
+    if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
+        Vcb->metadata_flags |= BLOCK_FLAG_DATA;
+        Vcb->data_flags = Vcb->metadata_flags;
+    }
+
     return STATUS_SUCCESS;
 }
 
-void protect_superblocks(device_extension* Vcb, chunk* c) {
+void protect_superblocks(_Inout_ chunk* c) {
     UINT16 i = 0, j;
     UINT64 off_start, off_end;
-    
+
     // The Linux driver also protects all the space before the first superblock.
-    // I realize this confuses physical and logical addresses, but this is what btrfs-progs does - 
+    // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
     // evidently Linux assumes the chunk at 0 is always SINGLE.
     if (c->offset < superblock_addrs[0])
-        space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
-    
+        space_list_subtract(c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
+
     while (superblock_addrs[i] != 0) {
         CHUNK_ITEM* ci = c->chunk_item;
         CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
-        
+
         if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
             for (j = 0; j < ci->num_stripes; j++) {
-                ULONG sub_stripes = max(ci->sub_stripes, 1);
-                
+                UINT16 sub_stripes = max(ci->sub_stripes, 1);
+
                 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
 #ifdef _DEBUG
                     UINT64 startoff;
                     UINT16 startoffstripe;
 #endif
-                    
+
                     TRACE("cut out superblock in chunk %llx\n", c->offset);
-                    
+
                     off_start = superblock_addrs[i] - cis[j].offset;
                     off_start -= off_start % ci->stripe_length;
                     off_start *= ci->num_stripes / sub_stripes;
                     off_start += (j / sub_stripes) * ci->stripe_length;
 
                     off_end = off_start + ci->stripe_length;
-                    
+
 #ifdef _DEBUG
                     get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
                     TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
                     TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
 #endif
-                    
-                    space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+
+                    space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+                }
+            }
+        } else if (ci->type & BLOCK_FLAG_RAID5) {
+            UINT64 stripe_size = ci->size / (ci->num_stripes - 1);
+
+            for (j = 0; j < ci->num_stripes; j++) {
+                if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+                    TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+                    off_start = superblock_addrs[i] - cis[j].offset;
+                    off_start -= off_start % ci->stripe_length;
+                    off_start *= ci->num_stripes - 1;
+
+                    off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+                    off_end *= ci->num_stripes - 1;
+
+                    TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
+
+                    space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+                }
+            }
+        } else if (ci->type & BLOCK_FLAG_RAID6) {
+            UINT64 stripe_size = ci->size / (ci->num_stripes - 2);
+
+            for (j = 0; j < ci->num_stripes; j++) {
+                if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+                    TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+                    off_start = superblock_addrs[i] - cis[j].offset;
+                    off_start -= off_start % ci->stripe_length;
+                    off_start *= ci->num_stripes - 2;
+
+                    off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+                    off_end *= ci->num_stripes - 2;
+
+                    TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
+
+                    space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
                 }
             }
         } else { // SINGLE, DUPLICATE, RAID1
             for (j = 0; j < ci->num_stripes; j++) {
                 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
                     TRACE("cut out superblock in chunk %llx\n", c->offset);
-                    
+
                     // The Linux driver protects the whole stripe in which the superblock lives
 
                     off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
                     off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
-                    
-                    space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+
+                    space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
                 }
             }
         }
-        
+
         i++;
     }
 }
 
-static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
+NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
     LIST_ENTRY* le = Vcb->chunks.Flink;
     chunk* c;
     KEY searchkey;
     traverse_ptr tp;
     BLOCK_GROUP_ITEM* bgi;
     NTSTATUS Status;
-    
-// c00000,c0,800000
-// block_group_item size=7f0000 chunktreeid=100 flags=1
-    
+
     searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
-    
+
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
-        
+
         searchkey.obj_id = c->offset;
         searchkey.offset = c->chunk_item->size;
-        
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
         }
-        
-        if (!keycmp(&searchkey, &tp.item->key)) {
+
+        if (!keycmp(searchkey, tp.item->key)) {
             if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
                 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
-                
+
                 c->used = c->oldused = bgi->used;
-                
+
                 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
             } else {
                 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
                     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));
             }
         }
-        
-//         if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
-//             cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-// 
-//             return (addr - c->offset) + cis->offset;
-//         }
-        
-        // FIXME - make sure we free occasionally after doing one of these, or we
-        // might use up a lot of memory with a big disk.
-        
-        Status = load_free_space_cache(Vcb, c);
-        if (!NT_SUCCESS(Status)) {
-            ERR("load_free_space_cache returned %08x\n", Status);
-            return Status;
-        }        
-        
-        protect_superblocks(Vcb, c);
 
         le = le->Flink;
     }
-    
+
+    Vcb->chunk_usage_found = TRUE;
+
     return STATUS_SUCCESS;
 }
 
-// static void STDCALL root_test(device_extension* Vcb) {
-//     root* r;
-//     KEY searchkey;
-//     traverse_ptr tp, next_tp;
-//     BOOL b;
-//     
-//     r = Vcb->roots;
-//     while (r) {
-//         if (r->id == 0x102)
-//             break;
-//         r = r->next;
-//     }
-//     
-//     if (!r) {
-//         ERR("Could not find root tree.\n");
-//         return;
-//     }
-//     
-//     searchkey.obj_id = 0x1b6;
-//     searchkey.obj_type = 0xb;
-//     searchkey.offset = 0;
-//     
-//     if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
-//         ERR("Could not find first item.\n");
-//         return;
-//     }
-//     
-//     b = TRUE;
-//     do {
-//         TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//         
-//         b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
-//         
-//         if (b) {
-//             free_traverse_ptr(&tp);
-//             tp = next_tp;
-//         }
-//     } while (b);
-//     
-//     free_traverse_ptr(&tp);
-// }
-
-static NTSTATUS load_sys_chunks(device_extension* Vcb) {
+static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
     KEY key;
     ULONG n = Vcb->superblock.n;
-    
+
     while (n > 0) {
         if (n > sizeof(KEY)) {
             RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
             n -= sizeof(KEY);
         } else
             return STATUS_SUCCESS;
-        
+
         TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
-        
+
         if (key.obj_type == TYPE_CHUNK_ITEM) {
             CHUNK_ITEM* ci;
-            ULONG cisize;
+            USHORT cisize;
             sys_chunk* sc;
-            
+
             if (n < sizeof(CHUNK_ITEM))
                 return STATUS_SUCCESS;
-            
+
             ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
             cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
-            
+
             if (n < cisize)
                 return STATUS_SUCCESS;
-            
+
             sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
-            
+
             if (!sc) {
                 ERR("out of memory\n");
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
-            
+
             sc->key = key;
             sc->size = cisize;
             sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
-            
+
             if (!sc->data) {
                 ERR("out of memory\n");
+                ExFreePool(sc);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
-            
+
             RtlCopyMemory(sc->data, ci, sc->size);
             InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
-            
+
             n -= cisize;
         } else {
             ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
             return STATUS_INTERNAL_ERROR;
         }
     }
-    
+
     return STATUS_SUCCESS;
 }
 
-static root* find_default_subvol(device_extension* Vcb) {
+_Ret_maybenull_
+static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
     LIST_ENTRY* le;
-    
+
     static char fn[] = "default";
     static UINT32 crc32 = 0x8dbfc2d2;
-    
+
+    if (Vcb->options.subvol_id != 0) {
+        le = Vcb->roots.Flink;
+        while (le != &Vcb->roots) {
+            root* r = CONTAINING_RECORD(le, root, list_entry);
+
+            if (r->id == Vcb->options.subvol_id)
+                return r;
+
+            le = le->Flink;
+        }
+    }
+
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
         NTSTATUS Status;
         KEY searchkey;
         traverse_ptr tp;
         DIR_ITEM* di;
-        
+
         searchkey.obj_id = Vcb->superblock.root_dir_objectid;
         searchkey.obj_type = TYPE_DIR_ITEM;
         searchkey.offset = crc32;
-        
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
         }
-        
-        if (keycmp(&tp.item->key, &searchkey)) {
+
+        if (keycmp(tp.item->key, searchkey)) {
             ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
             goto end;
         }
-        
+
         if (tp.item->size < sizeof(DIR_ITEM)) {
             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));
             goto end;
         }
-        
+
         di = (DIR_ITEM*)tp.item->data;
-        
+
         if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
             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);
             goto end;
         }
-        
+
         if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
             ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
             goto end;
         }
-        
+
         if (di->key.obj_type != TYPE_ROOT_ITEM) {
             ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
             goto end;
         }
-        
+
         le = Vcb->roots.Flink;
         while (le != &Vcb->roots) {
             root* r = CONTAINING_RECORD(le, root, list_entry);
-            
+
             if (r->id == di->key.obj_id)
                 return r;
-            
+
             le = le->Flink;
         }
-        
+
         ERR("could not find root %llx, using default instead\n", di->key.obj_id);
     }
-    
+
 end:
     le = Vcb->roots.Flink;
     while (le != &Vcb->roots) {
         root* r = CONTAINING_RECORD(le, root, list_entry);
-        
+
         if (r->id == BTRFS_ROOT_FSTREE)
             return r;
-        
+
         le = le->Flink;
     }
-    
+
     return NULL;
 }
 
-static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
+void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
+    TRACE("(%p, %p)\n", FileObject, ccfs);
+
+    CcInitializeCacheMap(FileObject, ccfs, FALSE, cache_callbacks, FileObject);
+
+    if (diskacc)
+        fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
+
+    CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+}
+
+static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
     device_extension* Vcb = DeviceObject->DeviceExtension;
     ULONG i;
-    NTSTATUS Status;
-    
-    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
-    
-    Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
-    if (!Vcb->threads.threads) {
+
+    Vcb->calcthreads.num_threads = KeQueryActiveProcessorCount(NULL);
+
+    Vcb->calcthreads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads, ALLOC_TAG);
+    if (!Vcb->calcthreads.threads) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
-    RtlZeroMemory(Vcb->threads.threads, sizeof(drv_thread) * Vcb->threads.num_threads);
-    
-    for (i = 0; i < Vcb->threads.num_threads; i++) {
-        Vcb->threads.threads[i].DeviceObject = DeviceObject;
-        KeInitializeEvent(&Vcb->threads.threads[i].event, SynchronizationEvent, FALSE);
-        KeInitializeEvent(&Vcb->threads.threads[i].finished, NotificationEvent, FALSE);
-        InitializeListHead(&Vcb->threads.threads[i].jobs);
-        KeInitializeSpinLock(&Vcb->threads.threads[i].spin_lock);
-        
-        Status = PsCreateSystemThread(&Vcb->threads.threads[i].handle, 0, NULL, NULL, NULL, worker_thread, &Vcb->threads.threads[i]);
+
+    InitializeListHead(&Vcb->calcthreads.job_list);
+    ExInitializeResourceLite(&Vcb->calcthreads.lock);
+    KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, FALSE);
+
+    RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
+
+    for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+        NTSTATUS Status;
+
+        Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
+        KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, FALSE);
+
+        Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, NULL, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
         if (!NT_SUCCESS(Status)) {
             ULONG j;
-            
+
             ERR("PsCreateSystemThread returned %08x\n", Status);
-            
+
             for (j = 0; j < i; j++) {
-                Vcb->threads.threads[i].quit = TRUE;
-                KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
+                Vcb->calcthreads.threads[i].quit = TRUE;
             }
-            
+
+            KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
+
             return Status;
         }
     }
-    
+
     return STATUS_SUCCESS;
 }
 
-BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
-    ULONG threadnum;
-    thread_job* tj;
-    
-    threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
-    
-    if (Vcb->threads.threads[threadnum].quit)
+static BOOL is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
+    NTSTATUS Status;
+    MOUNTDEV_NAME mdn, *mdn2;
+    ULONG mdnsize;
+
+    Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
+    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
         return FALSE;
-    
-    tj = ExAllocatePoolWithTag(NonPagedPool, sizeof(thread_job), ALLOC_TAG);
-    if (!tj) {
-        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-        Irp->IoStatus.Information = 0;
-        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+
+    mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
+    if (!mdn2) {
+        ERR("out of memory\n");
         return FALSE;
     }
-    
-    tj->Irp = Irp;
-    
-    ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
-    KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
-    
-    return TRUE;
+
+    Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        ExFreePool(mdn2);
+        return FALSE;
+    }
+
+    if (mdn2->NameLength > wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR) &&
+        RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) == wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) {
+        ExFreePool(mdn2);
+        return TRUE;
+    }
+
+    ExFreePool(mdn2);
+
+    return FALSE;
 }
 
-static BOOL raid_generations_okay(device_extension* Vcb) {
-    UINT64 i;
-    
-    // FIXME - if the difference between superblocks is small, we should try to recover
-    
-    for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        LIST_ENTRY* le = volumes.Flink;
-        while (le != &volumes) {
-            volume* v = CONTAINING_RECORD(le, volume, list_entry);
-            
-            if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-                RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
-            ) {
-                if (v->gen1 != Vcb->superblock.generation - 1) {
-                    WARN("device %llu had generation %llx, expected %llx\n", i, v->gen1, Vcb->superblock.generation - 1);
-                    return FALSE;
-                } else
-                    break;
+static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _In_ const GUID* guid) {
+    NTSTATUS Status;
+    WCHAR *list = NULL, *s;
+
+    Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceInterfaces returned %08x\n", Status);
+        return Status;
+    }
+
+    s = list;
+    while (s[0] != 0) {
+        PFILE_OBJECT FileObject;
+        PDEVICE_OBJECT devobj;
+        UNICODE_STRING name;
+
+        name.Length = name.MaximumLength = (USHORT)wcslen(s) * sizeof(WCHAR);
+        name.Buffer = s;
+
+        if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
+            if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
+                ObDereferenceObject(FileObject);
+
+                pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
+                if (!pnp_name->Buffer) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+
+                RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
+                pnp_name->Length = pnp_name->MaximumLength = name.Length;
+
+                Status = STATUS_SUCCESS;
+                goto end;
             }
-            le = le->Flink;
+
+            ObDereferenceObject(FileObject);
         }
+
+        s = &s[wcslen(s) + 1];
     }
-    
+
+    pnp_name->Length = pnp_name->MaximumLength = 0;
+    pnp_name->Buffer = 0;
+
+    Status = STATUS_NOT_FOUND;
+
+end:
+    if (list)
+        ExFreePool(list);
+
+    return Status;
+}
+
+NTSTATUS get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _Out_ const GUID** guid) {
+    NTSTATUS Status;
+
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_VOLUME;
+        return Status;
+    }
+
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
+        return Status;
+    }
+
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_DISK;
+        return Status;
+    }
+
+    return STATUS_NOT_FOUND;
+}
+
+_Success_(return>=0)
+static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ BOOL* no_pnp) {
+    NTSTATUS Status;
+    ULONG to_read;
+    superblock* sb;
+    UINT32 crc32;
+    UNICODE_STRING pnp_name;
+    const GUID* guid;
+
+    to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), DeviceObject->SectorSize);
+
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = sync_read_phys(DeviceObject, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("sync_read_phys returned %08x\n", Status);
+        goto end;
+    }
+
+    if (sb->magic != BTRFS_MAGIC) {
+        Status = STATUS_SUCCESS;
+        goto end;
+    }
+
+    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+    if (crc32 != *((UINT32*)sb->checksum)) {
+        WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+        Status = STATUS_SUCCESS;
+        goto end;
+    }
+
+    DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
+
+    pnp_name.Buffer = NULL;
+
+    Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
+    if (!NT_SUCCESS(Status)) {
+        WARN("get_device_pnp_name returned %08x\n", Status);
+        pnp_name.Length = 0;
+    }
+
+    if (pnp_name.Length == 0)
+        *no_pnp = TRUE;
+    else {
+        *no_pnp = FALSE;
+        volume_arrival(drvobj, &pnp_name);
+    }
+
+    if (pnp_name.Buffer)
+        ExFreePool(pnp_name.Buffer);
+
+    Status = STATUS_SUCCESS;
+
+end:
+    ExFreePool(sb);
+
+    return Status;
+}
+
+static BOOL still_has_superblock(_In_ PDEVICE_OBJECT device) {
+    NTSTATUS Status;
+    ULONG to_read;
+    superblock* sb;
+    PDEVICE_OBJECT device2;
+
+    if (!device)
+        return FALSE;
+
+    to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
+
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return FALSE;
+    }
+
+    Status = sync_read_phys(device, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("Failed to read superblock: %08x\n", Status);
+        ExFreePool(sb);
+        return FALSE;
+    }
+
+    if (sb->magic != BTRFS_MAGIC) {
+        TRACE("not a BTRFS volume\n");
+        ExFreePool(sb);
+        return FALSE;
+    } else {
+        UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+        if (crc32 != *((UINT32*)sb->checksum)) {
+            WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+            ExFreePool(sb);
+            return FALSE;
+        }
+    }
+
+    device2 = device;
+
+    do {
+        device2->Flags &= ~DO_VERIFY_VOLUME;
+        device2 = IoGetLowerDeviceObject(device2);
+    } while (device2);
+
+    ExFreePool(sb);
     return TRUE;
 }
 
-static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
-    PIO_STACK_LOCATION Stack;
+static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp;
     PDEVICE_OBJECT NewDeviceObject = NULL;
-    PDEVICE_OBJECT DeviceToMount;
+    PDEVICE_OBJECT DeviceToMount, readobj;
     NTSTATUS Status;
     device_extension* Vcb = NULL;
-    GET_LENGTH_INFORMATION gli;
-    UINT64 i;
-    LIST_ENTRY* le;
+    LIST_ENTRY *le, batchlist;
     KEY searchkey;
     traverse_ptr tp;
     fcb* root_fcb = NULL;
     ccb* root_ccb = NULL;
-    
-    TRACE("mount_vol called\n");
-    
-    if (DeviceObject != devobj)
-    {
+    BOOL init_lookaside = FALSE;
+    device* dev;
+    volume_device_extension* vde = NULL;
+    pdo_device_extension* pdode = NULL;
+    volume_child* vc;
+    BOOL no_pnp = FALSE;
+    UINT64 readobjsize;
+
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    if (DeviceObject != master_devobj) {
         Status = STATUS_INVALID_DEVICE_REQUEST;
         goto exit;
     }
 
-    Stack = IoGetCurrentIrpStackLocation(Irp);
-    DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
+    IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
 
-    Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
-                       &gli, sizeof(gli), TRUE, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error reading length information: %08x\n", Status);
-        Status = STATUS_UNRECOGNIZED_VOLUME;
-        goto exit;
+    if (!is_btrfs_volume(DeviceToMount)) {
+        Status = check_mount_device(DeviceToMount, &no_pnp);
+        if (!NT_SUCCESS(Status))
+            WARN("check_mount_device returned %08x\n", Status);
+
+        if (!no_pnp) {
+            Status = STATUS_UNRECOGNIZED_VOLUME;
+            goto exit2;
+        }
+    } else {
+        PDEVICE_OBJECT pdo;
+
+        pdo = DeviceToMount;
+
+        while (IoGetLowerDeviceObject(pdo)) {
+            pdo = IoGetLowerDeviceObject(pdo);
+        }
+
+        ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
+
+        le = pdo_list.Flink;
+        while (le != &pdo_list) {
+            pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+            if (pdode->pdo == pdo) {
+                vde = pdode->vde;
+                break;
+            }
+
+            le = le->Flink;
+        }
+
+        ExReleaseResourceLite(&pdo_list_lock);
+
+        if (!vde || vde->type != VCB_TYPE_VOLUME) {
+            vde = NULL;
+            Status = STATUS_UNRECOGNIZED_VOLUME;
+            goto exit2;
+        }
+    }
+
+    if (vde) {
+        pdode = vde->pdode;
+
+        ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
+
+        le = pdode->children.Flink;
+        while (le != &pdode->children) {
+            LIST_ENTRY* le2 = le->Flink;
+
+            vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
+
+            if (!still_has_superblock(vc->devobj)) {
+                remove_volume_child(vde, vc, FALSE);
+
+                if (pdode->num_children == 0) {
+                    ERR("error - number of devices is zero\n");
+                    Status = STATUS_INTERNAL_ERROR;
+                    goto exit2;
+                }
+
+                Status = STATUS_DEVICE_NOT_READY;
+                goto exit2;
+            }
+
+            le = le2;
+        }
+
+        if (pdode->num_children == 0 || pdode->children_loaded == 0) {
+            ERR("error - number of devices is zero\n");
+            Status = STATUS_INTERNAL_ERROR;
+            goto exit;
+        }
+
+        ExConvertExclusiveToSharedLite(&pdode->child_lock);
+
+        vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
+
+        readobj = vc->devobj;
+        readobjsize = vc->size;
+
+        vde->device->Characteristics &= ~FILE_DEVICE_SECURE_OPEN;
+    } else {
+        GET_LENGTH_INFORMATION gli;
+
+        vc = NULL;
+        readobj = DeviceToMount;
+
+        Status = dev_ioctl(readobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+                           &gli, sizeof(gli), TRUE, NULL);
+
+        if (!NT_SUCCESS(Status)) {
+            ERR("error reading length information: %08x\n", Status);
+            goto exit;
+        }
+
+        readobjsize = gli.Length.QuadPart;
     }
 
-    Status = IoCreateDevice(drvobj,
-                            sizeof(device_extension),
-                            NULL,
-                            FILE_DEVICE_DISK_FILE_SYSTEM,
-                            0,
-                            FALSE,
-                            &NewDeviceObject);
+    Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
         ERR("IoCreateDevice returned %08x\n", Status);
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
     }
-    
-//     TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
 
     NewDeviceObject->Flags |= DO_DIRECT_IO;
+
+    // Some programs seem to expect that the sector size will be 512, for
+    // FILE_NO_INTERMEDIATE_BUFFERING and the like.
+    NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
+
     Vcb = (PVOID)NewDeviceObject->DeviceExtension;
     RtlZeroMemory(Vcb, sizeof(device_extension));
-    Vcb->type = VCB_TYPE_VOLUME;
-    
+    Vcb->type = VCB_TYPE_FS;
+    Vcb->vde = vde;
+
     ExInitializeResourceLite(&Vcb->tree_lock);
-    Vcb->open_trees = 0;
     Vcb->need_write = FALSE;
 
     ExInitializeResourceLite(&Vcb->fcb_lock);
-    ExInitializeResourceLite(&Vcb->DirResource);
-    ExInitializeResourceLite(&Vcb->checksum_lock);
     ExInitializeResourceLite(&Vcb->chunk_lock);
-
-    ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
-    InsertTailList(&VcbList, &Vcb->list_entry);
-    ExReleaseResourceLite(&global_loading_lock);
+    ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
+    ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
+    ExInitializeResourceLite(&Vcb->dirty_subvols_lock);
+    ExInitializeResourceLite(&Vcb->scrub.stats_lock);
 
     ExInitializeResourceLite(&Vcb->load_lock);
     ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
 
-//     Vcb->Identifier.Type = NTFS_TYPE_VCB;
-//     Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
-// 
-//     Status = NtfsGetVolumeData(DeviceToMount,
-//                                Vcb);
-//     if (!NT_SUCCESS(Status))
-//         goto ByeBye;
-    
-//     Vcb->device = DeviceToMount;
+    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
     DeviceToMount->Flags |= DO_DIRECT_IO;
-    
-//     Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
-//                        &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
-//     if (!NT_SUCCESS(Status)) {
-//         ERR("error reading disk geometry: %08x\n", Status);
-//         goto exit;
-//     } else {
-//         TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
-//                       Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
-//                       Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
-//     }
-    
-    TRACE("partition length = %llx\n", gli.Length.QuadPart);
-
-    Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart);
+
+    Status = read_superblock(Vcb, readobj, readobjsize);
     if (!NT_SUCCESS(Status)) {
-        Status = STATUS_UNRECOGNIZED_VOLUME;
+        if (!IoIsErrorUserInduced(Status))
+            Status = STATUS_UNRECOGNIZED_VOLUME;
+        else if (Irp->Tail.Overlay.Thread)
+            IoSetHardErrorOrVerifyDevice(Irp, readobj);
+
         goto exit;
     }
 
-    if (Vcb->superblock.magic != BTRFS_MAGIC) {
-        ERR("not a BTRFS volume\n");
+    if (!vde && Vcb->superblock.num_devices > 1) {
+        ERR("cannot mount multi-device FS with non-PNP device\n");
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
-    } else {
-        TRACE("btrfs magic found\n");
     }
-    
-    Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
+
+    Status = registry_load_volume_options(Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("registry_load_volume_options returned %08x\n", Status);
         goto exit;
     }
-    
+
+    if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
+        ERR("could not mount as %u device(s) missing\n", pdode->num_children - pdode->children_loaded);
+        Status = STATUS_DEVICE_NOT_READY;
+        goto exit;
+    }
+
     if (Vcb->options.ignore) {
         TRACE("ignoring volume\n");
         Status = STATUS_UNRECOGNIZED_VOLUME;
@@ -3542,378 +4123,693 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
     }
-    
-    le = volumes.Flink;
-    while (le != &volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
-        
-        if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
-            // skipping over device in RAID which isn't the first one
-            Status = STATUS_UNRECOGNIZED_VOLUME;
-            goto exit;
-        }
-        
-        le = le->Flink;
-    }
-    
+
     Vcb->readonly = FALSE;
     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
         WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
         Vcb->readonly = TRUE;
     }
-    
+
+    if (Vcb->options.readonly)
+        Vcb->readonly = TRUE;
+
     Vcb->superblock.generation++;
     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
-    
-    Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
-    if (!Vcb->devices) {
+
+    InitializeListHead(&Vcb->devices);
+    dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+    if (!dev) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
-    
-    Vcb->devices[0].devobj = DeviceToMount;
-    RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
-    init_device(Vcb, &Vcb->devices[0], FALSE);
-    Vcb->devices[0].length = gli.Length.QuadPart;
-    
-    if (Vcb->superblock.num_devices > 1)
-        RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
-    
+
+    dev->devobj = readobj;
+    RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
+
+    if (dev->devitem.num_bytes > readobjsize) {
+        WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev->devitem.dev_id,
+                dev->devitem.num_bytes, readobjsize);
+
+        dev->devitem.num_bytes = readobjsize;
+    }
+
+    dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
+
+    init_device(Vcb, dev, TRUE);
+
+    InsertTailList(&Vcb->devices, &dev->list_entry);
     Vcb->devices_loaded = 1;
-    
+
+    if (DeviceToMount->Flags & DO_SYSTEM_BOOT_PARTITION)
+        Vcb->disallow_dismount = TRUE;
+
     TRACE("DeviceToMount = %p\n", DeviceToMount);
-    TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
+    TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp->Parameters.MountVolume.Vpb);
 
     NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
     NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
-    
+
     InitializeListHead(&Vcb->roots);
     InitializeListHead(&Vcb->drop_roots);
-    
+
     Vcb->log_to_phys_loaded = FALSE;
-    
-    Vcb->max_inline = Vcb->superblock.node_size / 2;
 
-    add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
-    
+    add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
+
     if (!Vcb->chunk_root) {
         ERR("Could not load chunk root.\n");
         Status = STATUS_INTERNAL_ERROR;
         goto exit;
     }
-       
+
     InitializeListHead(&Vcb->sys_chunks);
     Status = load_sys_chunks(Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("load_sys_chunks returned %08x\n", Status);
         goto exit;
     }
-    
+
     InitializeListHead(&Vcb->chunks);
-    InitializeListHead(&Vcb->chunks_changed);
     InitializeListHead(&Vcb->trees);
+    InitializeListHead(&Vcb->trees_hash);
     InitializeListHead(&Vcb->all_fcbs);
     InitializeListHead(&Vcb->dirty_fcbs);
     InitializeListHead(&Vcb->dirty_filerefs);
-    InitializeListHead(&Vcb->shared_extents);
-    InitializeListHead(&Vcb->sector_checksums);
-    
-    KeInitializeSpinLock(&Vcb->dirty_fcbs_lock);
-    KeInitializeSpinLock(&Vcb->dirty_filerefs_lock);
-    KeInitializeSpinLock(&Vcb->shared_extents_lock);
-    
+    InitializeListHead(&Vcb->dirty_subvols);
+    InitializeListHead(&Vcb->send_ops);
+
     InitializeListHead(&Vcb->DirNotifyList);
+    InitializeListHead(&Vcb->scrub.errors);
 
     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
-    
-    Status = load_chunk_root(Vcb);
+
+    ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
+    ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
+    ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
+    ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
+    init_lookaside = TRUE;
+
+    Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
+
+    Status = load_chunk_root(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("load_chunk_root returned %08x\n", Status);
         goto exit;
     }
-    
+
     if (Vcb->superblock.num_devices > 1) {
-        if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+        if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
             ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
-            
+
             IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
 
             Status = STATUS_INTERNAL_ERROR;
             goto exit;
         }
-        
-        if (!raid_generations_okay(Vcb)) {
-            ERR("could not mount as generation mismatch\n");
-            
-            IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
 
-            Status = STATUS_INTERNAL_ERROR;
-            goto exit;
+        if (dev->readonly && !Vcb->readonly) {
+            Vcb->readonly = TRUE;
+
+            le = Vcb->devices.Flink;
+            while (le != &Vcb->devices) {
+                device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+                if (dev2->readonly && !dev2->seeding)
+                    break;
+
+                if (!dev2->readonly) {
+                    Vcb->readonly = FALSE;
+                    break;
+                }
+
+                le = le->Flink;
+            }
+
+            if (Vcb->readonly)
+                WARN("setting volume to readonly\n");
+        }
+    } else {
+        if (dev->readonly) {
+            WARN("setting volume to readonly as device is readonly\n");
+            Vcb->readonly = TRUE;
         }
     }
-    
-    add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
-    
+
+    add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
+
     if (!Vcb->root_root) {
         ERR("Could not load root of roots.\n");
         Status = STATUS_INTERNAL_ERROR;
         goto exit;
     }
-    
-    Status = look_for_roots(Vcb);
+
+    Status = look_for_roots(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("look_for_roots returned %08x\n", Status);
         goto exit;
     }
-    
-    Status = find_chunk_usage(Vcb);
-    if (!NT_SUCCESS(Status)) {
-        ERR("find_chunk_usage returned %08x\n", Status);
-        goto exit;
+
+    if (!Vcb->readonly) {
+        Status = find_chunk_usage(Vcb, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("find_chunk_usage returned %08x\n", Status);
+            goto exit;
+        }
     }
-    
+
+    InitializeListHead(&batchlist);
+
     // We've already increased the generation by one
-    if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
-        WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
-        Status = clear_free_space_cache(Vcb);
+    if (!Vcb->readonly && (
+        Vcb->options.clear_cache ||
+        (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE) && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) ||
+        (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)))) {
+        if (Vcb->options.clear_cache)
+            WARN("ClearCache option was set, clearing cache...\n");
+        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))
+            WARN("clearing free-space tree created by buggy Linux driver\n");
+        else
+            WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
+
+        Status = clear_free_space_cache(Vcb, &batchlist, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("clear_free_space_cache returned %08x\n", Status);
+            clear_batch_list(Vcb, &batchlist);
             goto exit;
         }
     }
-    
-    Vcb->volume_fcb = create_fcb();
+
+    Status = commit_batch_list(Vcb, &batchlist, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("commit_batch_list returned %08x\n", Status);
+        goto exit;
+    }
+
+    Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
     if (!Vcb->volume_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
-    
+
     Vcb->volume_fcb->Vcb = Vcb;
     Vcb->volume_fcb->sd = NULL;
-    
-    root_fcb = create_fcb();
+
+    Vcb->dummy_fcb = create_fcb(Vcb, NonPagedPool);
+    if (!Vcb->dummy_fcb) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+
+    Vcb->dummy_fcb->Vcb = Vcb;
+    Vcb->dummy_fcb->type = BTRFS_TYPE_DIRECTORY;
+    Vcb->dummy_fcb->inode = 2;
+    Vcb->dummy_fcb->subvol = Vcb->root_root;
+    Vcb->dummy_fcb->atts = FILE_ATTRIBUTE_DIRECTORY;
+    Vcb->dummy_fcb->inode_item.st_nlink = 1;
+    Vcb->dummy_fcb->inode_item.st_mode = __S_IFDIR;
+
+    Vcb->dummy_fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+    if (!Vcb->dummy_fcb->hash_ptrs) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+
+    RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+
+    Vcb->dummy_fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+    if (!Vcb->dummy_fcb->hash_ptrs_uc) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+
+    RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+
+    root_fcb = create_fcb(Vcb, NonPagedPool);
     if (!root_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
-    
+
     root_fcb->Vcb = Vcb;
     root_fcb->inode = SUBVOL_ROOT_INODE;
     root_fcb->type = BTRFS_TYPE_DIRECTORY;
-    
+
 #ifdef DEBUG_FCB_REFCOUNTS
     WARN("volume FCB = %p\n", Vcb->volume_fcb);
     WARN("root FCB = %p\n", root_fcb);
 #endif
-    
-    root_fcb->subvol = find_default_subvol(Vcb);
+
+    root_fcb->subvol = find_default_subvol(Vcb, Irp);
 
     if (!root_fcb->subvol) {
         ERR("could not find top subvol\n");
         Status = STATUS_INTERNAL_ERROR;
         goto exit;
     }
-    
+
+    Status = load_dir_children(Vcb, root_fcb, TRUE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("load_dir_children returned %08x\n", Status);
+        goto exit;
+    }
+
     searchkey.obj_id = root_fcb->inode;
     searchkey.obj_type = TYPE_INODE_ITEM;
     searchkey.offset = 0xffffffffffffffff;
-    
-    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
+
+    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto exit;
     }
-    
+
     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
         ERR("couldn't find INODE_ITEM for root directory\n");
         Status = STATUS_INTERNAL_ERROR;
         goto exit;
     }
-    
+
     if (tp.item->size > 0)
         RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
-    
-    fcb_get_sd(root_fcb, NULL);
-    
-    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
-    
-    Vcb->root_fileref = create_fileref();
+
+    fcb_get_sd(root_fcb, NULL, TRUE, Irp);
+
+    root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
+
+    Vcb->root_fileref = create_fileref(Vcb);
     if (!Vcb->root_fileref) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
-    
+
     Vcb->root_fileref->fcb = root_fcb;
     InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
     InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
-    
+
     root_fcb->fileref = Vcb->root_fileref;
-    
+
     root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
     if (!root_ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
-    
+
     Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
     Vcb->root_file->FsContext = root_fcb;
-    
+    Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
+    Vcb->root_file->Vpb = DeviceObject->Vpb;
+
     RtlZeroMemory(root_ccb, sizeof(ccb));
     root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
     root_ccb->NodeSize = sizeof(ccb);
-    
-    Vcb->root_file->FsContext = root_ccb;
-    
-    for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        Status = find_disk_holes(Vcb, &Vcb->devices[i]);
+
+    Vcb->root_file->FsContext2 = root_ccb;
+
+    _SEH2_TRY {
+        CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), FALSE, cache_callbacks, Vcb->root_file);
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        Status = _SEH2_GetExceptionCode();
+        goto exit;
+    } _SEH2_END;
+
+    le = Vcb->devices.Flink;
+    while (le != &Vcb->devices) {
+        device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+        Status = find_disk_holes(Vcb, dev2, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("find_disk_holes returned %08x\n", Status);
             goto exit;
         }
+
+        le = le->Flink;
     }
-    
-//     root_test(Vcb);
-    
-    KeInitializeSpinLock(&Vcb->FcbListLock);
 
-    NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
-    Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
-    Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
+    NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
+    IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
+    IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
     NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
     NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
-    Vcb->Vpb = NewDeviceObject->Vpb;
-    
+
     KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
-    
+
     Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         goto exit;
     }
-    
-    Status = create_worker_threads(NewDeviceObject);
+
+    Status = create_calc_threads(NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("create_worker_threads returned %08x\n", Status);
+        ERR("create_calc_threads returned %08x\n", Status);
         goto exit;
     }
-    
+
     Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status))
         WARN("registry_mark_volume_mounted returned %08x\n", Status);
-    
+
+    Status = look_for_balance_item(Vcb);
+    if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
+        WARN("look_for_balance_item returned %08x\n", Status);
+
     Status = STATUS_SUCCESS;
 
+    if (vde)
+        vde->mounted_device = NewDeviceObject;
+
+    ExInitializeResourceLite(&Vcb->send_load_lock);
+
 exit:
+    if (pdode)
+        ExReleaseResourceLite(&pdode->child_lock);
+
+exit2:
     if (Vcb) {
+        ExReleaseResourceLite(&Vcb->tree_lock);
         ExReleaseResourceLite(&Vcb->load_lock);
     }
 
     if (!NT_SUCCESS(Status)) {
         if (Vcb) {
+            if (init_lookaside) {
+                ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
+                ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
+                ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+                ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+                ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+                ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
+                ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+                ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+                ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
+            }
+
             if (Vcb->root_file)
                 ObDereferenceObject(Vcb->root_file);
-            else if (Vcb->root_fileref)
-                free_fileref(Vcb->root_fileref);
-            else if (root_fcb)
-                free_fcb(root_fcb);
+            else if (Vcb->root_fileref) {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, Vcb->root_fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+            } else if (root_fcb) {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fcb(Vcb, root_fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+            }
 
-            if (Vcb->volume_fcb)
-                free_fcb(Vcb->volume_fcb);
+            if (Vcb->volume_fcb) {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fcb(Vcb, Vcb->volume_fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+            }
 
             ExDeleteResourceLite(&Vcb->tree_lock);
             ExDeleteResourceLite(&Vcb->load_lock);
             ExDeleteResourceLite(&Vcb->fcb_lock);
-            ExDeleteResourceLite(&Vcb->DirResource);
-            ExDeleteResourceLite(&Vcb->checksum_lock);
             ExDeleteResourceLite(&Vcb->chunk_lock);
+            ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+            ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+            ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
+            ExDeleteResourceLite(&Vcb->scrub.stats_lock);
 
-            if (Vcb->devices)
-                ExFreePoolWithTag(Vcb->devices, ALLOC_TAG);
+            if (Vcb->devices.Flink) {
+                while (!IsListEmpty(&Vcb->devices)) {
+                    device* dev2 = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
 
-            RemoveEntryList(&Vcb->list_entry);
+                    ExFreePool(dev2);
+                }
+            }
         }
 
         if (NewDeviceObject)
             IoDeleteDevice(NewDeviceObject);
-    } else
+    } else {
+        ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
+        InsertTailList(&VcbList, &Vcb->list_entry);
+        ExReleaseResourceLite(&global_loading_lock);
+
         FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
+    }
 
     TRACE("mount_vol done (status: %lx)\n", Status);
 
     return Status;
 }
 
-static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
+    NTSTATUS Status;
+    superblock* sb;
+    UINT32 crc32;
+    ULONG to_read, cc;
+
+    if (!dev->devobj)
+        return STATUS_WRONG_VOLUME;
+
+    if (dev->removable) {
+        IO_STATUS_BLOCK iosb;
+
+        Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+        if (IoIsErrorUserInduced(Status)) {
+            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
+
+            if (Vcb->vde) {
+                pdo_device_extension* pdode = Vcb->vde->pdode;
+                LIST_ENTRY* le2;
+                BOOL changed = FALSE;
+
+                ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
+
+                le2 = pdode->children.Flink;
+                while (le2 != &pdode->children) {
+                    volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+
+                    if (vc->devobj == dev->devobj) {
+                        TRACE("removing device\n");
+
+                        remove_volume_child(Vcb->vde, vc, TRUE);
+                        changed = TRUE;
+
+                        break;
+                    }
+
+                    le2 = le2->Flink;
+                }
+
+                if (!changed)
+                    ExReleaseResourceLite(&pdode->child_lock);
+            }
+        } else if (!NT_SUCCESS(Status)) {
+            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
+            return Status;
+        } else if (iosb.Information < sizeof(ULONG)) {
+            ERR("iosb.Information was too short\n");
+            return STATUS_INTERNAL_ERROR;
+        }
+
+        dev->change_count = cc;
+    }
+
+    to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), dev->devobj->SectorSize);
+
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = sync_read_phys(dev->devobj, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("Failed to read superblock: %08x\n", Status);
+        ExFreePool(sb);
+        return Status;
+    }
+
+    if (sb->magic != BTRFS_MAGIC) {
+        ERR("not a BTRFS volume\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+
+    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+    TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+
+    if (crc32 != *((UINT32*)sb->checksum)) {
+        ERR("checksum error\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+
+    if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
+        ERR("different UUIDs\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+
+    ExFreePool(sb);
+
+    dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
+    device_extension* Vcb = devobj->DeviceExtension;
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    UINT64 failed_devices = 0;
+    BOOL locked = FALSE, remove = FALSE;
+
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
+        return STATUS_WRONG_VOLUME;
+
+    if (!ExIsResourceAcquiredExclusive(&Vcb->tree_lock)) {
+        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+        locked = TRUE;
+    }
+
+    if (Vcb->removing) {
+        if (locked) ExReleaseResourceLite(&Vcb->tree_lock);
+        return STATUS_WRONG_VOLUME;
+    }
+
+    InterlockedIncrement(&Vcb->open_files); // so pnp_surprise_removal doesn't uninit the device while we're still using it
+
+    le = Vcb->devices.Flink;
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+        Status = verify_device(Vcb, dev);
+        if (!NT_SUCCESS(Status)) {
+            failed_devices++;
+
+            if (dev->devobj && Vcb->options.allow_degraded)
+                dev->devobj = NULL;
+        }
+
+        le = le->Flink;
+    }
+
+    InterlockedDecrement(&Vcb->open_files);
+
+    if (Vcb->removing && Vcb->open_files == 0)
+        remove = TRUE;
+
+    if (locked)
+        ExReleaseResourceLite(&Vcb->tree_lock);
+
+    if (remove) {
+        uninit(Vcb, FALSE);
+        return Status;
+    }
+
+    if (failed_devices == 0 || (Vcb->options.allow_degraded && failed_devices < Vcb->superblock.num_devices)) {
+        Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
+        return STATUS_SUCCESS;
+    }
+
+    return Status;
+}
+
+_Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
     device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
-    TRACE("file system control\n");
-    
     FsRtlEnterFileSystem();
 
+    TRACE("file system control\n");
+
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_file_system_control(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
-    
+
     Status = STATUS_NOT_IMPLEMENTED;
 
     IrpSp = IoGetCurrentIrpStackLocation( Irp );
-    
+
     Irp->IoStatus.Information = 0;
-    
+
     switch (IrpSp->MinorFunction) {
         case IRP_MN_MOUNT_VOLUME:
             TRACE("IRP_MN_MOUNT_VOLUME\n");
-            
+
             Status = mount_vol(DeviceObject, Irp);
             break;
-            
+
         case IRP_MN_KERNEL_CALL:
             TRACE("IRP_MN_KERNEL_CALL\n");
-            
-            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE);
+
+            Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
             break;
-            
+
         case IRP_MN_USER_FS_REQUEST:
             TRACE("IRP_MN_USER_FS_REQUEST\n");
-            
-            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
+
+            Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
             break;
-            
+
         case IRP_MN_VERIFY_VOLUME:
-            FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
+            TRACE("IRP_MN_VERIFY_VOLUME\n");
+
+            Status = verify_volume(DeviceObject);
+
+            if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
+                ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+                Vcb->removing = TRUE;
+                ExReleaseResourceLite(&Vcb->tree_lock);
+            }
+
             break;
-           
+
         default:
             break;
     }
 
-    Irp->IoStatus.Status = Status;
+end:
+    TRACE("returning %08x\n", Status);
 
-    IoCompleteRequest(Irp, IO_NO_INCREMENT);
-    
-exit:
-    if (top_level) 
+    if (Irp) {
+        Irp->IoStatus.Status = Status;
+
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_LOCK_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     fcb* fcb = IrpSp->FileObject->FsContext;
@@ -3923,278 +4819,349 @@ static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_lock_control(DeviceObject, Irp);
+
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
         goto exit;
     }
-    
+
     TRACE("lock control\n");
-    
+
     Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
-    
+
 exit:
-    if (top_level) 
+    TRACE("returning %08x\n", Status);
+
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
-    
-    return Status;
-}
 
-NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    part0_device_extension* p0de = DeviceObject->DeviceExtension;
-    
-    IoSkipCurrentIrpStackLocation(Irp);
-    
-    Status = IoCallDriver(p0de->devobj, Irp);
-    
     return Status;
 }
 
-static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_SHUTDOWN)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
-    part0_device_extension* p0de = DeviceObject->DeviceExtension;
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    
-    TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
-    
-    switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
-        case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
-        {
-            MOUNTDEV_UNIQUE_ID* mduid;
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
-                Status = STATUS_BUFFER_TOO_SMALL;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
+    BOOL top_level;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
 
-            mduid = Irp->AssociatedIrp.SystemBuffer;
-            mduid->UniqueIdLength = sizeof(BTRFS_UUID);
+    FsRtlEnterFileSystem();
 
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) {
-                Status = STATUS_BUFFER_OVERFLOW;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
+    TRACE("shutdown\n");
 
-            RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID));
+    top_level = is_top_level(Irp);
 
-            Status = STATUS_SUCCESS;
-            Irp->IoStatus.Status = Status;
-            Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength;
-            IoCompleteRequest(Irp, IO_NO_INCREMENT);
-            
-            return Status;
-        }
-        
-        case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
-        {
-            PMOUNTDEV_NAME name;
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
-                Status = STATUS_BUFFER_TOO_SMALL;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_shutdown(DeviceObject, Irp);
+        goto end;
+    }
 
-            name = Irp->AssociatedIrp.SystemBuffer;
-            name->NameLength = p0de->name.Length;
+    Status = STATUS_SUCCESS;
 
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME) - 1 + name->NameLength) {
-                Status = STATUS_BUFFER_OVERFLOW;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
-            
-            RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length);
+    shutting_down = TRUE;
+    KeSetEvent(&mountmgr_thread_event, 0, FALSE);
 
-            Status = STATUS_SUCCESS;
-            Irp->IoStatus.Status = Status;
-            Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) - 1 + name->NameLength;
-            IoCompleteRequest(Irp, IO_NO_INCREMENT);
-            
-            return Status;
-        }
+    while (!IsListEmpty(&VcbList)) {
+        Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
+
+        TRACE("shutting down Vcb %p\n", Vcb);
+
+        uninit(Vcb, TRUE);
     }
-    
-    IoSkipCurrentIrpStackLocation(Irp);
-    
-    Status = IoCallDriver(p0de->devobj, Irp);
-    
-    TRACE("returning %08x\n", Status);
-    
+
+#ifdef _DEBUG
+    if (comfo) {
+        ObDereferenceObject(comfo);
+        comdo = NULL;
+        comfo = NULL;
+    }
+#endif
+
+end:
+    Irp->IoStatus.Status = Status;
+    Irp->IoStatus.Information = 0;
+
+    IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+    if (top_level)
+        IoSetTopLevelIrp(NULL);
+
+    FsRtlExitFileSystem();
+
     return Status;
 }
 
-static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_POWER)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    PFILE_OBJECT FileObject = IrpSp->FileObject;
     device_extension* Vcb = DeviceObject->DeviceExtension;
-    fcb* fcb;
     BOOL top_level;
 
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
-    
+
     Irp->IoStatus.Information = 0;
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_device_control(DeviceObject, Irp);
-        goto end2;
-    }
-    
-    TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
-    
-    if (!FileObject) {
-        ERR("FileObject was NULL\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto end;
-    }
-    
-    fcb = FileObject->FsContext;
-    
-    if (!fcb) {
-        ERR("FCB was NULL\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto end;
-    }
-    
-    if (fcb != Vcb->volume_fcb) {
-        Status = STATUS_NOT_IMPLEMENTED;
-        goto end;
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_power(DeviceObject, Irp);
+
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto exit;
+    } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+        IoSkipCurrentIrpStackLocation(Irp);
+
+        Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
+
+        goto exit;
     }
-    
-    IoSkipCurrentIrpStackLocation(Irp);
-    
-    Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
-    
-    goto end2;
-    
-end:
+
+    Status = STATUS_INVALID_DEVICE_REQUEST;
     Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
-    if (Status != STATUS_PENDING)
-        IoCompleteRequest(Irp, IO_NO_INCREMENT);
-    
-end2:
-    if (top_level) 
+exit:
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
-    BOOL top_level;
     device_extension* Vcb = DeviceObject->DeviceExtension;
+    BOOL top_level;
 
-    TRACE("shutdown\n");
-    
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
-    
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+
+    Irp->IoStatus.Information = 0;
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        volume_device_extension* vde = DeviceObject->DeviceExtension;
+
+        IoSkipCurrentIrpStackLocation(Irp);
+
+        Status = IoCallDriver(vde->pdo, Irp);
+
         goto exit;
-    }    
-    
-    Status = STATUS_SUCCESS;
+    } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+        IoSkipCurrentIrpStackLocation(Irp);
 
-    while (!IsListEmpty(&VcbList)) {
-        LIST_ENTRY* le = RemoveHeadList(&VcbList);
-        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
-        
-        TRACE("shutting down Vcb %p\n", Vcb);
-        
-        uninit(Vcb, TRUE);
+        Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
+
+        goto exit;
     }
-    
-    Irp->IoStatus.Status = Status;
-    Irp->IoStatus.Information = 0;
 
-    IoCompleteRequest( Irp, IO_NO_INCREMENT );
+    Status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
 exit:
-    if (top_level) 
+    if (top_level)
         IoSetTopLevelIrp(NULL);
-    
+
     FsRtlExitFileSystem();
 
     return Status;
 }
 
-BOOL is_file_name_valid(PUNICODE_STRING us) {
+BOOL is_file_name_valid(_In_ PUNICODE_STRING us, _In_ BOOL posix) {
     ULONG i;
-    
+
     if (us->Length < sizeof(WCHAR))
         return FALSE;
-    
+
     if (us->Length > 255 * sizeof(WCHAR))
         return FALSE;
-    
+
     for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
-        if (us->Buffer[i] == '/' || us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
-            us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))
+        if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
+            (!posix && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
+            us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
             return FALSE;
     }
-    
+
     if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
         return FALSE;
-    
+
     return TRUE;
 }
 
+void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
+    LIST_ENTRY* le;
+    BOOL locked;
+    range_lock* rl;
+
+    rl = ExAllocateFromNPagedLookasideList(&Vcb->range_lock_lookaside);
+    if (!rl) {
+        ERR("out of memory\n");
+        return;
+    }
+
+    rl->start = start;
+    rl->length = length;
+    rl->thread = PsGetCurrentThread();
+
+    while (TRUE) {
+        locked = FALSE;
+
+        ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
+
+        le = c->range_locks.Flink;
+        while (le != &c->range_locks) {
+            range_lock* rl2 = CONTAINING_RECORD(le, range_lock, list_entry);
+
+            if (rl2->start < start + length && rl2->start + rl2->length > start && rl2->thread != PsGetCurrentThread()) {
+                locked = TRUE;
+                break;
+            }
+
+            le = le->Flink;
+        }
+
+        if (!locked) {
+            InsertTailList(&c->range_locks, &rl->list_entry);
+
+            ExReleaseResourceLite(&c->range_locks_lock);
+            return;
+        }
+
+        KeClearEvent(&c->range_locks_event);
+
+        ExReleaseResourceLite(&c->range_locks_lock);
+
+        KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, FALSE, NULL);
+    }
+}
+
+void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
+    LIST_ENTRY* le;
+
+    ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
+
+    le = c->range_locks.Flink;
+    while (le != &c->range_locks) {
+        range_lock* rl = CONTAINING_RECORD(le, range_lock, list_entry);
+
+        if (rl->start == start && rl->length == length) {
+            RemoveEntryList(&rl->list_entry);
+            ExFreeToNPagedLookasideList(&Vcb->range_lock_lookaside, rl);
+            break;
+        }
+
+        le = le->Flink;
+    }
+
+    KeSetEvent(&c->range_locks_event, 0, FALSE);
+
+    ExReleaseResourceLite(&c->range_locks_lock);
+}
+
+void log_device_error(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ int error) {
+    dev->stats[error]++;
+    dev->stats_changed = TRUE;
+    Vcb->stats_changed = TRUE;
+}
+
 #ifdef _DEBUG
-static void STDCALL init_serial() {
+_Function_class_(KSTART_ROUTINE)
+static void serial_thread(void* context) {
+    LARGE_INTEGER due_time;
+    KTIMER timer;
+
+    UNUSED(context);
+
+    KeInitializeTimer(&timer);
+
+    due_time.QuadPart = (UINT64)-10000000;
+
+    KeSetTimer(&timer, due_time, NULL);
+
+    while (TRUE) {
+        KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
+
+        init_serial(FALSE);
+
+        if (comdo)
+            break;
+
+        KeSetTimer(&timer, due_time, NULL);
+    }
+
+    KeCancelTimer(&timer);
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+
+    serial_thread_handle = NULL;
+}
+
+static void init_serial(BOOL first_time) {
     NTSTATUS Status;
-    
+
     Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
     if (!NT_SUCCESS(Status)) {
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+
+        if (first_time) {
+            NTSTATUS Status;
+
+            Status = PsCreateSystemThread(&serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, NULL);
+            if (!NT_SUCCESS(Status)) {
+                ERR("PsCreateSystemThread returned %08x\n", Status);
+                return;
+            }
+        }
     }
 }
 #endif
 
 #ifndef __REACTOS__
-static void STDCALL check_cpu() {
+static void check_cpu() {
     unsigned int cpuInfo[4];
 #ifndef _MSC_VER
     __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
     have_sse42 = cpuInfo[2] & bit_SSE4_2;
+    have_sse2 = cpuInfo[3] & bit_SSE2;
 #else
    __cpuid(cpuInfo, 1);
    have_sse42 = cpuInfo[2] & (1 << 20);
+   have_sse2 = cpuInfo[3] & (1 << 26);
 #endif
 
     if (have_sse42)
         TRACE("SSE4.2 is supported\n");
     else
         TRACE("SSE4.2 not supported\n");
+
+    if (have_sse2)
+        TRACE("SSE2 is supported\n");
+    else
+        TRACE("SSE2 is not supported\n");
 }
 #endif
 
 #ifdef _DEBUG
 static void init_logging() {
+    ExAcquireResourceExclusiveLite(&log_lock, TRUE);
+
     if (log_device.Length > 0)
-        init_serial();
+        init_serial(TRUE);
     else if (log_file.Length > 0) {
         NTSTATUS Status;
         OBJECT_ATTRIBUTES oa;
@@ -4202,118 +5169,308 @@ static void init_logging() {
         char* dateline;
         LARGE_INTEGER time;
         TIME_FIELDS tf;
-        
+
         InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-        
+
         Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
                               FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
-        
+
         if (!NT_SUCCESS(Status)) {
             ERR("ZwCreateFile returned %08x\n", Status);
-            return;
+            goto end;
         }
-        
+
         if (iosb.Information == FILE_OPENED) { // already exists
             FILE_STANDARD_INFORMATION fsi;
             FILE_POSITION_INFORMATION fpi;
-            
+
             static char delim[] = "\n---\n";
-            
+
             // move to end of file
-            
+
             Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
-            
+
             if (!NT_SUCCESS(Status)) {
                 ERR("ZwQueryInformationFile returned %08x\n", Status);
-                return;
+                goto end;
             }
-            
+
             fpi.CurrentByteOffset = fsi.EndOfFile;
-            
+
             Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
-            
+
             if (!NT_SUCCESS(Status)) {
                 ERR("ZwSetInformationFile returned %08x\n", Status);
-                return;
+                goto end;
             }
 
-            Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, strlen(delim), NULL, NULL);
-        
+            Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, (ULONG)strlen(delim), NULL, NULL);
+
             if (!NT_SUCCESS(Status)) {
                 ERR("ZwWriteFile returned %08x\n", Status);
-                return;
+                goto end;
             }
         }
-        
+
         dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
-        
+
         if (!dateline) {
             ERR("out of memory\n");
-            return;
+            goto end;
         }
-        
+
         KeQuerySystemTime(&time);
-        
+
         RtlTimeToTimeFields(&time, &tf);
-        
-        sprintf(dateline, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
 
-        Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, strlen(dateline), NULL, NULL);
-        
+        sprintf(dateline, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
+
+        Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, (ULONG)strlen(dateline), NULL, NULL);
+
+        ExFreePool(dateline);
+
         if (!NT_SUCCESS(Status)) {
             ERR("ZwWriteFile returned %08x\n", Status);
-            return;
+            goto end;
         }
-        
-        ExFreePool(dateline);
     }
+
+end:
+    ExReleaseResourceLite(&log_lock);
+}
+#endif
+
+_Function_class_(KSTART_ROUTINE)
+#ifdef __REACTOS__
+static void NTAPI degraded_wait_thread(_In_ void* context) {
+#else
+static void degraded_wait_thread(_In_ void* context) {
+#endif
+    KTIMER timer;
+    LARGE_INTEGER delay;
+
+    UNUSED(context);
+
+    KeInitializeTimer(&timer);
+
+    delay.QuadPart = -30000000; // wait three seconds
+    KeSetTimer(&timer, delay, NULL);
+    KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
+
+    TRACE("timer expired\n");
+
+    degraded_wait = FALSE;
+
+    ZwClose(degraded_wait_handle);
+    degraded_wait_handle = NULL;
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
 }
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
+#else
+NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
 #endif
+    LIST_ENTRY* le;
+    NTSTATUS Status;
+    UNICODE_STRING volname;
+    ULONG i, j;
+    pdo_device_extension* pdode = NULL;
+    PDEVICE_OBJECT voldev;
+    volume_device_extension* vde;
+
+    TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
+
+    ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
+
+    le = pdo_list.Flink;
+    while (le != &pdo_list) {
+        pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+        if (pdode2->pdo == PhysicalDeviceObject) {
+            pdode = pdode2;
+            break;
+        }
+
+        le = le->Flink;
+    }
+
+    if (!pdode) {
+        WARN("unrecognized PDO %p\n", PhysicalDeviceObject);
+        Status = STATUS_NOT_SUPPORTED;
+        goto end;
+    }
+
+    ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+    volname.Length = volname.MaximumLength = (USHORT)((wcslen(BTRFS_VOLUME_PREFIX) + 36 + 1) * sizeof(WCHAR));
+    volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
+
+    if (!volname.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end2;
+    }
+
+    RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR));
+
+    j = (ULONG)wcslen(BTRFS_VOLUME_PREFIX);
+    for (i = 0; i < 16; i++) {
+        volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] >> 4); j++;
+        volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] & 0xf); j++;
+
+        if (i == 3 || i == 5 || i == 7 || i == 9) {
+            volname.Buffer[j] = '-';
+            j++;
+        }
+    }
+
+    volname.Buffer[j] = '}';
+
+    Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
+                            RtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, FALSE, &voldev);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoCreateDevice returned %08x\n", Status);
+        goto end2;
+    }
+
+    voldev->SectorSize = PhysicalDeviceObject->SectorSize;
+    voldev->Flags |= DO_DIRECT_IO;
+
+    vde = voldev->DeviceExtension;
+    vde->type = VCB_TYPE_VOLUME;
+    vde->name = volname;
+    vde->device = voldev;
+    vde->mounted_device = NULL;
+    vde->pdo = PhysicalDeviceObject;
+    vde->pdode = pdode;
+    vde->removing = FALSE;
+    vde->open_count = 0;
+
+    Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
+    if (!NT_SUCCESS(Status))
+        WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+
+    vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
+
+    pdode->vde = vde;
+
+    if (pdode->removable)
+        voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
 
-NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
+    voldev->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    Status = IoSetDeviceInterfaceState(&vde->bus_name, TRUE);
+    if (!NT_SUCCESS(Status))
+        WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+
+    Status = STATUS_SUCCESS;
+
+end2:
+    ExReleaseResourceLite(&pdode->child_lock);
+
+end:
+    ExReleaseResourceLite(&pdo_list_lock);
+
+    return Status;
+}
+
+_Function_class_(DRIVER_INITIALIZE)
+#ifdef __REACTOS__
+NTSTATUS NTAPI DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
+#else
+NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
+#endif
     NTSTATUS Status;
     PDEVICE_OBJECT DeviceObject;
     UNICODE_STRING device_nameW;
     UNICODE_STRING dosdevice_nameW;
-    
+    control_device_extension* cde;
+    HANDLE regh;
+    OBJECT_ATTRIBUTES oa;
+    ULONG dispos;
+
     InitializeListHead(&uid_map_list);
-    
+    InitializeListHead(&gid_map_list);
+
+#ifdef _DEBUG
+    ExInitializeResourceLite(&log_lock);
+#endif
+    ExInitializeResourceLite(&mapping_lock);
+
     log_device.Buffer = NULL;
     log_device.Length = log_device.MaximumLength = 0;
     log_file.Buffer = NULL;
     log_file.Length = log_file.MaximumLength = 0;
-    
-    read_registry(RegistryPath);
-    
-#ifdef _DEBUG
-    if (debug_log_level > 0)
-        init_logging();
-    
-    log_started = TRUE;
-#endif
 
-    TRACE("DriverEntry\n");
-    
     registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
     registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
-    
+
     if (!registry_path.Buffer) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    
+
     RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
-   
+
+    read_registry(&registry_path, FALSE);
+
+#ifdef _DEBUG
+    if (debug_log_level > 0)
+        init_logging();
+
+    log_started = TRUE;
+#endif
+
+    TRACE("DriverEntry\n");
+
 #ifndef __REACTOS__
     check_cpu();
 #endif
-   
-//    TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
-   
+
+    if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8)) {
+        UNICODE_STRING name;
+        tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled;
+
+        RtlInitUnicodeString(&name, L"PsIsDiskCountersEnabled");
+        fPsIsDiskCountersEnabled = (tPsIsDiskCountersEnabled)MmGetSystemRoutineAddress(&name);
+
+        if (fPsIsDiskCountersEnabled) {
+            diskacc = fPsIsDiskCountersEnabled();
+
+            RtlInitUnicodeString(&name, L"PsUpdateDiskCounters");
+            fPsUpdateDiskCounters = (tPsUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
+
+            if (!fPsUpdateDiskCounters)
+                diskacc = FALSE;
+
+            RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
+            fFsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
+        }
+
+        RtlInitUnicodeString(&name, L"CcCopyReadEx");
+        fCcCopyReadEx = (tCcCopyReadEx)MmGetSystemRoutineAddress(&name);
+
+        RtlInitUnicodeString(&name, L"CcCopyWriteEx");
+        fCcCopyWriteEx = (tCcCopyWriteEx)MmGetSystemRoutineAddress(&name);
+
+        RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
+        fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
+    } else {
+        fPsUpdateDiskCounters = NULL;
+        fCcCopyReadEx = NULL;
+        fCcCopyWriteEx = NULL;
+        fCcSetAdditionalCacheAttributesEx = NULL;
+        fFsRtlUpdateDiskCounters = NULL;
+    }
+
     drvobj = DriverObject;
 
     DriverObject->DriverUnload = DriverUnload;
 
+    DriverObject->DriverExtension->AddDevice = AddDevice;
+
     DriverObject->MajorFunction[IRP_MJ_CREATE]                   = (PDRIVER_DISPATCH)drv_create;
     DriverObject->MajorFunction[IRP_MJ_CLOSE]                    = (PDRIVER_DISPATCH)drv_close;
     DriverObject->MajorFunction[IRP_MJ_READ]                     = (PDRIVER_DISPATCH)drv_read;
@@ -4325,15 +5482,17 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]            = (PDRIVER_DISPATCH)drv_flush_buffers;
     DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
     DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION]   = (PDRIVER_DISPATCH)drv_set_volume_information;
-    DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = (PDRIVER_DISPATCH)drv_cleanup;
     DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL]        = (PDRIVER_DISPATCH)drv_directory_control;
     DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL]      = (PDRIVER_DISPATCH)drv_file_system_control;
-    DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = (PDRIVER_DISPATCH)drv_lock_control;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]           = (PDRIVER_DISPATCH)drv_device_control;
     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]                 = (PDRIVER_DISPATCH)drv_shutdown;
-    DriverObject->MajorFunction[IRP_MJ_PNP]                      = (PDRIVER_DISPATCH)drv_pnp;
+    DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = (PDRIVER_DISPATCH)drv_lock_control;
+    DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = (PDRIVER_DISPATCH)drv_cleanup;
     DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY]           = (PDRIVER_DISPATCH)drv_query_security;
     DriverObject->MajorFunction[IRP_MJ_SET_SECURITY]             = (PDRIVER_DISPATCH)drv_set_security;
+    DriverObject->MajorFunction[IRP_MJ_POWER]                    = (PDRIVER_DISPATCH)drv_power;
+    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]           = (PDRIVER_DISPATCH)drv_system_control;
+    DriverObject->MajorFunction[IRP_MJ_PNP]                      = (PDRIVER_DISPATCH)drv_pnp;
 
     init_fast_io_dispatch(&DriverObject->FastIoDispatch);
 
@@ -4342,14 +5501,20 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
     dosdevice_nameW.Buffer = dosdevice_name;
     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
 
-    Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
+    Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
+                            FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
     if (!NT_SUCCESS(Status)) {
         ERR("IoCreateDevice returned %08x\n", Status);
         return Status;
     }
-    
-    devobj = DeviceObject;
-    
+
+    master_devobj = DeviceObject;
+    cde = (control_device_extension*)master_devobj->DeviceExtension;
+
+    RtlZeroMemory(cde, sizeof(control_device_extension));
+
+    cde->type = VCB_TYPE_CONTROL;
+
     DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
 
     Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
@@ -4357,19 +5522,78 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
         ERR("IoCreateSymbolicLink returned %08x\n", Status);
         return Status;
     }
-    
+
     Status = init_cache();
     if (!NT_SUCCESS(Status)) {
         ERR("init_cache returned %08x\n", Status);
         return Status;
     }
 
-    InitializeListHead(&volumes);
-    look_for_vols(DriverObject, &volumes);
-    
     InitializeListHead(&VcbList);
     ExInitializeResourceLite(&global_loading_lock);
-    
+    ExInitializeResourceLite(&pdo_list_lock);
+
+    InitializeListHead(&pdo_list);
+
+    InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+    Status = ZwCreateKey(&regh, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+    if (!NT_SUCCESS(Status)) {
+        ERR("ZwCreateKey returned %08x\n", Status);
+        return Status;
+    }
+
+    watch_registry(regh);
+
+    Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
+                                    NULL, NULL, 0, &cde->buspdo);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoReportDetectedDevice returned %08x\n", Status);
+        return Status;
+    }
+
+    Status = IoRegisterDeviceInterface(cde->buspdo, &BtrfsBusInterface, NULL, &cde->bus_name);
+    if (!NT_SUCCESS(Status))
+        WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+
+    cde->attached_device = IoAttachDeviceToDeviceStack(DeviceObject, cde->buspdo);
+
+    Status = IoSetDeviceInterfaceState(&cde->bus_name, TRUE);
+    if (!NT_SUCCESS(Status))
+        WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+
+    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    IoInvalidateDeviceRelations(cde->buspdo, BusRelations);
+
+    Status = PsCreateSystemThread(&degraded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL);
+    if (!NT_SUCCESS(Status))
+        WARN("PsCreateSystemThread returned %08x\n", Status);
+
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
+    if (!NT_SUCCESS(Status))
+        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
+    if (!NT_SUCCESS(Status))
+        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
+    if (!NT_SUCCESS(Status))
+        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+    finished_probing = TRUE;
+
+    KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, FALSE);
+
+#ifndef __REACTOS__
+    Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, NULL, NULL, NULL, mountmgr_thread, NULL);
+    if (!NT_SUCCESS(Status))
+        WARN("PsCreateSystemThread returned %08x\n", Status);
+#endif
+
     IoRegisterFileSystem(DeviceObject);
 
     return STATUS_SUCCESS;