[BTRFS] Upgrade to 1.2.1
authorPierre Schweitzer <pierre@reactos.org>
Sat, 11 May 2019 09:20:02 +0000 (11:20 +0200)
committerPierre Schweitzer <pierre@reactos.org>
Sat, 11 May 2019 09:20:02 +0000 (11:20 +0200)
CORE-16004

16 files changed:
drivers/filesystems/btrfs/balance.c
drivers/filesystems/btrfs/btrfs.c
drivers/filesystems/btrfs/btrfs.rc
drivers/filesystems/btrfs/btrfs_drv.h
drivers/filesystems/btrfs/create.c
drivers/filesystems/btrfs/dirctrl.c
drivers/filesystems/btrfs/fileinfo.c
drivers/filesystems/btrfs/flushthread.c
drivers/filesystems/btrfs/free-space.c
drivers/filesystems/btrfs/fsctl.c
drivers/filesystems/btrfs/pnp.c
drivers/filesystems/btrfs/read.c
drivers/filesystems/btrfs/resource.h
drivers/filesystems/btrfs/search.c
drivers/filesystems/btrfs/treefuncs.c
drivers/filesystems/btrfs/volume.c

index fe641f4..83546f0 100644 (file)
@@ -2108,8 +2108,6 @@ end:
         // update open FCBs
         // FIXME - speed this up(?)
 
-        acquire_fcb_lock_shared(Vcb);
-
         le = Vcb->all_fcbs.Flink;
         while (le != &Vcb->all_fcbs) {
             struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
@@ -2148,8 +2146,6 @@ end:
 
             le = le->Flink;
         }
-
-        release_fcb_lock(Vcb);
     } else
         do_rollback(Vcb, &rollback);
 
index 2582ee0..4cd6bb1 100644 (file)
@@ -1036,6 +1036,8 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
+        t->nonpaged = NULL;
+
         t->is_unique = TRUE;
         t->uniqueness_determined = TRUE;
         t->buf = NULL;
@@ -1065,7 +1067,9 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
     r->send_ops = 0;
     RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
     r->root_item.num_references = 1;
+    r->fcbs_version = 0;
     InitializeListHead(&r->fcbs);
+    RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
     RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
 
@@ -1391,7 +1395,7 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
         return;
     }
 
-    acquire_fcb_lock_exclusive(fcb->Vcb);
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
 
     le = fcb->hardlinks.Flink;
     while (le != &fcb->hardlinks) {
@@ -1410,7 +1414,7 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
             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);
+                free_fileref(parfr);
                 break;
             }
 
@@ -1419,7 +1423,7 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
 
             if (pathlen + hl->name.Length > 0xffff) {
                 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
-                free_fileref(fcb->Vcb, parfr);
+                free_fileref(parfr);
                 break;
             }
 
@@ -1427,14 +1431,14 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
             fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
             if (!fn.Buffer) {
                 ERR("out of memory\n");
-                free_fileref(fcb->Vcb, parfr);
+                free_fileref(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);
+                free_fileref(parfr);
                 ExFreePool(fn.Buffer);
                 break;
             }
@@ -1452,13 +1456,13 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
 
             ExFreePool(fn.Buffer);
 
-            free_fileref(fcb->Vcb, parfr);
+            free_fileref(parfr);
         }
 
         le = le->Flink;
     }
 
-    release_fcb_lock(fcb->Vcb);
+    ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
 }
 
 void mark_fcb_dirty(_In_ fcb* fcb) {
@@ -1497,24 +1501,31 @@ void mark_fileref_dirty(_In_ file_ref* fileref) {
 }
 
 #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) {
+void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func) {
+    LONG rc = InterlockedDecrement(&fcb->refcount);
 #else
-void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb) {
+void free_fcb(_Inout_ fcb* fcb) {
+    InterlockedDecrement(&fcb->refcount);
 #endif
-    LONG rc;
-
-    rc = InterlockedDecrement(&fcb->refcount);
 
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
-    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #else
-    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p (%s): refcount now %i (subvol %llx, inode %llx)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #endif
 #endif
+}
 
-    if (rc > 0)
-        return;
+void reap_fcb(fcb* fcb) {
+    UINT8 c = fcb->hash >> 24;
+
+    if (fcb->subvol && fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
+        if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
+            fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
+        else
+            fcb->subvol->fcbs_ptrs[c] = NULL;
+    }
 
     if (fcb->list_entry.Flink)
         RemoveEntryList(&fcb->list_entry);
@@ -1526,7 +1537,7 @@ void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_exten
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
     ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
 
-    ExFreeToNPagedLookasideList(&Vcb->fcb_np_lookaside, fcb->nonpaged);
+    ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
 
     if (fcb->sd)
         ExFreePool(fcb->sd);
@@ -1596,21 +1607,31 @@ void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_exten
     if (fcb->pool_type == NonPagedPool)
         ExFreePool(fcb);
     else
-        ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
+        ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
+}
 
-#ifdef DEBUG_FCB_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "freeing fcb %p\n", fcb);
-#else
-    _debug_message(func, "freeing fcb %p\n", fcb);
-#endif
-#endif
+void reap_fcbs(device_extension* Vcb) {
+    LIST_ENTRY* le;
+
+    le = Vcb->all_fcbs.Flink;
+    while (le != &Vcb->all_fcbs) {
+        fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
+        LIST_ENTRY* le2 = le->Flink;
+
+        if (fcb->refcount == 0)
+            reap_fcb(fcb);
+
+        le = le2;
+    }
 }
 
-void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ file_ref* fr) {
+void free_fileref(_Inout_ file_ref* fr) {
     LONG rc;
 
     rc = InterlockedDecrement(&fr->refcount);
+#ifdef __REACTOS__
+    (void)rc;
+#endif
 
 #ifdef DEBUG_FCB_REFCOUNTS
     ERR("fileref %p: refcount now %i\n", fr, rc);
@@ -1622,13 +1643,9 @@ void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_e
         int3;
     }
 #endif
+}
 
-    if (rc > 0)
-        return;
-
-    if (fr->parent)
-        ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
-
+void reap_fileref(device_extension* Vcb, file_ref* fr) {
     // FIXME - do we need a file_ref lock?
 
     // FIXME - do delete if needed
@@ -1636,7 +1653,6 @@ void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_e
     if (fr->debug_desc)
         ExFreePool(fr->debug_desc);
 
-    ExDeleteResourceLite(&fr->nonpaged->children_lock);
     ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
 
     ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
@@ -1656,22 +1672,38 @@ void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_e
     if (fr->list_entry.Flink)
         RemoveEntryList(&fr->list_entry);
 
-    if (fr->parent) {
-        ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
-        free_fileref(Vcb, fr->parent);
-    }
+    if (fr->parent)
+        free_fileref(fr->parent);
 
-    free_fcb(Vcb, fr->fcb);
+    free_fcb(fr->fcb);
 
     ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
 }
 
+void reap_filerefs(device_extension* Vcb, file_ref* fr) {
+    LIST_ENTRY* le;
+
+    // FIXME - recursion is a bad idea in kernel mode
+
+    le = fr->children.Flink;
+    while (le != &fr->children) {
+        file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
+        LIST_ENTRY* le2 = le->Flink;
+
+        reap_filerefs(Vcb, c);
+
+        le = le2;
+    }
+
+    if (fr->refcount == 0)
+        reap_fileref(Vcb, fr);
+}
+
 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);
 
@@ -1735,16 +1767,10 @@ static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
     if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
         return STATUS_SUCCESS;
 
-    Vcb = fcb->Vcb;
-
-    acquire_fcb_lock_exclusive(Vcb);
-
     if (fileref)
-        free_fileref(fcb->Vcb, fileref);
+        free_fileref(fileref);
     else
-        free_fcb(Vcb, fcb);
-
-    release_fcb_lock(Vcb);
+        free_fcb(fcb);
 
     return STATUS_SUCCESS;
 }
@@ -1835,10 +1861,8 @@ void uninit(_In_ device_extension* Vcb) {
     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
 
-    acquire_fcb_lock_exclusive(Vcb);
-    free_fcb(Vcb, Vcb->volume_fcb);
-    free_fcb(Vcb, Vcb->dummy_fcb);
-    release_fcb_lock(Vcb);
+    reap_fcb(Vcb->volume_fcb);
+    reap_fcb(Vcb->dummy_fcb);
 
     if (Vcb->root_file)
         ObDereferenceObject(Vcb->root_file);
@@ -1848,9 +1872,7 @@ void uninit(_In_ device_extension* Vcb) {
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
 
         if (c->cache) {
-            acquire_fcb_lock_exclusive(Vcb);
-            free_fcb(Vcb, c->cache);
-            release_fcb_lock(Vcb);
+            reap_fcb(c->cache);
             c->cache = NULL;
         }
 
@@ -1885,11 +1907,8 @@ void uninit(_In_ device_extension* Vcb) {
         if (c->devices)
             ExFreePool(c->devices);
 
-        if (c->cache) {
-            acquire_fcb_lock_exclusive(Vcb);
-            free_fcb(Vcb, c->cache);
-            release_fcb_lock(Vcb);
-        }
+        if (c->cache)
+            reap_fcb(c->cache);
 
         ExDeleteResourceLite(&c->range_locks_lock);
         ExDeleteResourceLite(&c->partial_stripes_lock);
@@ -1924,6 +1943,7 @@ void uninit(_In_ device_extension* Vcb) {
     ExReleaseResourceLite(&Vcb->scrub.stats_lock);
 
     ExDeleteResourceLite(&Vcb->fcb_lock);
+    ExDeleteResourceLite(&Vcb->fileref_lock);
     ExDeleteResourceLite(&Vcb->load_lock);
     ExDeleteResourceLite(&Vcb->tree_lock);
     ExDeleteResourceLite(&Vcb->chunk_lock);
@@ -2269,21 +2289,19 @@ static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
                     ExReleaseResourceLite(fcb->Header.Resource);
                     locked = FALSE;
 
-                    // fcb_lock needs to be acquired before fcb->Header.Resource
-                    acquire_fcb_lock_exclusive(fcb->Vcb);
+                    // fileref_lock needs to be acquired before fcb->Header.Resource
+                    ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
 
                     Status = delete_fileref(fileref, FileObject, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("delete_fileref returned %08x\n", Status);
                         do_rollback(fcb->Vcb, &rollback);
-                        release_fcb_lock(fcb->Vcb);
+                        ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
                         goto exit;
                     }
 
-                    release_fcb_lock(fcb->Vcb);
-
-                    locked = FALSE;
+                    ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
 
                     clear_rollback(&rollback);
                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
@@ -2658,7 +2676,9 @@ static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ UINT64 id, _In_ UIN
     r->treeholder.generation = generation;
     r->parent = 0;
     r->send_ops = 0;
+    r->fcbs_version = 0;
     InitializeListHead(&r->fcbs);
+    RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
     if (!r->nonpaged) {
@@ -4132,6 +4152,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     Vcb->need_write = FALSE;
 
     ExInitializeResourceLite(&Vcb->fcb_lock);
+    ExInitializeResourceLite(&Vcb->fileref_lock);
     ExInitializeResourceLite(&Vcb->chunk_lock);
     ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
     ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
@@ -4260,6 +4281,8 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     InitializeListHead(&Vcb->dirty_subvols);
     InitializeListHead(&Vcb->send_ops);
 
+    ExInitializeFastMutex(&Vcb->trees_list_mutex);
+
     InitializeListHead(&Vcb->DirNotifyList);
     InitializeListHead(&Vcb->scrub.errors);
 
@@ -4424,6 +4447,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     root_fcb->Vcb = Vcb;
     root_fcb->inode = SUBVOL_ROOT_INODE;
+    root_fcb->hash = calc_crc32c(0xffffffff, (UINT8*)&root_fcb->inode, sizeof(UINT64));
     root_fcb->type = BTRFS_TYPE_DIRECTORY;
 
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -4479,6 +4503,8 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
     InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
 
+    root_fcb->subvol->fcbs_ptrs[root_fcb->hash >> 24] = &root_fcb->list_entry;
+
     root_fcb->fileref = Vcb->root_fileref;
 
     root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
@@ -4596,25 +4622,21 @@ exit2:
 
             if (Vcb->root_file)
                 ObDereferenceObject(Vcb->root_file);
-            else if (Vcb->root_fileref) {
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, Vcb->root_fileref);
-                release_fcb_lock(Vcb);
-            } else if (root_fcb) {
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fcb(Vcb, root_fcb);
-                release_fcb_lock(Vcb);
-            }
+            else if (Vcb->root_fileref)
+                free_fileref(Vcb->root_fileref);
+            else if (root_fcb)
+                free_fcb(root_fcb);
 
-            if (Vcb->volume_fcb) {
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fcb(Vcb, Vcb->volume_fcb);
-                release_fcb_lock(Vcb);
-            }
+            if (root_fcb && root_fcb->refcount == 0)
+                reap_fcb(root_fcb);
+
+            if (Vcb->volume_fcb)
+                reap_fcb(Vcb->volume_fcb);
 
             ExDeleteResourceLite(&Vcb->tree_lock);
             ExDeleteResourceLite(&Vcb->load_lock);
             ExDeleteResourceLite(&Vcb->fcb_lock);
+            ExDeleteResourceLite(&Vcb->fileref_lock);
             ExDeleteResourceLite(&Vcb->chunk_lock);
             ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
             ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
index 957792a..d10323e 100644 (file)
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
-// English (U.K.) resources
+// English (United Kingdom) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
-#ifdef _WIN32
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 #pragma code_page(1252)
-#endif //_WIN32
 
 #ifdef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
@@ -27,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 // TEXTINCLUDE
 //
 
-1 TEXTINCLUDE
+1 TEXTINCLUDE 
 BEGIN
     "resource.h\0"
 END
 
-2 TEXTINCLUDE
+2 TEXTINCLUDE 
 BEGIN
     "#include ""winres.h""\r\n"
     "\0"
 END
 
-3 TEXTINCLUDE
+3 TEXTINCLUDE 
 BEGIN
     "\r\n"
     "\0"
@@ -53,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,1,0,0
- PRODUCTVERSION 1,1,0,0
+ FILEVERSION 1,2,1,0
+ PRODUCTVERSION 1,2,1,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -70,12 +68,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "1.1"
+            VALUE "FileVersion", "1.2.1"
             VALUE "InternalName", "btrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-18"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.1"
+            VALUE "ProductVersion", "1.2.1"
         END
     END
     BLOCK "VarFileInfo"
@@ -84,7 +82,7 @@ BEGIN
     END
 END
 
-#endif    // English (U.K.) resources
+#endif    // English (United Kingdom) resources
 /////////////////////////////////////////////////////////////////////////////
 
 
index 3ccb10a..b41c979 100644 (file)
@@ -260,6 +260,7 @@ typedef struct _fcb {
     struct _device_extension* Vcb;
     struct _root* subvol;
     UINT64 inode;
+    UINT32 hash;
     UINT8 type;
     INODE_ITEM inode_item;
     SECURITY_DESCRIPTOR* sd;
@@ -309,7 +310,6 @@ typedef struct _fcb {
 
 typedef struct {
     ERESOURCE fileref_lock;
-    ERESOURCE children_lock;
 } file_ref_nonpaged;
 
 typedef struct _file_ref {
@@ -392,7 +392,12 @@ typedef struct _tree_data {
     };
 } tree_data;
 
+typedef struct {
+    FAST_MUTEX mutex;
+} tree_nonpaged;
+
 typedef struct _tree {
+    tree_nonpaged* nonpaged;
     tree_header header;
     UINT32 hash;
     BOOL has_address;
@@ -428,7 +433,9 @@ typedef struct _root {
     PEPROCESS reserved;
     UINT64 parent;
     LONG send_ops;
+    UINT64 fcbs_version;
     LIST_ENTRY fcbs;
+    LIST_ENTRY* fcbs_ptrs[256];
     LIST_ENTRY list_entry;
     LIST_ENTRY list_entry_dirty;
 } root;
@@ -742,6 +749,7 @@ typedef struct _device_extension {
     file_ref* root_fileref;
     LONG open_files;
     _Has_lock_level_(fcb_lock) ERESOURCE fcb_lock;
+    ERESOURCE fileref_lock;
     ERESOURCE load_lock;
     _Has_lock_level_(tree_lock) ERESOURCE tree_lock;
     PNOTIFY_SYNC NotifySync;
@@ -768,6 +776,7 @@ typedef struct _device_extension {
     LIST_ENTRY trees;
     LIST_ENTRY trees_hash;
     LIST_ENTRY* trees_ptrs[256];
+    FAST_MUTEX trees_list_mutex;
     LIST_ENTRY all_fcbs;
     LIST_ENTRY dirty_fcbs;
     ERESOURCE dirty_fcbs_lock;
@@ -1087,9 +1096,9 @@ BOOL get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
                _Out_ UINT8** data, _Out_ UINT16* datalen, _In_opt_ PIRP Irp);
 
 #ifndef DEBUG_FCB_REFCOUNTS
-void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb);
+void free_fcb(_Inout_ fcb* fcb);
 #endif
-void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ file_ref* fr);
+void free_fileref(_Inout_ file_ref* fr);
 void protect_superblocks(_Inout_ chunk* c);
 BOOL is_top_level(_In_ PIRP Irp);
 NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ UINT64 id,
@@ -1129,6 +1138,10 @@ NTSTATUS NTAPI AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDev
 #else
 NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject);
 #endif
+void reap_fcb(fcb* fcb);
+void reap_fcbs(device_extension* Vcb);
+void reap_fileref(device_extension* Vcb, file_ref* fr);
+void reap_filerefs(device_extension* Vcb, file_ref* fr);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -1192,8 +1205,8 @@ void _debug_message(_In_ const char* func, _In_ char* s, ...);
 #endif
 
 #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);
-#define free_fcb(Vcb, fcb) _free_fcb(Vcb, fcb, funcname)
+void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func);
+#define free_fcb(fcb) _free_fcb(fcb, funcname)
 #endif
 
 // in fastio.c
@@ -1251,9 +1264,9 @@ NTSTATUS insert_tree_item(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock)
                           _In_ UINT8 obj_type, _In_ UINT64 offset, _In_reads_bytes_opt_(size) _When_(return >= 0, __drv_aliasesMem) void* data,
                           _In_ UINT16 size, _Out_opt_ traverse_ptr* ptp, _In_opt_ PIRP Irp);
 NTSTATUS delete_tree_item(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _Inout_ traverse_ptr* tp);
-tree* free_tree(tree* t);
-NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT64 generation, PIRP Irp);
-NTSTATUS do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp);
+void free_tree(tree* t);
+NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, UINT8* buf, root* r, tree** pt);
+NTSTATUS do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, PIRP Irp);
 void clear_rollback(LIST_ENTRY* rollback);
 void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
 void free_trees_root(device_extension* Vcb, root* r);
@@ -1380,7 +1393,6 @@ NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 BOOL has_open_children(file_ref* fileref);
 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, UINT16 end, fcb* fcb, file_ref* fileref, BOOL advance_only);
 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen);
-NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc);
 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc);
 
@@ -1411,6 +1423,7 @@ fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type);
 NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive);
 UINT32 inherit_mode(fcb* parfcb, BOOL is_dir);
 file_ref* create_fileref(device_extension* Vcb);
+NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
 
 // in fsctl.c
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, UINT32 type);
index 2cde237..089b19c 100644 (file)
@@ -137,7 +137,6 @@ file_ref* create_fileref(device_extension* Vcb) {
     InitializeListHead(&fr->children);
 
     ExInitializeResourceLite(&fr->nonpaged->fileref_lock);
-    ExInitializeResourceLite(&fr->nonpaged->children_lock);
 
     return fr;
 }
@@ -282,11 +281,17 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENT
     BOOL has_stream;
     WCHAR* buf;
     name_bit* nb;
+    NTSTATUS Status;
 
     len = path->Length / sizeof(WCHAR);
     if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
         len--;
 
+    if (len == 0 || (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) {
+        WARN("zero-length filename part\n");
+        return STATUS_OBJECT_NAME_INVALID;
+    }
+
     has_stream = FALSE;
     for (i = 0; i < len; i++) {
         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
@@ -300,10 +305,17 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENT
 
     for (i = 0; i < len; i++) {
         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+            if (buf[0] == '/' || buf[0] == '\\') {
+                WARN("zero-length filename part\n");
+                Status = STATUS_OBJECT_NAME_INVALID;
+                goto cleanup;
+            }
+
             nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
             if (!nb) {
                 ERR("out of memory\n");
-                return STATUS_INSUFFICIENT_RESOURCES;
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto cleanup;
             }
 
             nb->us.Buffer = buf;
@@ -317,7 +329,8 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENT
     nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
     if (!nb) {
         ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto cleanup;
     }
 
     nb->us.Buffer = buf;
@@ -335,10 +348,17 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENT
             if (nb->us.Buffer[i] == ':') {
                 name_bit* nb2;
 
+                if (nb->us.Buffer[i+1] == 0) {
+                    WARN("zero-length stream name\n");
+                    Status = STATUS_OBJECT_NAME_INVALID;
+                    goto cleanup;
+                }
+
                 nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
                 if (!nb2) {
                     ERR("out of memory\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto cleanup;
                 }
 
                 nb2->us.Buffer = &nb->us.Buffer[i+1];
@@ -377,6 +397,15 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENT
     *stream = has_stream;
 
     return STATUS_SUCCESS;
+
+cleanup:
+    while (!IsListEmpty(parts)) {
+        nb = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
+
+        ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+    }
+
+    return Status;
 }
 
 NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
@@ -560,11 +589,6 @@ cont:
             break;
     }
 
-    // If a directory has a lot of files, force it to stick around until the next flush
-    // so we aren't constantly re-reading.
-    if (num_children >= 100)
-        mark_fcb_dirty(fcb);
-
     return STATUS_SUCCESS;
 }
 
@@ -577,9 +601,15 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
     BOOL atts_set = FALSE, sd_set = FALSE, no_data;
     LIST_ENTRY* lastle = NULL;
     EXTENT_DATA* ed = NULL;
+    UINT64 fcbs_version;
+    UINT32 hash;
+
+    hash = calc_crc32c(0xffffffff, (UINT8*)&inode, sizeof(UINT64));
 
-    if (!IsListEmpty(&subvol->fcbs)) {
-        LIST_ENTRY* le = subvol->fcbs.Flink;
+    acquire_fcb_lock_shared(Vcb);
+
+    if (subvol->fcbs_ptrs[hash >> 24]) {
+        LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
 
         while (le != &subvol->fcbs) {
             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
@@ -598,17 +628,21 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 #endif
 
                         *pfcb = fcb;
+                        release_fcb_lock(Vcb);
                         return STATUS_SUCCESS;
                     }
                 }
-            } else if (fcb->inode > inode) {
+            } else if (fcb->hash > hash) {
                 if (deleted_fcb) {
                     InterlockedIncrement(&deleted_fcb->refcount);
                     *pfcb = deleted_fcb;
+                    release_fcb_lock(Vcb);
                     return STATUS_SUCCESS;
                 }
 
                 lastle = le->Blink;
+                fcbs_version = subvol->fcbs_version;
+
                 break;
             }
 
@@ -616,6 +650,8 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
         }
     }
 
+    release_fcb_lock(Vcb);
+
     if (deleted_fcb) {
         InterlockedIncrement(&deleted_fcb->refcount);
         *pfcb = deleted_fcb;
@@ -632,6 +668,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
     fcb->subvol = subvol;
     fcb->inode = inode;
+    fcb->hash = hash;
     fcb->type = type;
 
     searchkey.obj_id = inode;
@@ -641,13 +678,13 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
     Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         return Status;
     }
 
     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
         WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         return STATUS_INVALID_PARAMETER;
     }
 
@@ -696,7 +733,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
                 if (!hl) {
                     ERR("out of memory\n");
-                    free_fcb(Vcb, fcb);
+                    reap_fcb(fcb);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
 
@@ -714,7 +751,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 if (!NT_SUCCESS(Status)) {
                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
                     ExFreePool(hl);
-                    free_fcb(Vcb, fcb);
+                    reap_fcb(fcb);
                     return Status;
                 }
 
@@ -728,7 +765,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     if (!hl->name.Buffer) {
                         ERR("out of memory\n");
                         ExFreePool(hl);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -737,7 +774,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
                         ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return Status;
                     }
                 }
@@ -761,7 +798,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
                 if (!hl) {
                     ERR("out of memory\n");
-                    free_fcb(Vcb, fcb);
+                    reap_fcb(fcb);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
 
@@ -779,7 +816,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 if (!NT_SUCCESS(Status)) {
                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
                     ExFreePool(hl);
-                    free_fcb(Vcb, fcb);
+                    reap_fcb(fcb);
                     return Status;
                 }
 
@@ -793,7 +830,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     if (!hl->name.Buffer) {
                         ERR("out of memory\n");
                         ExFreePool(hl);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -802,7 +839,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
                         ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return Status;
                     }
                 }
@@ -835,7 +872,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
                         if (!fcb->reparse_xattr.Buffer) {
                             ERR("out of memory\n");
-                            free_fcb(Vcb, fcb);
+                            reap_fcb(fcb);
                             return STATUS_INSUFFICIENT_RESOURCES;
                         }
 
@@ -858,7 +895,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                             fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
                             if (!fcb->ea_xattr.Buffer) {
                                 ERR("out of memory\n");
-                                free_fcb(Vcb, fcb);
+                                reap_fcb(fcb);
                                 return STATUS_INSUFFICIENT_RESOURCES;
                             }
 
@@ -906,7 +943,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
                         if (!fcb->sd) {
                             ERR("out of memory\n");
-                            free_fcb(Vcb, fcb);
+                            reap_fcb(fcb);
                             return STATUS_INSUFFICIENT_RESOURCES;
                         }
 
@@ -941,14 +978,14 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return Status;
                     }
 
                     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
                     if (!dc) {
                         ERR("out of memory\n");
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -959,7 +996,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     if (!dc->utf8.Buffer) {
                         ERR("out of memory\n");
                         ExFreePool(dc);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -971,7 +1008,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         ERR("out of memory\n");
                         ExFreePool(dc->utf8.Buffer);
                         ExFreePool(dc);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -981,7 +1018,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         ExFreePool(dc->utf8.Buffer);
                         ExFreePool(dc->name.Buffer);
                         ExFreePool(dc);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return Status;
                     }
 
@@ -991,7 +1028,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         ExFreePool(dc->utf8.Buffer);
                         ExFreePool(dc->name.Buffer);
                         ExFreePool(dc);
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return Status;
                     }
 
@@ -1004,7 +1041,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG);
                     if (!xa) {
                         ERR("out of memory\n");
-                        free_fcb(Vcb, fcb);
+                        reap_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
 
@@ -1033,7 +1070,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 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(EXTENT_DATA));
 
-                free_fcb(Vcb, fcb);
+                reap_fcb(fcb);
                 return STATUS_INTERNAL_ERROR;
             }
 
@@ -1044,7 +1081,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     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(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
 
-                    free_fcb(Vcb, fcb);
+                    reap_fcb(fcb);
                     return STATUS_INTERNAL_ERROR;
                 }
 
@@ -1058,7 +1095,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
             ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
             if (!ext) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                reap_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -1078,7 +1115,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
         Status = load_dir_children(Vcb, fcb, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_dir_children returned %08x\n", Status);
-            free_fcb(Vcb, fcb);
+            reap_fcb(fcb);
             return Status;
         }
     }
@@ -1103,25 +1140,102 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
     if (!sd_set)
         fcb_get_sd(fcb, parent, FALSE, Irp);
 
-    if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
-        fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+    acquire_fcb_lock_exclusive(Vcb);
+
+    if (lastle && subvol->fcbs_version == fcbs_version)
+        InsertHeadList(lastle, &fcb->list_entry);
+    else {
+        if (subvol->fcbs_ptrs[hash >> 24]) {
+            LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
+
+            while (le != &subvol->fcbs) {
+                struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+                if (fcb2->inode == inode) {
+                    if (!fcb2->ads) {
+                        if (fcb2->deleted)
+                            deleted_fcb = fcb2;
+                        else {
+#ifdef DEBUG_FCB_REFCOUNTS
+                            LONG rc = InterlockedIncrement(&fcb2->refcount);
+
+                            WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb2, rc, fcb2->subvol->id, fcb2->inode);
+#else
+                            InterlockedIncrement(&fcb2->refcount);
+#endif
+
+                            *pfcb = fcb2;
+                            release_fcb_lock(Vcb);
+                            reap_fcb(fcb);
+                            return STATUS_SUCCESS;
+                        }
+                    }
+                } else if (fcb2->hash > hash) {
+                    if (deleted_fcb) {
+                        InterlockedIncrement(&deleted_fcb->refcount);
+                        *pfcb = deleted_fcb;
+                        release_fcb_lock(Vcb);
+                        reap_fcb(fcb);
+                        return STATUS_SUCCESS;
+                    }
+
+                    lastle = le->Blink;
+                    break;
+                }
+
+                le = le->Flink;
+            }
+        }
+
+        if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
+            fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+            if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
+                fcb->atts_changed = TRUE;
+                mark_fcb_dirty(fcb);
+            }
+        }
+
+        if (!lastle) {
+            UINT8 c = hash >> 24;
+
+            if (c != 0xff) {
+                UINT8 d = c + 1;
+
+                do {
+                    if (subvol->fcbs_ptrs[d]) {
+                        lastle = subvol->fcbs_ptrs[d]->Blink;
+                        break;
+                    }
+
+                    d++;
+                } while (d != 0);
+            }
+        }
+
+        if (lastle) {
+            InsertHeadList(lastle, &fcb->list_entry);
+
+            if (lastle == &subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
+                subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
+        } else {
+            InsertTailList(&subvol->fcbs, &fcb->list_entry);
 
-        if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
-            fcb->atts_changed = TRUE;
-            mark_fcb_dirty(fcb);
+            if (fcb->list_entry.Blink == &subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
+                subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
         }
     }
 
-    if (lastle)
-        InsertHeadList(lastle, &fcb->list_entry);
-    else
-        InsertTailList(&subvol->fcbs, &fcb->list_entry);
+    subvol->fcbs_version++;
 
     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
 
+    release_fcb_lock(Vcb);
+
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
 
     *pfcb = fcb;
+
     return STATUS_SUCCESS;
 }
 
@@ -1162,7 +1276,7 @@ static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
 
     if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
         ERR("get_xattr failed\n");
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         ExFreePool(xattr.Buffer);
         return STATUS_INTERNAL_ERROR;
     }
@@ -1183,19 +1297,19 @@ static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
     Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("find_item returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         return Status;
     }
 
     if (keycmp(tp.item->key, searchkey)) {
         ERR("error - could not find key for xattr\n");
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         return STATUS_INTERNAL_ERROR;
     }
 
     if (tp.item->size < xattrlen) {
         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, xattrlen);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         return STATUS_INTERNAL_ERROR;
     }
 
@@ -1213,10 +1327,6 @@ static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
 
     TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
 
-    InsertHeadList(&parent->list_entry, &fcb->list_entry);
-
-    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
     *pfcb = fcb;
 
     return STATUS_SUCCESS;
@@ -1237,6 +1347,8 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
         UNICODE_STRING name_uc;
         dir_child* dc = NULL;
         fcb* fcb;
+        struct _fcb* duff_fcb = NULL;
+        file_ref* duff_fr = NULL;
 
         if (!case_sensitive) {
             Status = RtlUpcaseUnicodeString(&name_uc, name, TRUE);
@@ -1268,46 +1380,102 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
             le = le->Flink;
         }
 
-        if (!case_sensitive)
-            ExFreePool(name_uc.Buffer);
+        if (!dc) {
+            if (locked)
+                ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
 
-        if (locked)
-            ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+            if (!case_sensitive)
+                ExFreePool(name_uc.Buffer);
 
-        if (!dc)
             return STATUS_OBJECT_NAME_NOT_FOUND;
+        }
 
         if (dc->fileref) {
+            if (locked)
+                ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+
+            if (!case_sensitive)
+                ExFreePool(name_uc.Buffer);
+
             increase_fileref_refcount(dc->fileref);
             *psf2 = dc->fileref;
             return STATUS_SUCCESS;
         }
 
+        if (locked)
+            ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+
+        if (!case_sensitive)
+            ExFreePool(name_uc.Buffer);
+
         Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("open_fcb_stream returned %08x\n", Status);
             return Status;
         }
 
+        fcb->hash = sf->fcb->hash;
+
+        acquire_fcb_lock_exclusive(Vcb);
+
+        if (sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
+            LIST_ENTRY* le = sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
+
+            while (le != &sf->fcb->subvol->fcbs) {
+                struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+                if (fcb2->inode == fcb->inode) {
+                    if (fcb2->ads && fcb2->adshash == fcb->adshash) { // FIXME - handle hash collisions
+                        duff_fcb = fcb;
+                        fcb = fcb2;
+                        break;
+                    }
+                } else if (fcb2->hash > fcb->hash)
+                    break;
+
+                le = le->Flink;
+            }
+        }
+
+        if (!duff_fcb) {
+            InsertHeadList(&sf->fcb->list_entry, &fcb->list_entry);
+            InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+            fcb->subvol->fcbs_version++;
+        }
+
+        release_fcb_lock(Vcb);
+
+        if (duff_fcb) {
+            reap_fcb(duff_fcb);
+            InterlockedIncrement(&fcb->refcount);
+        }
+
         sf2 = create_fileref(Vcb);
         if (!sf2) {
             ERR("out of memory\n");
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
-        sf2->fcb = fcb;
-
-        sf2->parent = (struct _file_ref*)sf;
+        ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
 
-        sf2->dc = dc;
-        dc->fileref = sf2;
+        if (dc->fileref) {
+            duff_fr = sf2;
+            sf2 = dc->fileref;
+            increase_fileref_refcount(sf2);
+        } else {
+            sf2->fcb = fcb;
+            sf2->parent = (struct _file_ref*)sf;
+            sf2->dc = dc;
+            dc->fileref = sf2;
+            increase_fileref_refcount(sf);
+            InsertTailList(&sf->children, &sf2->list_entry);
+        }
 
-        ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
-        InsertTailList(&sf->children, &sf2->list_entry);
-        ExReleaseResourceLite(&sf->nonpaged->children_lock);
+        ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
 
-        increase_fileref_refcount(sf);
+        if (duff_fr)
+            reap_fileref(Vcb, duff_fr);
     } else {
         root* subvol;
         UINT64 inode;
@@ -1323,6 +1491,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
             return Status;
         } else {
             fcb* fcb;
+            file_ref* duff_fr = NULL;
 #ifdef DEBUG_STATS
             LARGE_INTEGER time1, time2;
 #endif
@@ -1360,14 +1529,14 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
 
             if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
                 TRACE("passed path including file as subdirectory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_OBJECT_PATH_NOT_FOUND;
             }
 
             sf2 = create_fileref(Vcb);
             if (!sf2) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -1376,16 +1545,24 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
             if (dc->type == BTRFS_TYPE_DIRECTORY)
                 fcb->fileref = sf2;
 
-            sf2->dc = dc;
-            dc->fileref = sf2;
+            ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
 
-            sf2->parent = (struct _file_ref*)sf;
+            if (!dc->fileref) {
+                sf2->parent = (struct _file_ref*)sf;
+                sf2->dc = dc;
+                dc->fileref = sf2;
+                InsertTailList(&sf->children, &sf2->list_entry);
+                increase_fileref_refcount(sf);
+            } else {
+                duff_fr = sf2;
+                sf2 = dc->fileref;
+                increase_fileref_refcount(sf2);
+            }
 
-            ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
-            InsertTailList(&sf->children, &sf2->list_entry);
-            ExReleaseResourceLite(&sf->nonpaged->children_lock);
+            ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
 
-            increase_fileref_refcount(sf);
+            if (duff_fr)
+                reap_fileref(Vcb, duff_fr);
         }
     }
 
@@ -1406,13 +1583,6 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
 
     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
 
-#ifdef DEBUG
-    if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
-        ERR("fcb_lock not acquired exclusively\n");
-        int3;
-    }
-#endif
-
     if (Vcb->removing || Vcb->locked)
         return STATUS_ACCESS_DENIED;
 
@@ -1456,7 +1626,8 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
                 *fn_offset = 0;
 
             return STATUS_SUCCESS;
-        }
+        } else if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\')
+            return STATUS_OBJECT_NAME_INVALID;
 
         dir = Vcb->root_fileref;
 
@@ -1559,7 +1730,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
             break;
         }
 
-        free_fileref(Vcb, sf);
+        free_fileref(sf);
         sf = sf2;
 
         le = le->Flink;
@@ -1570,7 +1741,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
     *pfr = sf2;
 
 end:
-    free_fileref(Vcb, sf);
+    free_fileref(sf);
 
     while (!IsListEmpty(&parts)) {
         name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
@@ -1586,6 +1757,7 @@ end2:
 NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, PUNICODE_STRING name, UINT8 type, dir_child** pdc) {
     NTSTATUS Status;
     dir_child* dc;
+    BOOL locked;
 
     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
     if (!dc) {
@@ -1632,7 +1804,10 @@ NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, P
     dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
     dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
 
-    ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
+    locked = ExIsResourceAcquiredExclusive(&fcb->nonpaged->dir_children_lock);
+
+    if (!locked)
+        ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
 
     if (IsListEmpty(&fcb->dir_children_index))
         dc->index = 2;
@@ -1646,7 +1821,8 @@ NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, P
 
     insert_dir_child_into_hash_lists(fcb, dc);
 
-    ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+    if (!locked)
+        ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
 
     *pdc = dc;
 
@@ -1883,7 +2059,7 @@ end:
 
 static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ PUNICODE_STRING fpus,
                              _In_ file_ref* parfileref, _In_ ULONG options, _In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen,
-                             _Out_ file_ref** pfr, _In_ LIST_ENTRY* rollback) {
+                             _Out_ file_ref** pfr, BOOL case_sensitive, _In_ LIST_ENTRY* rollback) {
     NTSTATUS Status;
     fcb* fcb;
     ULONG utf8len;
@@ -1898,6 +2074,8 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
     file_ref* fileref;
     dir_child* dc;
     ANSI_STRING utf8as;
+    LIST_ENTRY* lastle = NULL;
+    file_ref* existing_fileref = NULL;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
@@ -2057,18 +2235,74 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
     fcb->created = TRUE;
     fcb->deleted = TRUE;
 
+    fcb->hash = calc_crc32c(0xffffffff, (UINT8*)&inode, sizeof(UINT64));
+
+    acquire_fcb_lock_exclusive(Vcb);
+
+    if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
+        LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
+
+        while (le != &fcb->subvol->fcbs) {
+            struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+            if (fcb2->hash > fcb->hash) {
+                lastle = le->Blink;
+                break;
+            }
+
+            le = le->Flink;
+        }
+    }
+
+    if (!lastle) {
+        UINT8 c = fcb->hash >> 24;
+
+        if (c != 0xff) {
+            UINT8 d = c + 1;
+
+            do {
+                if (fcb->subvol->fcbs_ptrs[d]) {
+                    lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
+                    break;
+                }
+
+                d++;
+            } while (d != 0);
+        }
+    }
+
+    if (lastle) {
+        InsertHeadList(lastle, &fcb->list_entry);
+
+        if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
+            fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
+    } else {
+        InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
+        if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
+            fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
+    }
+
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+
+    fcb->subvol->fcbs_version++;
+
+    release_fcb_lock(Vcb);
+
     mark_fcb_dirty(fcb);
 
     Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
 
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_new_sd returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        free_fcb(fcb);
 
         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
         parfileref->fcb->inode_item.st_size -= utf8len * 2;
         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+        ExFreePool(utf8);
+
         return Status;
     }
 
@@ -2078,12 +2312,14 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
         Status = file_create_parse_ea(fcb, ea);
         if (!NT_SUCCESS(Status)) {
             ERR("file_create_parse_ea returned %08x\n", Status);
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
 
             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
             parfileref->fcb->inode_item.st_size -= utf8len * 2;
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+            ExFreePool(utf8);
+
             return Status;
         }
     }
@@ -2091,12 +2327,14 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
     fileref = create_fileref(Vcb);
     if (!fileref) {
         ERR("out of memory\n");
-        free_fcb(Vcb, fcb);
+        free_fcb(fcb);
 
         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
         parfileref->fcb->inode_item.st_size -= utf8len * 2;
         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+        ExFreePool(utf8);
+
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2107,12 +2345,14 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
 
         if (!NT_SUCCESS(Status)) {
             ERR("extend_file returned %08x\n", Status);
-            free_fileref(Vcb, fileref);
+            reap_fileref(Vcb, fileref);
 
             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
             parfileref->fcb->inode_item.st_size -= utf8len * 2;
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+            ExFreePool(utf8);
+
             return Status;
         }
     }
@@ -2121,12 +2361,14 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs) {
             ERR("out of memory\n");
-            free_fileref(Vcb, fileref);
+            reap_fileref(Vcb, fileref);
 
             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
             parfileref->fcb->inode_item.st_size -= utf8len * 2;
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+            ExFreePool(utf8);
+
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -2135,12 +2377,14 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs_uc) {
             ERR("out of memory\n");
-            free_fileref(Vcb, fileref);
+            reap_fileref(Vcb, fileref);
 
             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
             parfileref->fcb->inode_item.st_size -= utf8len * 2;
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
 
+            ExFreePool(utf8);
+
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -2150,39 +2394,126 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
     fcb->deleted = FALSE;
 
     fileref->created = TRUE;
-    mark_fileref_dirty(fileref);
 
     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
     fcb->subvol->root_item.ctime = now;
 
-    fileref->parent = parfileref;
-
     utf8as.Buffer = utf8;
     utf8as.Length = utf8as.MaximumLength = (UINT16)utf8len;
 
-    Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8as, fpus, fcb->type, &dc);
-    if (!NT_SUCCESS(Status))
-        WARN("add_dir_child returned %08x\n", Status);
+    ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
 
-    ExFreePool(utf8);
+    // check again doesn't already exist
+    if (case_sensitive) {
+        UINT32 dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpus->Buffer, fpus->Length);
+
+        if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) {
+            LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24];
+            while (le != &parfileref->fcb->dir_children_hash) {
+                dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
+
+                if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) {
+                    existing_fileref = dc->fileref;
+                    break;
+                } else if (dc->hash > dc_hash)
+                    break;
+
+                le = le->Flink;
+            }
+        }
+    } else {
+        UNICODE_STRING fpusuc;
+#ifdef __REACTOS__
+        UINT32 dc_hash;
+#endif
+
+        Status = RtlUpcaseUnicodeString(&fpusuc, fpus, TRUE);
+        if (!NT_SUCCESS(Status)) {
+            ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            reap_fileref(Vcb, fileref);
 
+            ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+            parfileref->fcb->inode_item.st_size -= utf8len * 2;
+            ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+            ExFreePool(utf8);
+
+            return Status;
+        }
+
+#ifndef __REACTOS__
+        UINT32 dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+#else
+        dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+#endif
+
+        if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
+            LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24];
+            while (le != &parfileref->fcb->dir_children_hash_uc) {
+                dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
+
+                if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) {
+                    existing_fileref = dc->fileref;
+                    break;
+                } else if (dc->hash_uc > dc_hash)
+                    break;
+
+                le = le->Flink;
+            }
+        }
+
+        ExFreePool(fpusuc.Buffer);
+    }
+
+    if (existing_fileref) {
+        ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+        reap_fileref(Vcb, fileref);
+
+        ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+        parfileref->fcb->inode_item.st_size -= utf8len * 2;
+        ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+        ExFreePool(utf8);
+
+        increase_fileref_refcount(existing_fileref);
+        *pfr = existing_fileref;
+
+        return STATUS_OBJECT_NAME_COLLISION;
+    }
+
+    Status = add_dir_child(parfileref->fcb, fcb->inode, FALSE, &utf8as, fpus, fcb->type, &dc);
+    if (!NT_SUCCESS(Status)) {
+        ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+        ERR("add_dir_child returned %08x\n", Status);
+        reap_fileref(Vcb, fileref);
+
+        ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+        parfileref->fcb->inode_item.st_size -= utf8len * 2;
+        ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+        ExFreePool(utf8);
+
+        return Status;
+    }
+
+    fileref->parent = parfileref;
     fileref->dc = dc;
     dc->fileref = fileref;
 
-    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+    if (type == BTRFS_TYPE_DIRECTORY)
+        fileref->fcb->fileref = fileref;
+
     InsertTailList(&parfileref->children, &fileref->list_entry);
-    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+    ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
 
-    increase_fileref_refcount(parfileref);
+    ExFreePool(utf8);
 
-    InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
-    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    mark_fileref_dirty(fileref);
+    increase_fileref_refcount(parfileref);
 
     *pfr = fileref;
 
-    if (type == BTRFS_TYPE_DIRECTORY)
-        fileref->fcb->fileref = fileref;
-
     TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
 
     return STATUS_SUCCESS;
@@ -2205,10 +2536,14 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     KEY searchkey;
     traverse_ptr tp;
     dir_child* dc;
+    dir_child* existing_dc = NULL;
     ACCESS_MASK granted_access;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
+#ifdef __REACTOS__
+    LIST_ENTRY* le;
+#endif
 
     TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
     TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
@@ -2248,16 +2583,18 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
 
         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
 
-        Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, rollback);
+        Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
 
         if (!NT_SUCCESS(Status)) {
             ERR("file_create2 returned %08x\n", Status);
             ExFreePool(fpus2.Buffer);
             return Status;
+        } else if (Status != STATUS_OBJECT_NAME_COLLISION) {
+            send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
+            send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
         }
 
-        send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
-        send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+        ExFreePool(fpus2.Buffer);
     } else if (!NT_SUCCESS(Status)) {
         ERR("open_fileref returned %08x\n", Status);
         return Status;
@@ -2268,16 +2605,20 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
 
     if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
         WARN("parent not file, directory, or symlink\n");
+        free_fileref(parfileref);
         return STATUS_INVALID_PARAMETER;
     }
 
     if (options & FILE_DIRECTORY_FILE) {
         WARN("tried to create directory as stream\n");
+        free_fileref(parfileref);
         return STATUS_INVALID_PARAMETER;
     }
 
-    if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY)
+    if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+        free_fileref(parfileref);
         return STATUS_ACCESS_DENIED;
+    }
 
     SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
 
@@ -2285,6 +2626,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
                        TRUE, FILE_WRITE_DATA, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
                        &granted_access, &Status)) {
         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+        free_fileref(parfileref);
         return Status;
     }
 
@@ -2293,12 +2635,14 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
         (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
         (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
+        free_fileref(parfileref);
         return STATUS_OBJECT_NAME_INVALID;
     }
 
     fcb = create_fcb(Vcb, pool_type);
     if (!fcb) {
         ERR("out of memory\n");
+        free_fileref(parfileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2324,7 +2668,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
+        free_fileref(parfileref);
         return Status;
     }
 
@@ -2333,7 +2678,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
     if (!fcb->adsxattr.Buffer) {
         ERR("out of memory\n");
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
+        free_fileref(parfileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2342,7 +2688,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
+        free_fileref(parfileref);
         return Status;
     }
 
@@ -2360,7 +2707,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("find_item returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
+        free_fileref(parfileref);
         return Status;
     }
 
@@ -2373,24 +2721,38 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
 
     if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
         WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
+        free_fileref(parfileref);
         return STATUS_DISK_FULL;
     } else
         fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
 
-    fileref = create_fileref(Vcb);
-    if (!fileref) {
-        ERR("out of memory\n");
-        free_fcb(Vcb, fcb);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    fcb->created = TRUE;
+    fcb->deleted = TRUE;
+
+    acquire_fcb_lock_exclusive(Vcb);
+    InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    parfileref->fcb->subvol->fcbs_version++;
+    release_fcb_lock(Vcb);
+
+    mark_fcb_dirty(fcb);
+
+    fileref = create_fileref(Vcb);
+    if (!fileref) {
+        ERR("out of memory\n");
+        free_fcb(fcb);
+        free_fileref(parfileref);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
     fileref->fcb = fcb;
 
     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
     if (!dc) {
         ERR("out of memory\n");
-        free_fileref(Vcb, fileref);
+        reap_fileref(Vcb, fileref);
+        free_fileref(parfileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2401,7 +2763,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     if (!dc->utf8.Buffer) {
         ERR("out of memory\n");
         ExFreePool(dc);
-        free_fileref(Vcb, fileref);
+        reap_fileref(Vcb, fileref);
+        free_fileref(parfileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2413,7 +2776,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
         ERR("out of memory\n");
         ExFreePool(dc->utf8.Buffer);
         ExFreePool(dc);
-        free_fileref(Vcb, fileref);
+        reap_fileref(Vcb, fileref);
+        free_fileref(parfileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -2425,23 +2789,62 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
         ExFreePool(dc->utf8.Buffer);
         ExFreePool(dc->name.Buffer);
         ExFreePool(dc);
-        free_fileref(Vcb, fileref);
+        reap_fileref(Vcb, fileref);
+        free_fileref(parfileref);
         return Status;
     }
 
+    KeQuerySystemTime(&time);
+    win_time_to_unix(time, &now);
+
+    ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
+
+#ifndef __REACTOS__
+    LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink;
+#else
+    le = parfileref->fcb->dir_children_index.Flink;
+#endif
+    while (le != &parfileref->fcb->dir_children_index) {
+        dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
+
+        if (dc2->index == 0) {
+            if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) ||
+                (!case_sensitive && dc2->name_uc.Length == dc->name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, dc->name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
+            ) {
+                existing_dc = dc2;
+                break;
+            }
+        } else
+            break;
+
+        le = le->Flink;
+    }
+
+    if (existing_dc) {
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc->name.Buffer);
+        ExFreePool(dc);
+        reap_fileref(Vcb, fileref);
+        free_fileref(parfileref);
+
+        increase_fileref_refcount(existing_dc->fileref);
+        *pfileref = existing_dc->fileref;
+
+        return STATUS_OBJECT_NAME_COLLISION;
+    }
+
     dc->fileref = fileref;
     fileref->dc = dc;
+    fileref->parent = (struct _file_ref*)parfileref;
+    fcb->deleted = FALSE;
 
     InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
 
-    mark_fcb_dirty(fcb);
-    mark_fileref_dirty(fileref);
+    InsertTailList(&parfileref->children, &fileref->list_entry);
 
-    InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
-    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
 
-    KeQuerySystemTime(&time);
-    win_time_to_unix(time, &now);
+    mark_fileref_dirty(fileref);
 
     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
     parfileref->fcb->inode_item.sequence++;
@@ -2453,12 +2856,6 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
     parfileref->fcb->subvol->root_item.ctime = now;
 
-    fileref->parent = (struct _file_ref*)parfileref;
-
-    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-    InsertTailList(&parfileref->children, &fileref->list_entry);
-    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
-
     increase_fileref_refcount(parfileref);
 
     *pfileref = fileref;
@@ -2501,7 +2898,8 @@ static __inline BOOL called_from_lxss() {
 #endif
 
 static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
-                            PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
+                            PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options,
+                            file_ref** existing_fileref, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     file_ref *fileref, *parfileref = NULL;
     ULONG i, j;
@@ -2655,9 +3053,12 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
         }
 
         Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
-                              &fileref, rollback);
+                              &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
 
-        if (!NT_SUCCESS(Status)) {
+        if (Status == STATUS_OBJECT_NAME_COLLISION) {
+            *existing_fileref = fileref;
+            goto end;
+        } else if (!NT_SUCCESS(Status)) {
             ERR("file_create2 returned %08x\n", Status);
             goto end;
         }
@@ -2683,7 +3084,7 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
         }
 
-        free_fileref(Vcb, fileref);
+        free_fileref(fileref);
         goto end;
     }
 
@@ -2739,7 +3140,7 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
                     ExReleaseResourceLite(parfileref->fcb->Header.Resource);
                 }
 
-                free_fileref(Vcb, fileref);
+                free_fileref(fileref);
                 return Status;
             }
         }
@@ -2757,7 +3158,7 @@ end:
 
 end2:
     if (parfileref && !loaded_related)
-        free_fileref(Vcb, parfileref);
+        free_fileref(parfileref);
 
     return Status;
 }
@@ -3050,791 +3451,887 @@ end:
     fcb->csum_loaded = TRUE;
 }
 
-static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
-    PFILE_OBJECT FileObject = NULL;
-    ULONG RequestedDisposition;
-    ULONG options;
+static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, POOL_TYPE pool_type, file_ref* fileref, ACCESS_MASK* granted_access,
+                           PFILE_OBJECT FileObject, UNICODE_STRING* fn, ULONG options, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
+    file_ref* sf;
+    BOOL readonly;
     ccb* ccb;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    USHORT parsed;
-    ULONG fn_offset = 0;
-    file_ref *related, *fileref = NULL;
-    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
-    ACCESS_MASK granted_access;
-    BOOL loaded_related = FALSE;
-    UNICODE_STRING fn;
-#ifdef DEBUG_FCB_REFCOUNTS
-    LONG oc;
-#endif
-#ifdef DEBUG_STATS
-    LARGE_INTEGER time1, time2;
-    UINT8 open_type = 0;
 
-    time1 = KeQueryPerformanceCounter(NULL);
+    if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
+        LARGE_INTEGER zero;
+
+#ifdef DEBUG_STATS
+        open_type = 1;
 #endif
+        if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
+            free_fileref(fileref);
 
-    Irp->IoStatus.Information = 0;
+            return STATUS_ACCESS_DENIED;
+        }
 
-    RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
-    options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+        if (Vcb->readonly) {
+            free_fileref(fileref);
 
-    if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
-        WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
-        return STATUS_INVALID_PARAMETER;
-    }
+            return STATUS_MEDIA_WRITE_PROTECTED;
+        }
 
-    FileObject = IrpSp->FileObject;
+        zero.QuadPart = 0;
+        if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
+            free_fileref(fileref);
 
-    if (!FileObject) {
-        ERR("FileObject was NULL\n");
-        return STATUS_INVALID_PARAMETER;
+            return STATUS_USER_MAPPED_FILE;
+        }
     }
 
-    if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
-        struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
-
-        related = relatedccb->fileref;
-    } else
-        related = NULL;
+    if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
+        SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
 
-    debug_create_options(options);
+        if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
+                            &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
+                            TRUE, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
+                            IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
+                            granted_access, &Status)) {
+            SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+            TRACE("SeAccessCheck failed, returning %08x\n", Status);
 
-    switch (RequestedDisposition) {
-        case FILE_SUPERSEDE:
-            TRACE("requested disposition: FILE_SUPERSEDE\n");
-            break;
+            free_fileref(fileref);
 
-        case FILE_CREATE:
-            TRACE("requested disposition: FILE_CREATE\n");
-            break;
+            return Status;
+        }
 
-        case FILE_OPEN:
-            TRACE("requested disposition: FILE_OPEN\n");
-            break;
+        SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+    } else
+        *granted_access = 0;
 
-        case FILE_OPEN_IF:
-            TRACE("requested disposition: FILE_OPEN_IF\n");
-            break;
+    TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
 
-        case FILE_OVERWRITE:
-            TRACE("requested disposition: FILE_OVERWRITE\n");
-            break;
+    sf = fileref;
+    while (sf) {
+        if (sf->delete_on_close) {
+            TRACE("could not open as deletion pending\n");
 
-        case FILE_OVERWRITE_IF:
-            TRACE("requested disposition: FILE_OVERWRITE_IF\n");
-            break;
+            free_fileref(fileref);
 
-        default:
-            ERR("unknown disposition: %x\n", RequestedDisposition);
-            Status = STATUS_NOT_IMPLEMENTED;
-            goto exit;
+            return STATUS_DELETE_PENDING;
+        }
+        sf = sf->parent;
     }
 
-    fn = FileObject->FileName;
+    readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) ||
+                is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
 
-    TRACE("(%.*S)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
-    TRACE("FileObject = %p\n", FileObject);
+    if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
+        free_fileref(fileref);
 
-    if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
-        Status = STATUS_MEDIA_WRITE_PROTECTED;
-        goto exit;
+        return STATUS_CANNOT_DELETE;
     }
 
-    acquire_fcb_lock_exclusive(Vcb);
-
-    if (options & FILE_OPEN_BY_FILE_ID) {
-        if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
-            UINT64 inode;
-
-            RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
-
-            if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
-                inode = Vcb->root_fileref->fcb->inode;
+    if (readonly) {
+        ACCESS_MASK allowed;
 
-            if (inode == 0) { // we use 0 to mean the parent of a subvolume
-                fileref = related->parent;
-                increase_fileref_refcount(fileref);
-                Status = STATUS_SUCCESS;
-            } else {
-                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
-            }
-        } else {
-            WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
-            Status = STATUS_NOT_IMPLEMENTED;
-            release_fcb_lock(Vcb);
-            goto exit;
-        }
-    } else {
-        if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
-            Status = STATUS_INVALID_PARAMETER;
-            release_fcb_lock(Vcb);
-            goto exit;
-        }
+        allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
+                    FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
+                    FILE_TRAVERSE;
 
-        if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
-            ULONG fnoff;
+        if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
+            allowed |= DELETE;
 
-            Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
-                                  pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+        if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
+            allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
 
-            if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
-                Status = STATUS_OBJECT_PATH_NOT_FOUND;
-            else if (Status == STATUS_REPARSE)
-                fileref = related;
-            else if (NT_SUCCESS(Status)) {
-                fnoff *= sizeof(WCHAR);
-                fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
-
-                if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
-                    Status = STATUS_REPARSE;
-                    fileref = related;
-                    parsed = (USHORT)fnoff - sizeof(WCHAR);
-                } else {
-                    fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
-                    fn.Length -= (USHORT)fnoff;
+            if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
+                allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
+        } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
+            // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
 
-                    Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
-                                          pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+            allowed |= FILE_WRITE_ATTRIBUTES;
+        }
 
-                    loaded_related = TRUE;
-                }
+        if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
+            *granted_access &= allowed;
+            IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
+        } else if (*granted_access & ~allowed) {
+            free_fileref(fileref);
 
-            }
-        } else {
-            Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
-                                  pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+            return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
         }
     }
 
-    if (Status == STATUS_REPARSE) {
+    if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
         REPARSE_DATA_BUFFER* data;
 
-        ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
-        Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
-        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+        /* How reparse points work from the point of view of the filesystem appears to
+            * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
+            * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
+            * translation. If we instead return the reparse tag in Information, and store
+            * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
+            * IopSymlinkProcessReparse will do the translation for us. */
 
+        Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
         if (!NT_SUCCESS(Status)) {
             ERR("get_reparse_block returned %08x\n", Status);
-
             Status = STATUS_SUCCESS;
         } else {
-            Status = STATUS_REPARSE;
-            RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+            Irp->IoStatus.Information = data->ReparseTag;
 
-            data->Reserved = FileObject->FileName.Length - parsed;
+            if (fn->Buffer[(fn->Length / sizeof(WCHAR)) - 1] == '\\')
+                data->Reserved = sizeof(WCHAR);
 
             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
 
-            free_fileref(Vcb, fileref);
-            release_fcb_lock(Vcb);
+            free_fileref(fileref);
 
-            goto exit;
+            return STATUS_REPARSE;
         }
     }
 
-    if (NT_SUCCESS(Status) && fileref->deleted)
-        Status = STATUS_OBJECT_NAME_NOT_FOUND;
+    if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
+        if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+            free_fileref(fileref);
 
-    if (NT_SUCCESS(Status)) {
-        if (RequestedDisposition == FILE_CREATE) {
-            TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
-            Status = STATUS_OBJECT_NAME_COLLISION;
+            return STATUS_FILE_IS_A_DIRECTORY;
+        }
+    } else if (options & FILE_DIRECTORY_FILE) {
+        TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
 
-            free_fileref(Vcb, fileref);
-            release_fcb_lock(Vcb);
+        free_fileref(fileref);
 
-            goto exit;
-        }
-    } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-        if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
-            TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
-            release_fcb_lock(Vcb);
-            goto exit;
-        }
-    } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
-        TRACE("open_fileref returned %08x\n", Status);
-        release_fcb_lock(Vcb);
-        goto exit;
-    } else {
-        ERR("open_fileref returned %08x\n", Status);
-        release_fcb_lock(Vcb);
-        goto exit;
+        return STATUS_NOT_A_DIRECTORY;
     }
 
-    if (NT_SUCCESS(Status)) { // file already exists
-        file_ref* sf;
-        BOOL readonly;
+    if (fileref->open_count > 0) {
+        Status = IoCheckShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, FALSE);
 
-        release_fcb_lock(Vcb);
+        if (!NT_SUCCESS(Status)) {
+            if (Status == STATUS_SHARING_VIOLATION)
+                TRACE("IoCheckShareAccess failed, returning %08x\n", Status);
+            else
+                WARN("IoCheckShareAccess failed, returning %08x\n", Status);
 
-        if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
-            LARGE_INTEGER zero;
+            free_fileref(fileref);
 
-#ifdef DEBUG_STATS
-            open_type = 1;
-#endif
-            if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
-                Status = STATUS_ACCESS_DENIED;
+            return Status;
+        }
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+        IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
+    } else
+        IoSetShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
 
-                goto exit;
-            }
+    if (*granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
+        if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
 
-            if (Vcb->readonly) {
-                Status = STATUS_MEDIA_WRITE_PROTECTED;
+            IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+            free_fileref(fileref);
 
-                goto exit;
-            }
+            return (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
+        }
+    }
 
-            zero.QuadPart = 0;
-            if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
-                Status = STATUS_USER_MAPPED_FILE;
+    if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
+        ULONG defda, oldatts, filter;
+        LARGE_INTEGER time;
+        BTRFS_TIME now;
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+        if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
+            WARN("cannot overwrite readonly file\n");
 
-                goto exit;
-            }
-        }
+            IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-        if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
-            SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+            free_fileref(fileref);
 
-            if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
-                               &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
-                               TRUE, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
-                               IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
-                               &granted_access, &Status)) {
-                SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-                TRACE("SeAccessCheck failed, returning %08x\n", Status);
+            return STATUS_ACCESS_DENIED;
+        }
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+        if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) {
+            IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                goto exit;
-            }
+            free_fileref(fileref);
 
-            SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-        } else
-            granted_access = 0;
+            return STATUS_ACCESS_DENIED;
+        }
 
-        TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
+        if (fileref->fcb->ads) {
+            Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, FALSE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("stream_set_end_of_file_information returned %08x\n", Status);
 
-        sf = fileref;
-        while (sf) {
-            if (sf->delete_on_close) {
-                TRACE("could not open as deletion pending\n");
-                Status = STATUS_DELETE_PENDING;
+                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+                free_fileref(fileref);
 
-                goto exit;
+                return Status;
             }
-            sf = sf->parent;
-        }
-
-        readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) ||
-                   is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
+        } else {
+            Status = truncate_file(fileref->fcb, 0, Irp, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("truncate_file returned %08x\n", Status);
 
-        if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
-            Status = STATUS_CANNOT_DELETE;
+                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-            acquire_fcb_lock_exclusive(Vcb);
-            free_fileref(Vcb, fileref);
-            release_fcb_lock(Vcb);
+                free_fileref(fileref);
 
-            goto exit;
+                return Status;
+            }
         }
 
-        if (readonly) {
-            ACCESS_MASK allowed;
+        if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+            Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
 
-            allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
-                      FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
-                      FILE_TRAVERSE;
-
-            if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
-                allowed |= DELETE;
+            if (!NT_SUCCESS(Status)) {
+                ERR("extend_file returned %08x\n", Status);
 
-            if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
-                allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
+                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
-                    allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
-            } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
-                // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
+                free_fileref(fileref);
 
-                allowed |= FILE_WRITE_ATTRIBUTES;
+                return Status;
             }
+        }
 
-            if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
-                granted_access &= allowed;
-                IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
-            } else if (granted_access & ~allowed) {
-                Status = Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
+        if (!fileref->fcb->ads) {
+            LIST_ENTRY* le;
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+            if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
+                ULONG offset;
+                FILE_FULL_EA_INFORMATION* eainfo;
 
-                goto exit;
-            }
-        }
+                Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
 
-        if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
-            REPARSE_DATA_BUFFER* data;
+                    IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-            /* How reparse points work from the point of view of the filesystem appears to
-             * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
-             * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
-             * translation. If we instead return the reparse tag in Information, and store
-             * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
-             * IopSymlinkProcessReparse will do the translation for us. */
+                    free_fileref(fileref);
 
-            Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
-            if (!NT_SUCCESS(Status)) {
-                ERR("get_reparse_block returned %08x\n", Status);
-                Status = STATUS_SUCCESS;
-            } else {
-                Status = STATUS_REPARSE;
-                Irp->IoStatus.Information = data->ReparseTag;
+                    return Status;
+                }
 
-                if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
-                    data->Reserved = sizeof(WCHAR);
+                fileref->fcb->ealen = 4;
 
-                Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+                // capitalize EA name
+                eainfo = Irp->AssociatedIrp.SystemBuffer;
+                do {
+                    STRING s;
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+                    s.Length = s.MaximumLength = eainfo->EaNameLength;
+                    s.Buffer = eainfo->EaName;
 
-                goto exit;
-            }
-        }
+                    RtlUpperString(&s, &s);
 
-        if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
-            if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
-                Status = STATUS_FILE_IS_A_DIRECTORY;
+                    fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+                    if (eainfo->NextEntryOffset == 0)
+                        break;
 
-                goto exit;
-            }
-        } else if (options & FILE_DIRECTORY_FILE) {
-            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
-            Status = STATUS_NOT_A_DIRECTORY;
+                    eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+                } while (TRUE);
 
-            acquire_fcb_lock_exclusive(Vcb);
-            free_fileref(Vcb, fileref);
-            release_fcb_lock(Vcb);
+                if (fileref->fcb->ea_xattr.Buffer)
+                    ExFreePool(fileref->fcb->ea_xattr.Buffer);
 
-            goto exit;
-        }
+                fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
+                if (!fileref->fcb->ea_xattr.Buffer) {
+                    ERR("out of memory\n");
 
-        if (fileref->open_count > 0) {
-            Status = IoCheckShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, FALSE);
+                    IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-            if (!NT_SUCCESS(Status)) {
-                if (Status == STATUS_SHARING_VIOLATION)
-                    TRACE("IoCheckShareAccess failed, returning %08x\n", Status);
-                else
-                    WARN("IoCheckShareAccess failed, returning %08x\n", Status);
+                    free_fileref(fileref);
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
 
-                goto exit;
+                fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
+                RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
+            } else {
+                if (fileref->fcb->ea_xattr.Length > 0) {
+                    ExFreePool(fileref->fcb->ea_xattr.Buffer);
+                    fileref->fcb->ea_xattr.Buffer = NULL;
+                    fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
+
+                    fileref->fcb->ea_changed = TRUE;
+                    fileref->fcb->ealen = 0;
+                }
             }
 
-            IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
-        } else
-            IoSetShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
+            // remove streams and send notifications
+            le = fileref->fcb->dir_children_index.Flink;
+            while (le != &fileref->fcb->dir_children_index) {
+                dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+                LIST_ENTRY* le2 = le->Flink;
 
-        if (granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
-            if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
-                Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
+                if (dc->index == 0) {
+                    if (!dc->fileref) {
+                        file_ref* fr2;
 
-                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+                        Status = open_fileref_child(Vcb, fileref, &dc->name, TRUE, TRUE, TRUE, PagedPool, &fr2, NULL);
+                        if (!NT_SUCCESS(Status))
+                            WARN("open_fileref_child returned %08x\n", Status);
+                    }
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+                    if (dc->fileref) {
+                        send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
 
-                goto exit;
+                        Status = delete_fileref(dc->fileref, NULL, NULL, rollback);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_fileref returned %08x\n", Status);
+
+                            free_fileref(fileref);
+
+                            return Status;
+                        }
+                    }
+                } else
+                    break;
+
+                le = le2;
             }
         }
 
-        if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
-            ULONG defda, oldatts, filter;
-            LARGE_INTEGER time;
-            BTRFS_TIME now;
+        KeQuerySystemTime(&time);
+        win_time_to_unix(time, &now);
 
-            if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
-                WARN("cannot overwrite readonly file\n");
-                Status = STATUS_ACCESS_DENIED;
+        filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
 
-                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+        if (fileref->fcb->ads) {
+            fileref->parent->fcb->inode_item.st_mtime = now;
+            fileref->parent->fcb->inode_item_changed = TRUE;
+            mark_fcb_dirty(fileref->parent->fcb);
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+            send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
+        } else {
+            mark_fcb_dirty(fileref->fcb);
 
-                goto exit;
-            }
+            oldatts = fileref->fcb->atts;
 
-            if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) {
-                IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+            defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
+                                        fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
 
-                acquire_fcb_lock_exclusive(Vcb);
-                free_fileref(Vcb, fileref);
-                release_fcb_lock(Vcb);
+            if (RequestedDisposition == FILE_SUPERSEDE)
+                fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+            else
+                fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
 
-                Status = STATUS_ACCESS_DENIED;
-                goto exit;
+            if (fileref->fcb->atts != oldatts) {
+                fileref->fcb->atts_changed = TRUE;
+                fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
+                filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
             }
 
-            if (fileref->fcb->ads) {
-                Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, FALSE);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("stream_set_end_of_file_information returned %08x\n", Status);
+            fileref->fcb->inode_item.transid = Vcb->superblock.generation;
+            fileref->fcb->inode_item.sequence++;
+            fileref->fcb->inode_item.st_ctime = now;
+            fileref->fcb->inode_item.st_mtime = now;
+            fileref->fcb->inode_item_changed = TRUE;
+
+            send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+        }
+    } else {
+        if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
+            FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
+
+            do {
+                if (ffei->Flags & FILE_NEED_EA) {
+                    WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
 
                     IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                    acquire_fcb_lock_exclusive(Vcb);
-                    free_fileref(Vcb, fileref);
-                    release_fcb_lock(Vcb);
+                    free_fileref(fileref);
 
-                    goto exit;
+                    return STATUS_ACCESS_DENIED;
                 }
-            } else {
-                Status = truncate_file(fileref->fcb, 0, Irp, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("truncate_file returned %08x\n", Status);
 
-                    IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+                if (ffei->NextEntryOffset == 0)
+                    break;
 
-                    acquire_fcb_lock_exclusive(Vcb);
-                    free_fileref(Vcb, fileref);
-                    release_fcb_lock(Vcb);
+                ffei = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ffei) + ffei->NextEntryOffset);
+            } while (TRUE);
+        }
+    }
 
-                    goto exit;
-                }
-            }
+    FileObject->FsContext = fileref->fcb;
 
-            if (Irp->Overlay.AllocationSize.QuadPart > 0) {
-                Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
+    ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+    if (!ccb) {
+        ERR("out of memory\n");
 
-                if (!NT_SUCCESS(Status)) {
-                    ERR("extend_file returned %08x\n", Status);
+        IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
 
-                    IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+        free_fileref(fileref);
 
-                    acquire_fcb_lock_exclusive(Vcb);
-                    free_fileref(Vcb, fileref);
-                    release_fcb_lock(Vcb);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-                    goto exit;
-                }
-            }
+    RtlZeroMemory(ccb, sizeof(*ccb));
 
-            if (!fileref->fcb->ads) {
-                LIST_ENTRY* le;
+    ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+    ccb->NodeSize = sizeof(*ccb);
+    ccb->disposition = RequestedDisposition;
+    ccb->options = options;
+    ccb->query_dir_offset = 0;
+    RtlInitUnicodeString(&ccb->query_string, NULL);
+    ccb->has_wildcard = FALSE;
+    ccb->specific_file = FALSE;
+    ccb->access = *granted_access;
+    ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
+    ccb->reserving = FALSE;
+    ccb->lxss = called_from_lxss();
 
-                if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
-                    ULONG offset;
-                    FILE_FULL_EA_INFORMATION* eainfo;
+    ccb->fileref = fileref;
 
-                    Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+    FileObject->FsContext2 = ccb;
+    FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
 
-                        IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+    if (NT_SUCCESS(Status)) {
+        switch (RequestedDisposition) {
+            case FILE_SUPERSEDE:
+                Irp->IoStatus.Information = FILE_SUPERSEDED;
+                break;
 
-                        acquire_fcb_lock_exclusive(Vcb);
-                        free_fileref(Vcb, fileref);
-                        release_fcb_lock(Vcb);
+            case FILE_OPEN:
+            case FILE_OPEN_IF:
+                Irp->IoStatus.Information = FILE_OPENED;
+                break;
 
-                        goto exit;
-                    }
+            case FILE_OVERWRITE:
+            case FILE_OVERWRITE_IF:
+                Irp->IoStatus.Information = FILE_OVERWRITTEN;
+                break;
+        }
+    }
 
-                    fileref->fcb->ealen = 4;
+    // Make sure paging files don't have any extents marked as being prealloc,
+    // as this would mean we'd have to lock exclusively when writing.
+    if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
+        LIST_ENTRY* le;
+        BOOL changed = FALSE;
 
-                    // capitalize EA name
-                    eainfo = Irp->AssociatedIrp.SystemBuffer;
-                    do {
-                        STRING s;
+        ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
 
-                        s.Length = s.MaximumLength = eainfo->EaNameLength;
-                        s.Buffer = eainfo->EaName;
+        le = fileref->fcb->extents.Flink;
 
-                        RtlUpperString(&s, &s);
+        while (le != &fileref->fcb->extents) {
+            extent* ext = CONTAINING_RECORD(le, extent, list_entry);
 
-                        fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+            if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+                ext->extent_data.type = EXTENT_TYPE_REGULAR;
+                changed = TRUE;
+            }
 
-                        if (eainfo->NextEntryOffset == 0)
-                            break;
+            le = le->Flink;
+        }
 
-                        eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
-                    } while (TRUE);
+        ExReleaseResourceLite(fileref->fcb->Header.Resource);
 
-                    if (fileref->fcb->ea_xattr.Buffer)
-                        ExFreePool(fileref->fcb->ea_xattr.Buffer);
+        if (changed) {
+            fileref->fcb->extents_changed = TRUE;
+            mark_fcb_dirty(fileref->fcb);
+        }
 
-                    fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
-                    if (!fileref->fcb->ea_xattr.Buffer) {
-                        ERR("out of memory\n");
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
+        fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
+        Vcb->disallow_dismount = TRUE;
+    }
 
-                        IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+#ifdef DEBUG_FCB_REFCOUNTS
+    LONG oc = InterlockedIncrement(&fileref->open_count);
+    ERR("fileref %p: open_count now %i\n", fileref, oc);
+#else
+    InterlockedIncrement(&fileref->open_count);
+#endif
+    InterlockedIncrement(&Vcb->open_files);
 
-                        acquire_fcb_lock_exclusive(Vcb);
-                        free_fileref(Vcb, fileref);
-                        release_fcb_lock(Vcb);
+    return STATUS_SUCCESS;
+}
 
-                        goto exit;
-                    }
+NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
+                               root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
+    NTSTATUS Status;
+    fcb* fcb;
+    UINT64 parent = 0;
+    UNICODE_STRING name;
+    BOOL hl_alloc = FALSE;
+    file_ref *parfr, *fr;
+#ifdef __REACTOS__
+    hardlink* hl;
+#endif
 
-                    fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
-                    RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
-                } else {
-                    if (fileref->fcb->ea_xattr.Length > 0) {
-                        ExFreePool(fileref->fcb->ea_xattr.Buffer);
-                        fileref->fcb->ea_xattr.Buffer = NULL;
-                        fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
+    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("open_fcb returned %08x\n", Status);
+        return Status;
+    }
 
-                        fileref->fcb->ea_changed = TRUE;
-                        fileref->fcb->ealen = 0;
-                    }
-                }
+    if (fcb->fileref) {
+        *pfr = fcb->fileref;
+        increase_fileref_refcount(fcb->fileref);
+        return STATUS_SUCCESS;
+    }
 
-                // remove streams and send notifications
-                le = fileref->fcb->dir_children_index.Flink;
-                while (le != &fileref->fcb->dir_children_index) {
-                    dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
-                    LIST_ENTRY* le2 = le->Flink;
+#ifndef __REACTOS__
+    hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+#else
+    hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+#endif
 
-                    if (dc->index == 0) {
-                        if (!dc->fileref) {
-                            file_ref* fr2;
+    name = hl->name;
+    parent = hl->parent;
 
-                            Status = open_fileref_child(Vcb, fileref, &dc->name, TRUE, TRUE, TRUE, PagedPool, &fr2, NULL);
-                            if (!NT_SUCCESS(Status))
-                                WARN("open_fileref_child returned %08x\n", Status);
-                        }
+    if (parent == inode) { // subvolume root
+        KEY searchkey;
+        traverse_ptr tp;
 
-                        if (dc->fileref) {
-                            send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
+        searchkey.obj_id = subvol->id;
+        searchkey.obj_type = TYPE_ROOT_BACKREF;
+        searchkey.offset = 0xffffffffffffffff;
 
-                            Status = delete_fileref(dc->fileref, NULL, NULL, rollback);
-                            if (!NT_SUCCESS(Status)) {
-                                ERR("delete_fileref returned %08x\n", Status);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("find_item returned %08x\n", Status);
+            free_fcb(fcb);
+            return Status;
+        }
 
-                                acquire_fcb_lock_exclusive(Vcb);
-                                free_fileref(Vcb, fileref);
-                                release_fcb_lock(Vcb);
+        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+            ROOT_REF* rr = (ROOT_REF*)tp.item->data;
+            LIST_ENTRY* le;
+            root* r = NULL;
+            ULONG stringlen;
 
-                                goto exit;
-                            }
-                        }
-                    } else
-                        break;
+            if (tp.item->size < sizeof(ROOT_REF)) {
+                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(ROOT_REF));
+                free_fcb(fcb);
+                return STATUS_INTERNAL_ERROR;
+            }
 
-                    le = le2;
-                }
+            if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->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, offsetof(ROOT_REF, name[0]) + rr->n);
+                free_fcb(fcb);
+                return STATUS_INTERNAL_ERROR;
             }
 
-            KeQuerySystemTime(&time);
-            win_time_to_unix(time, &now);
+            le = Vcb->roots.Flink;
+            while (le != &Vcb->roots) {
+                root* r2 = CONTAINING_RECORD(le, root, list_entry);
+
+                if (r2->id == tp.item->key.offset) {
+                    r = r2;
+                    break;
+                }
 
-            filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
+                le = le->Flink;
+            }
 
-            if (fileref->fcb->ads) {
-                fileref->parent->fcb->inode_item.st_mtime = now;
-                fileref->parent->fcb->inode_item_changed = TRUE;
-                mark_fcb_dirty(fileref->parent->fcb);
+            if (!r) {
+                ERR("couldn't find subvol %llx\n", tp.item->key.offset);
+                free_fcb(fcb);
+                return STATUS_INTERNAL_ERROR;
+            }
 
-                send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
-            } else {
-                mark_fcb_dirty(fileref->fcb);
+            Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("open_fileref_by_inode returned %08x\n", Status);
+                free_fcb(fcb);
+                return Status;
+            }
 
-                oldatts = fileref->fcb->atts;
+            Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                free_fcb(fcb);
+                return Status;
+            }
 
-                defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
-                                            fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
+            name.Length = name.MaximumLength = (UINT16)stringlen;
 
-                if (RequestedDisposition == FILE_SUPERSEDE)
-                    fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
-                else
-                    fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+            if (stringlen == 0)
+                name.Buffer = NULL;
+            else {
+                name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
 
-                if (fileref->fcb->atts != oldatts) {
-                    fileref->fcb->atts_changed = TRUE;
-                    fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
-                    filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+                if (!name.Buffer) {
+                    ERR("out of memory\n");
+                    free_fcb(fcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
                 }
 
-                fileref->fcb->inode_item.transid = Vcb->superblock.generation;
-                fileref->fcb->inode_item.sequence++;
-                fileref->fcb->inode_item.st_ctime = now;
-                fileref->fcb->inode_item.st_mtime = now;
-                fileref->fcb->inode_item_changed = TRUE;
+                Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                    ExFreePool(name.Buffer);
+                    free_fcb(fcb);
+                    return Status;
+                }
 
-                send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+                hl_alloc = TRUE;
             }
         } else {
-            if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
-                FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
+            ERR("couldn't find parent for subvol %llx\n", subvol->id);
+            free_fcb(fcb);
+            return STATUS_INTERNAL_ERROR;
+        }
+    } else {
+        Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("open_fileref_by_inode returned %08x\n", Status);
+            free_fcb(fcb);
+            return Status;
+        }
+    }
 
-                do {
-                    if (ffei->Flags & FILE_NEED_EA) {
-                        WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
-                        Status = STATUS_ACCESS_DENIED;
+    Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
 
-                        IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+    if (hl_alloc)
+        ExFreePool(name.Buffer);
 
-                        acquire_fcb_lock_exclusive(Vcb);
-                        free_fileref(Vcb, fileref);
-                        release_fcb_lock(Vcb);
+    if (!NT_SUCCESS(Status)) {
+        ERR("open_fileref_child returned %08x\n", Status);
 
-                        goto exit;
-                    }
+        free_fcb(fcb);
+        free_fileref(parfr);
 
-                    if (ffei->NextEntryOffset == 0)
-                        break;
+        return Status;
+    }
 
-                    ffei = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ffei) + ffei->NextEntryOffset);
-                } while (TRUE);
-            }
-        }
+    *pfr = fr;
 
-        FileObject->FsContext = fileref->fcb;
+    free_fcb(fcb);
+    free_fileref(parfr);
 
-        ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
-        if (!ccb) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
+    return STATUS_SUCCESS;
+}
 
-            IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+    PFILE_OBJECT FileObject = NULL;
+    ULONG RequestedDisposition;
+    ULONG options;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    USHORT parsed;
+    ULONG fn_offset = 0;
+    file_ref *related, *fileref = NULL;
+    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
+    ACCESS_MASK granted_access;
+    BOOL loaded_related = FALSE;
+    UNICODE_STRING fn;
+#ifdef DEBUG_STATS
+    LARGE_INTEGER time1, time2;
+    UINT8 open_type = 0;
+
+    time1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+    Irp->IoStatus.Information = 0;
+
+    RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+    options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+    if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
+        WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    FileObject = IrpSp->FileObject;
+
+    if (!FileObject) {
+        ERR("FileObject was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
+        struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+
+        related = relatedccb->fileref;
+    } else
+        related = NULL;
+
+    debug_create_options(options);
+
+    switch (RequestedDisposition) {
+        case FILE_SUPERSEDE:
+            TRACE("requested disposition: FILE_SUPERSEDE\n");
+            break;
+
+        case FILE_CREATE:
+            TRACE("requested disposition: FILE_CREATE\n");
+            break;
+
+        case FILE_OPEN:
+            TRACE("requested disposition: FILE_OPEN\n");
+            break;
+
+        case FILE_OPEN_IF:
+            TRACE("requested disposition: FILE_OPEN_IF\n");
+            break;
+
+        case FILE_OVERWRITE:
+            TRACE("requested disposition: FILE_OVERWRITE\n");
+            break;
+
+        case FILE_OVERWRITE_IF:
+            TRACE("requested disposition: FILE_OVERWRITE_IF\n");
+            break;
+
+        default:
+            ERR("unknown disposition: %x\n", RequestedDisposition);
+            Status = STATUS_NOT_IMPLEMENTED;
+            goto exit;
+    }
+
+    fn = FileObject->FileName;
+
+    TRACE("(%.*S)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
+    TRACE("FileObject = %p\n", FileObject);
+
+    if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
+        Status = STATUS_MEDIA_WRITE_PROTECTED;
+        goto exit;
+    }
+
+    if (options & FILE_OPEN_BY_FILE_ID) {
+        if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+            UINT64 inode;
+
+            RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
+
+            if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
+                inode = Vcb->root_fileref->fcb->inode;
 
-            acquire_fcb_lock_exclusive(Vcb);
-            free_fileref(Vcb, fileref);
-            release_fcb_lock(Vcb);
+            if (inode == 0) { // we use 0 to mean the parent of a subvolume
+                fileref = related->parent;
+                increase_fileref_refcount(fileref);
+                Status = STATUS_SUCCESS;
+            } else
+                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
 
+            goto loaded;
+        } else {
+            WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
+            Status = STATUS_NOT_IMPLEMENTED;
             goto exit;
         }
+    }
 
-        RtlZeroMemory(ccb, sizeof(*ccb));
+    if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
+    }
 
-        ccb->NodeType = BTRFS_NODE_TYPE_CCB;
-        ccb->NodeSize = sizeof(*ccb);
-        ccb->disposition = RequestedDisposition;
-        ccb->options = options;
-        ccb->query_dir_offset = 0;
-        RtlInitUnicodeString(&ccb->query_string, NULL);
-        ccb->has_wildcard = FALSE;
-        ccb->specific_file = FALSE;
-        ccb->access = granted_access;
-        ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
-        ccb->reserving = FALSE;
-        ccb->lxss = called_from_lxss();
+    if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
+        ULONG fnoff;
 
-        ccb->fileref = fileref;
+        Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
+                              pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
 
-        FileObject->FsContext2 = ccb;
-        FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
+        if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+            Status = STATUS_OBJECT_PATH_NOT_FOUND;
+        else if (Status == STATUS_REPARSE)
+            fileref = related;
+        else if (NT_SUCCESS(Status)) {
+            fnoff *= sizeof(WCHAR);
+            fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
 
-        if (NT_SUCCESS(Status)) {
-            switch (RequestedDisposition) {
-                case FILE_SUPERSEDE:
-                    Irp->IoStatus.Information = FILE_SUPERSEDED;
-                    break;
+            if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+                Status = STATUS_REPARSE;
+                fileref = related;
+                parsed = (USHORT)fnoff - sizeof(WCHAR);
+            } else {
+                fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
+                fn.Length -= (USHORT)fnoff;
 
-                case FILE_OPEN:
-                case FILE_OPEN_IF:
-                    Irp->IoStatus.Information = FILE_OPENED;
-                    break;
+                Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+                                      pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
 
-                case FILE_OVERWRITE:
-                case FILE_OVERWRITE_IF:
-                    Irp->IoStatus.Information = FILE_OVERWRITTEN;
-                    break;
+                loaded_related = TRUE;
             }
         }
+    } else {
+        Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+                              pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+    }
 
-        // Make sure paging files don't have any extents marked as being prealloc,
-        // as this would mean we'd have to lock exclusively when writing.
-        if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
-            LIST_ENTRY* le;
-            BOOL changed = FALSE;
+loaded:
+    if (Status == STATUS_REPARSE) {
+        REPARSE_DATA_BUFFER* data;
 
-            ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
+        ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
+        Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
+        ExReleaseResourceLite(fileref->fcb->Header.Resource);
 
-            le = fileref->fcb->extents.Flink;
+        if (!NT_SUCCESS(Status)) {
+            ERR("get_reparse_block returned %08x\n", Status);
 
-            while (le != &fileref->fcb->extents) {
-                extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+            Status = STATUS_SUCCESS;
+        } else {
+            Status = STATUS_REPARSE;
+            RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
 
-                if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
-                    ext->extent_data.type = EXTENT_TYPE_REGULAR;
-                    changed = TRUE;
-                }
+            data->Reserved = FileObject->FileName.Length - parsed;
 
-                le = le->Flink;
-            }
+            Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
 
-            ExReleaseResourceLite(fileref->fcb->Header.Resource);
+            free_fileref(fileref);
 
-            if (changed) {
-                fileref->fcb->extents_changed = TRUE;
-                mark_fcb_dirty(fileref->fcb);
-            }
+            goto exit;
+        }
+    }
+
+    if (NT_SUCCESS(Status) && fileref->deleted)
+        Status = STATUS_OBJECT_NAME_NOT_FOUND;
 
-            fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
-            Vcb->disallow_dismount = TRUE;
+    if (NT_SUCCESS(Status)) {
+        if (RequestedDisposition == FILE_CREATE) {
+            TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
+            Status = STATUS_OBJECT_NAME_COLLISION;
+
+            free_fileref(fileref);
+
+            goto exit;
         }
+    } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+        if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
+            TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
+            goto exit;
+        }
+    } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
+        TRACE("open_fileref returned %08x\n", Status);
+        goto exit;
+    } else {
+        ERR("open_fileref returned %08x\n", Status);
+        goto exit;
+    }
 
-#ifdef DEBUG_FCB_REFCOUNTS
-        oc = InterlockedIncrement(&fileref->open_count);
-        ERR("fileref %p: open_count now %i\n", fileref, oc);
-#else
-        InterlockedIncrement(&fileref->open_count);
-#endif
-        InterlockedIncrement(&Vcb->open_files);
+    if (NT_SUCCESS(Status)) { // file already exists
+        Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
+        if (!NT_SUCCESS(Status))
+            goto exit;
     } else {
+        file_ref* existing_file;
+
 #ifdef DEBUG_STATS
         open_type = 2;
 #endif
-        Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
-        release_fcb_lock(Vcb);
+        Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, &existing_file, rollback);
+
+        if (Status == STATUS_OBJECT_NAME_COLLISION) { // already exists
+            fileref = existing_file;
 
-        Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
-        granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+            Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
+            if (!NT_SUCCESS(Status))
+                goto exit;
+        } else {
+            Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
+            granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+        }
     }
 
     if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
         FileObject->Flags |= FO_CACHE_SUPPORTED;
 
 exit:
-    if (loaded_related) {
-        acquire_fcb_lock_exclusive(Vcb);
-        free_fileref(Vcb, related);
-        release_fcb_lock(Vcb);
-    }
+    if (loaded_related)
+        free_fileref(related);
 
     if (Status == STATUS_SUCCESS) {
         fcb* fcb2;
@@ -4128,6 +4625,8 @@ NTSTATUS NTAPI drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         if (!skip_lock)
             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
 
+        ExAcquireResourceSharedLite(&Vcb->fileref_lock, TRUE);
+
         Status = open_file(DeviceObject, Vcb, Irp, &rollback);
 
         if (!NT_SUCCESS(Status))
@@ -4135,6 +4634,8 @@ NTSTATUS NTAPI drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         else
             clear_rollback(&rollback);
 
+        ExReleaseResourceLite(&Vcb->fileref_lock);
+
         if (!skip_lock)
             ExReleaseResourceLite(&Vcb->tree_lock);
     }
index 4d8df55..19b261b 100644 (file)
@@ -91,7 +91,7 @@ ULONG get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 t
 
     ExReleaseResourceLite(fcb->Header.Resource);
 
-    free_fcb(Vcb, fcb);
+    free_fcb(fcb);
 
     return tag;
 }
@@ -602,7 +602,6 @@ static NTSTATUS query_directory(PIRP Irp) {
     BOOL has_wildcard = FALSE, specific_file = FALSE, initial;
     dir_entry de;
     UINT64 newoffset;
-    ANSI_STRING utf8;
     dir_child* dc = NULL;
 
     TRACE("query directory\n");
@@ -612,8 +611,6 @@ static NTSTATUS query_directory(PIRP Irp) {
     ccb = IrpSp->FileObject->FsContext2;
     fileref = ccb ? ccb->fileref : NULL;
 
-    utf8.Buffer = NULL;
-
     if (!fileref)
         return STATUS_INVALID_PARAMETER;
 
@@ -642,11 +639,6 @@ static NTSTATUS query_directory(PIRP Irp) {
     if (fileref->fcb == Vcb->dummy_fcb)
         return STATUS_NO_MORE_FILES;
 
-    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-    acquire_fcb_lock_shared(Vcb);
-
-    TRACE("%S\n", file_desc(IrpSp->FileObject));
-
     if (IrpSp->Flags == 0) {
         TRACE("QD flags: (none)\n");
     } else {
@@ -708,8 +700,7 @@ static NTSTATUS query_directory(PIRP Irp) {
             ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
             if (!ccb->query_string.Buffer) {
                 ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end2;
+                return STATUS_INSUFFICIENT_RESOURCES;
             }
 
             ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
@@ -725,10 +716,8 @@ static NTSTATUS query_directory(PIRP Irp) {
         if (!(IrpSp->Flags & SL_RESTART_SCAN)) {
             initial = FALSE;
 
-            if (specific_file) {
-                Status = STATUS_NO_MORE_FILES;
-                goto end2;
-            }
+            if (specific_file)
+                return STATUS_NO_MORE_FILES;
         }
     }
 
@@ -738,6 +727,8 @@ static NTSTATUS query_directory(PIRP Irp) {
 
     newoffset = ccb->query_dir_offset;
 
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+
     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
 
     Status = next_dir_entry(fileref, &newoffset, &de, &dc);
@@ -923,15 +914,10 @@ static NTSTATUS query_directory(PIRP Irp) {
 end:
     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
 
-end2:
-    release_fcb_lock(Vcb);
     ExReleaseResourceLite(&Vcb->tree_lock);
 
     TRACE("returning %08x\n", Status);
 
-    if (utf8.Buffer)
-        ExFreePool(utf8.Buffer);
-
     return Status;
 }
 
index 1c342b0..2339c5d 100644 (file)
@@ -19,7 +19,7 @@
 
 #if (NTDDI_VERSION >= NTDDI_WIN10)
 // not currently in mingw - introduced with Windows 10
-#ifndef FileIdInformation
+#ifndef _MSC_VER
 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
 
@@ -230,8 +230,6 @@ static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFI
     if (!fileref)
         return STATUS_INVALID_PARAMETER;
 
-    acquire_fcb_lock_exclusive(Vcb);
-
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
 
     TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
@@ -277,8 +275,6 @@ static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFI
 end:
     ExReleaseResourceLite(fcb->Header.Resource);
 
-    release_fcb_lock(Vcb);
-
     // send notification that directory is about to be deleted
     if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
         FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
@@ -343,7 +339,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
 
             if (!fcb->adsxattr.Buffer) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -357,7 +353,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
 
             if (!fcb->adsdata.Buffer) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -374,7 +370,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
         if (!fcb->sd) {
             ERR("out of memory\n");
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -392,7 +388,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
 
             if (!ext2) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -420,7 +416,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
                 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
                 if (!ext2->csum) {
                     ERR("out of memory\n");
-                    free_fcb(Vcb, fcb);
+                    free_fcb(fcb);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
 
@@ -442,7 +438,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
 
         if (!hl2) {
             ERR("out of memory\n");
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -455,7 +451,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         if (!hl2->name.Buffer) {
             ERR("out of memory\n");
             ExFreePool(hl2);
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -468,7 +464,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             ERR("out of memory\n");
             ExFreePool(hl2->name.Buffer);
             ExFreePool(hl2);
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -485,7 +481,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
         if (!fcb->reparse_xattr.Buffer) {
             ERR("out of memory\n");
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -498,7 +494,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
         if (!fcb->ea_xattr.Buffer) {
             ERR("out of memory\n");
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -518,7 +514,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
 
             if (!xa2) {
                 ERR("out of memory\n");
-                free_fcb(Vcb, fcb);
+                free_fcb(fcb);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -666,11 +662,13 @@ static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb
     Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
 
     if (!NT_SUCCESS(Status)) {
+        reap_fcb(fcb);
         ERR("SeAssignSecurity returned %08x\n", Status);
         return Status;
     }
 
     if (!fcb->sd) {
+        reap_fcb(fcb);
         ERR("SeAssignSecurity returned NULL security descriptor\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -689,16 +687,12 @@ static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb
 
     fcb->inode_item_changed = TRUE;
 
-    InsertTailList(&r->fcbs, &fcb->list_entry);
-    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
     fcb->Header.AllocationSize.QuadPart = 0;
     fcb->Header.FileSize.QuadPart = 0;
     fcb->Header.ValidDataLength.QuadPart = 0;
 
     fcb->created = TRUE;
-    mark_fcb_dirty(fcb);
 
     if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
         fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
@@ -722,6 +716,14 @@ static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb
 
     RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
 
+    acquire_fcb_lock_exclusive(Vcb);
+    InsertTailList(&r->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    r->fcbs_version++;
+    release_fcb_lock(Vcb);
+
+    mark_fcb_dirty(fcb);
+
     *pfcb = fcb;
 
     return STATUS_SUCCESS;
@@ -735,11 +737,15 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
     BTRFS_TIME now;
     file_ref* origparent;
 
+    // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
+
     InitializeListHead(&move_list);
 
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
 
+    acquire_fcb_lock_exclusive(fileref->fcb->Vcb);
+
     me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
 
     if (!me) {
@@ -1020,9 +1026,9 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
         me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
         increase_fileref_refcount(me->dummyfileref->parent);
 
-        ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->nonpaged->children_lock, TRUE);
+        ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
         InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
-        ExReleaseResourceLite(&me->dummyfileref->parent->nonpaged->children_lock);
+        ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
 
         me->dummyfileref->debug_desc = me->fileref->debug_desc;
 
@@ -1104,12 +1110,12 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
                 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
             }
 
-            free_fileref(fileref->fcb->Vcb, me->fileref->parent);
+            free_fileref(me->fileref->parent);
             me->fileref->parent = destdir;
 
-            ExAcquireResourceExclusiveLite(&me->fileref->parent->nonpaged->children_lock, TRUE);
+            ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
             InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
-            ExReleaseResourceLite(&me->fileref->parent->nonpaged->children_lock);
+            ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
 
             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
             me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
@@ -1237,16 +1243,21 @@ end:
         me = CONTAINING_RECORD(le, move_entry, list_entry);
 
         if (me->dummyfcb)
-            free_fcb(fileref->fcb->Vcb, me->dummyfcb);
+            free_fcb(me->dummyfcb);
 
         if (me->dummyfileref)
-            free_fileref(fileref->fcb->Vcb, me->dummyfileref);
+            free_fileref(me->dummyfileref);
 
-        free_fileref(fileref->fcb->Vcb, me->fileref);
+        free_fileref(me->fileref);
 
         ExFreePool(me);
     }
 
+    destdir->fcb->subvol->fcbs_version++;
+    fileref->fcb->subvol->fcbs_version++;
+
+    release_fcb_lock(fileref->fcb->Vcb);
+
     return Status;
 }
 
@@ -1390,7 +1401,7 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     }
 
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-    acquire_fcb_lock_exclusive(Vcb);
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
 
     if (fcb->ads) {
@@ -1457,7 +1468,7 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
         }
 
         if (fileref == oldfileref || oldfileref->deleted) {
-            free_fileref(Vcb, oldfileref);
+            free_fileref(oldfileref);
             oldfileref = NULL;
         }
     }
@@ -1726,10 +1737,10 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     fileref->created = TRUE;
     fileref->parent = related;
 
-    ExAcquireResourceExclusiveLite(&fileref->parent->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
     InsertHeadList(&fileref->list_entry, &fr2->list_entry);
     RemoveEntryList(&fileref->list_entry);
-    ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock);
+    ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
 
     mark_fileref_dirty(fr2);
     mark_fileref_dirty(fileref);
@@ -1794,9 +1805,9 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
         ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
     }
 
-    ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
     InsertTailList(&related->children, &fileref->list_entry);
-    ExReleaseResourceLite(&related->nonpaged->children_lock);
+    ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
 
     if (fcb->inode_item.st_nlink > 1) {
         // add new hardlink entry to fcb
@@ -1900,7 +1911,7 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     fr2->parent->fcb->inode_item.st_ctime = now;
     fr2->parent->fcb->inode_item.st_mtime = now;
 
-    free_fileref(Vcb, fr2);
+    free_fileref(fr2);
 
     fr2->parent->fcb->inode_item_changed = TRUE;
     mark_fcb_dirty(fr2->parent->fcb);
@@ -1913,13 +1924,13 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
 
 end:
     if (oldfileref)
-        free_fileref(Vcb, oldfileref);
+        free_fileref(oldfileref);
 
     if (!NT_SUCCESS(Status) && related)
-        free_fileref(Vcb, related);
+        free_fileref(related);
 
     if (!NT_SUCCESS(Status) && fr2)
-        free_fileref(Vcb, fr2);
+        free_fileref(fr2);
 
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -1927,7 +1938,7 @@ end:
         do_rollback(Vcb, &rollback);
 
     ExReleaseResourceLite(fcb->Header.Resource);
-    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
     ExReleaseResourceLite(&Vcb->tree_lock);
 
     return Status;
@@ -2227,7 +2238,7 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
     }
 
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-    acquire_fcb_lock_exclusive(Vcb);
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
 
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
@@ -2299,7 +2310,7 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
                 goto end;
             }
         } else {
-            free_fileref(Vcb, oldfileref);
+            free_fileref(oldfileref);
             oldfileref = NULL;
         }
     }
@@ -2364,9 +2375,9 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
     fr2->dc = dc;
     dc->fileref = fr2;
 
-    ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
     InsertTailList(&related->children, &fr2->list_entry);
-    ExReleaseResourceLite(&related->nonpaged->children_lock);
+    ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
 
     // add hardlink for existing fileref, if it's not there already
     if (IsListEmpty(&fcb->hardlinks)) {
@@ -2447,7 +2458,7 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
     InsertTailList(&fcb->hardlinks, &hl->list_entry);
 
     mark_fileref_dirty(fr2);
-    free_fileref(Vcb, fr2);
+    free_fileref(fr2);
 
     // update inode's INODE_ITEM
 
@@ -2482,13 +2493,13 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
 
 end:
     if (oldfileref)
-        free_fileref(Vcb, oldfileref);
+        free_fileref(oldfileref);
 
     if (!NT_SUCCESS(Status) && related)
-        free_fileref(Vcb, related);
+        free_fileref(related);
 
     if (!NT_SUCCESS(Status) && fr2)
-        free_fileref(Vcb, fr2);
+        free_fileref(fr2);
 
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -2496,7 +2507,7 @@ end:
         do_rollback(Vcb, &rollback);
 
     ExReleaseResourceLite(fcb->Header.Resource);
-    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
     ExReleaseResourceLite(&Vcb->tree_lock);
 
     return Status;
@@ -3186,282 +3197,7 @@ static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORM
 
     return STATUS_SUCCESS;
 }
-#endif /* __REACTOS__ */
-
-NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
-                               root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
-    NTSTATUS Status;
-    fcb* fcb;
-    UINT64 parent = 0;
-    UNICODE_STRING name;
-    BOOL hl_alloc = FALSE;
-    file_ref *parfr, *fr;
-
-    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("open_fcb returned %08x\n", Status);
-        return Status;
-    }
-
-    if (fcb->fileref) {
-        *pfr = fcb->fileref;
-        increase_fileref_refcount(fcb->fileref);
-        return STATUS_SUCCESS;
-    }
-
-    // find hardlink if fcb doesn't have any loaded
-    if (IsListEmpty(&fcb->hardlinks)) {
-        KEY searchkey;
-        traverse_ptr tp;
-
-        searchkey.obj_id = fcb->inode;
-        searchkey.obj_type = TYPE_INODE_EXTREF;
-        searchkey.offset = 0xffffffffffffffff;
-
-        Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("find_item returned %08x\n", Status);
-            free_fcb(Vcb, fcb);
-            return Status;
-        }
-
-        if (tp.item->key.obj_id == fcb->inode) {
-            if (tp.item->key.obj_type == TYPE_INODE_REF) {
-                INODE_REF* ir;
-                ULONG stringlen;
-
-                ir = (INODE_REF*)tp.item->data;
-
-                parent = tp.item->key.offset;
-
-                Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                    free_fcb(Vcb, fcb);
-                    return Status;
-                }
-
-                name.Length = name.MaximumLength = (UINT16)stringlen;
-
-                if (stringlen == 0)
-                    name.Buffer = NULL;
-                else {
-                    name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
-                    if (!name.Buffer) {
-                        ERR("out of memory\n");
-                        free_fcb(Vcb, fcb);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-
-                    Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        ExFreePool(name.Buffer);
-                        free_fcb(Vcb, fcb);
-                        return Status;
-                    }
-
-                    hl_alloc = TRUE;
-                }
-            } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
-                INODE_EXTREF* ier;
-                ULONG stringlen;
-
-                ier = (INODE_EXTREF*)tp.item->data;
-
-                parent = ier->dir;
-
-                Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                    free_fcb(Vcb, fcb);
-                    return Status;
-                }
-
-                name.Length = name.MaximumLength = (UINT16)stringlen;
-
-                if (stringlen == 0)
-                    name.Buffer = NULL;
-                else {
-                    name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
-                    if (!name.Buffer) {
-                        ERR("out of memory\n");
-                        free_fcb(Vcb, fcb);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-
-                    Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        ExFreePool(name.Buffer);
-                        free_fcb(Vcb, fcb);
-                        return Status;
-                    }
-
-                    hl_alloc = TRUE;
-                }
-
-            }
-        }
-    } else {
-        hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
-
-        name = hl->name;
-        parent = hl->parent;
-    }
-
-    if (parent == 0) {
-        ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
-        free_fcb(Vcb, fcb);
-        if (hl_alloc) ExFreePool(name.Buffer);
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    if (parent == inode) { // subvolume root
-        KEY searchkey;
-        traverse_ptr tp;
-
-        searchkey.obj_id = subvol->id;
-        searchkey.obj_type = TYPE_ROOT_BACKREF;
-        searchkey.offset = 0xffffffffffffffff;
-
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("find_item returned %08x\n", Status);
-            free_fcb(Vcb, fcb);
-            if (hl_alloc) ExFreePool(name.Buffer);
-            return Status;
-        }
-
-        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-            ROOT_REF* rr = (ROOT_REF*)tp.item->data;
-            LIST_ENTRY* le;
-            root* r = NULL;
-            ULONG stringlen;
-
-            if (tp.item->size < sizeof(ROOT_REF)) {
-                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(ROOT_REF));
-                free_fcb(Vcb, fcb);
-                if (hl_alloc) ExFreePool(name.Buffer);
-                return STATUS_INTERNAL_ERROR;
-            }
-
-            if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->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, offsetof(ROOT_REF, name[0]) + rr->n);
-                free_fcb(Vcb, fcb);
-                if (hl_alloc) ExFreePool(name.Buffer);
-                return STATUS_INTERNAL_ERROR;
-            }
-
-            le = Vcb->roots.Flink;
-            while (le != &Vcb->roots) {
-                root* r2 = CONTAINING_RECORD(le, root, list_entry);
-
-                if (r2->id == tp.item->key.offset) {
-                    r = r2;
-                    break;
-                }
-
-                le = le->Flink;
-            }
-
-            if (!r) {
-                ERR("couldn't find subvol %llx\n", tp.item->key.offset);
-                free_fcb(Vcb, fcb);
-                if (hl_alloc) ExFreePool(name.Buffer);
-                return STATUS_INTERNAL_ERROR;
-            }
-
-            Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("open_fileref_by_inode returned %08x\n", Status);
-                free_fcb(Vcb, fcb);
-                if (hl_alloc) ExFreePool(name.Buffer);
-                return Status;
-            }
-
-            if (hl_alloc) {
-                ExFreePool(name.Buffer);
-                hl_alloc = FALSE;
-            }
-
-            Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                free_fcb(Vcb, fcb);
-                return Status;
-            }
-
-            name.Length = name.MaximumLength = (UINT16)stringlen;
-
-            if (stringlen == 0)
-                name.Buffer = NULL;
-            else {
-                name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
-                if (!name.Buffer) {
-                    ERR("out of memory\n");
-                    free_fcb(Vcb, fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-
-                Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                    ExFreePool(name.Buffer);
-                    free_fcb(Vcb, fcb);
-                    return Status;
-                }
-
-                hl_alloc = TRUE;
-            }
-        } else {
-            ERR("couldn't find parent for subvol %llx\n", subvol->id);
-            free_fcb(Vcb, fcb);
-            if (hl_alloc) ExFreePool(name.Buffer);
-            return STATUS_INTERNAL_ERROR;
-        }
-    } else {
-        Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("open_fileref_by_inode returned %08x\n", Status);
-            free_fcb(Vcb, fcb);
-
-            if (hl_alloc)
-                ExFreePool(name.Buffer);
-
-            return Status;
-        }
-    }
-
-    Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
-
-    if (!NT_SUCCESS(Status)) {
-        ERR("open_fileref_child returned %08x\n", Status);
-
-        if (hl_alloc)
-            ExFreePool(name.Buffer);
-
-        free_fcb(Vcb, fcb);
-        free_fileref(Vcb, parfr);
-
-        return Status;
-    }
-
-    *pfr = fr;
-
-    if (hl_alloc)
-        ExFreePool(name.Buffer);
-
-    free_fcb(Vcb, fcb);
-    free_fileref(Vcb, parfr);
-
-    return STATUS_SUCCESS;
-}
 
-#ifndef __REACTOS__
 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
     NTSTATUS Status;
     LIST_ENTRY* le;
@@ -3517,7 +3253,7 @@ static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_
             len = bytes_needed;
         }
     } else {
-        acquire_fcb_lock_exclusive(fcb->Vcb);
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
 
         if (IsListEmpty(&fcb->hardlinks)) {
             bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
@@ -3603,14 +3339,14 @@ static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_
                         }
                     }
 
-                    free_fileref(fcb->Vcb, parfr);
+                    free_fileref(parfr);
                 }
 
                 le = le->Flink;
             }
         }
 
-        release_fcb_lock(fcb->Vcb);
+        ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
     }
 
     fli->BytesNeeded = bytes_needed;
index 6353abb..6784de5 100644 (file)
@@ -2704,7 +2704,11 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY*
                 }
             }
 
-            free_fcb(Vcb, c->old_cache);
+            free_fcb(c->old_cache);
+
+            if (c->old_cache->refcount == 0)
+                reap_fcb(c->old_cache);
+
             c->old_cache = NULL;
         }
 
@@ -2810,6 +2814,18 @@ static NTSTATUS split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirs
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    if (t->header.level > 0) {
+        nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+        if (!nt->nonpaged) {
+            ERR("out of memory\n");
+            ExFreePool(nt);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        ExInitializeFastMutex(&nt->nonpaged->mutex);
+    } else
+        nt->nonpaged = NULL;
+
     RtlCopyMemory(&nt->header, &t->header, sizeof(tree_header));
     nt->header.address = 0;
     nt->header.generation = Vcb->superblock.generation;
@@ -2924,6 +2940,15 @@ static NTSTATUS split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirs
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+    if (!pt->nonpaged) {
+        ERR("out of memory\n");
+        ExFreePool(pt);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    ExInitializeFastMutex(&pt->nonpaged->mutex);
+
     RtlCopyMemory(&pt->header, &nt->header, sizeof(tree_header));
     pt->header.address = 0;
     pt->header.num_items = 2;
@@ -3103,7 +3128,6 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, BOOL* done,
     tree_data* nextparitem = NULL;
     NTSTATUS Status;
     tree *next_tree, *par;
-    BOOL loaded;
 
     *done = FALSE;
 
@@ -3127,10 +3151,12 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, BOOL* done,
 
     TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem->key.obj_id, nextparitem->key.obj_type, nextparitem->key.offset);
 
-    Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("do_load_tree returned %08x\n", Status);
-        return Status;
+    if (!nextparitem->treeholder.tree) {
+        Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_load_tree returned %08x\n", Status);
+            return Status;
+        }
     }
 
     if (!is_tree_unique(Vcb, nextparitem->treeholder.tree, Irp))
@@ -3667,7 +3693,27 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder*
     NTSTATUS Status;
 
     if (!th->tree) {
-        Status = load_tree(Vcb, th->address, r, &th->tree, th->generation, NULL);
+        UINT8* buf;
+        chunk* c;
+
+        buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+        if (!buf) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        Status = read_data(Vcb, th->address, Vcb->superblock.node_size, NULL, TRUE, buf, NULL,
+                           &c, Irp, th->generation, FALSE, NormalPagePriority);
+        if (!NT_SUCCESS(Status)) {
+            ERR("read_data returned 0x%08x\n", Status);
+            ExFreePool(buf);
+            return Status;
+        }
+
+        Status = load_tree(Vcb, th->address, buf, r, &th->tree);
+
+        if (!th->tree || th->tree->buf != buf)
+            ExFreePool(buf);
 
         if (!NT_SUCCESS(Status)) {
             ERR("load_tree(%llx) returned %08x\n", th->address, Status);
@@ -4070,6 +4116,7 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp) {
     }
 
     c->created = FALSE;
+    c->oldused = c->used;
 
     return STATUS_SUCCESS;
 }
@@ -5194,7 +5241,10 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* batchlis
 
         Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
 
-        free_fcb(Vcb, c->cache);
+        free_fcb(c->cache);
+
+        if (c->cache->refcount == 0)
+            reap_fcb(c->cache);
 
         if (!NT_SUCCESS(Status)) {
             ERR("flush_fcb returned %08x\n", Status);
@@ -6921,7 +6971,7 @@ static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback)
         file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&Vcb->dirty_filerefs), file_ref, list_entry_dirty);
 
         flush_fileref(fr, &batchlist, Irp);
-        free_fileref(Vcb, fr);
+        free_fileref(fr);
 
 #ifdef DEBUG_FLUSH_TIMES
         filerefs++;
@@ -6961,7 +7011,7 @@ static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback)
             Status = flush_fcb(fcb, FALSE, &batchlist, Irp);
             ExReleaseResourceLite(fcb->Header.Resource);
 
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
 
             if (!NT_SUCCESS(Status)) {
                 ERR("flush_fcb returned %08x\n", Status);
@@ -6994,7 +7044,7 @@ static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback)
             ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
             Status = flush_fcb(fcb, FALSE, &batchlist, Irp);
             ExReleaseResourceLite(fcb->Header.Resource);
-            free_fcb(Vcb, fcb);
+            free_fcb(fcb);
 
             if (!NT_SUCCESS(Status)) {
                 ERR("flush_fcb returned %08x\n", Status);
index f6248e9..cd3de36 100644 (file)
@@ -46,11 +46,11 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     Status = flush_fcb(fcb, FALSE, batchlist, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("flush_fcb returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        free_fcb(fcb);
         return Status;
     }
 
-    free_fcb(Vcb, fcb);
+    free_fcb(fcb);
 
     return STATUS_SUCCESS;
 }
@@ -103,7 +103,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb, LIST_ENTRY* batchlist, PI
                         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
 
                         if (c->offset == tp.item->key.offset && c->cache) {
-                            free_fcb(Vcb, c->cache);
+                            reap_fcb(c->cache);
                             c->cache = NULL;
                         }
 
@@ -508,7 +508,7 @@ NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, BOOL load
 
     if (c->cache->inode_item.st_size == 0) {
         WARN("cache had zero length\n");
-        free_fcb(Vcb, c->cache);
+        free_fcb(c->cache);
         c->cache = NULL;
         return STATUS_NOT_FOUND;
     }
@@ -524,11 +524,16 @@ NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, BOOL load
 
     if (!data) {
         ERR("out of memory\n");
-        free_fcb(Vcb, c->cache);
+        free_fcb(c->cache);
         c->cache = NULL;
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    if (c->chunk_item->size < 0x6400000) { // 100 MB
+        WARN("deleting free space cache for chunk smaller than 100MB\n");
+        goto clearcache;
+    }
+
     Status = read_file(c->cache, data, 0, c->cache->inode_item.st_size, NULL, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("read_file returned %08x\n", Status);
@@ -537,7 +542,7 @@ NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, BOOL load
         c->cache->deleted = TRUE;
         mark_fcb_dirty(c->cache);
 
-        free_fcb(Vcb, c->cache);
+        free_fcb(c->cache);
         c->cache = NULL;
         return STATUS_NOT_FOUND;
     }
@@ -1126,7 +1131,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         fsi = ExAllocatePoolWithTag(PagedPool, sizeof(FREE_SPACE_ITEM), ALLOC_TAG);
         if (!fsi) {
             ERR("out of memory\n");
-            free_fcb(Vcb, c->cache);
+            reap_fcb(c->cache);
             c->cache = NULL;
             return STATUS_INSUFFICIENT_RESOURCES;
         }
@@ -1139,7 +1144,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             ExFreePool(fsi);
-            free_fcb(Vcb, c->cache);
+            reap_fcb(c->cache);
             c->cache = NULL;
             return Status;
         }
@@ -1149,7 +1154,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_tree_item returned %08x\n", Status);
                 ExFreePool(fsi);
-                free_fcb(Vcb, c->cache);
+                reap_fcb(c->cache);
                 c->cache = NULL;
                 return Status;
             }
@@ -1163,7 +1168,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         if (!NT_SUCCESS(Status)) {
             ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(fsi);
-            free_fcb(Vcb, c->cache);
+            reap_fcb(c->cache);
             c->cache = NULL;
             return Status;
         }
@@ -1173,7 +1178,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         Status = insert_cache_extent(c->cache, 0, new_cache_size, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("insert_cache_extent returned %08x\n", Status);
-            free_fcb(Vcb, c->cache);
+            reap_fcb(c->cache);
             c->cache = NULL;
             return Status;
         }
@@ -1184,7 +1189,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("flush_fcb returned %08x\n", Status);
-            free_fcb(Vcb, c->cache);
+            free_fcb(c->cache);
             c->cache = NULL;
             return Status;
         }
@@ -1357,7 +1362,7 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENT
     while (le != &Vcb->chunks) {
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
 
-        if (c->space_changed) {
+        if (c->space_changed && c->chunk_item->size >= 0x6400000) { // 100MB
             BOOL b;
 
             acquire_chunk_lock(c, Vcb);
@@ -1827,7 +1832,7 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollba
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
 
-        if (c->space_changed) {
+        if (c->space_changed && c->chunk_item->size >= 0x6400000) { // 100MB
             acquire_chunk_lock(c, Vcb);
             Status = update_chunk_cache(Vcb, c, &now, &batchlist, Irp, rollback);
             release_chunk_lock(c, Vcb);
index 3b13e89..3845965 100644 (file)
@@ -435,7 +435,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
-        free_fileref(Vcb, fr);
+        free_fileref(fr);
         goto end;
     }
 
@@ -448,9 +448,9 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     fr->dc = dc;
     dc->fileref = fr;
 
-    ExAcquireResourceExclusiveLite(&fileref->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
     InsertTailList(&fileref->children, &fr->list_entry);
-    ExReleaseResourceLite(&fileref->nonpaged->children_lock);
+    ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
 
     increase_fileref_refcount(fileref);
 
@@ -462,7 +462,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
 
     fr->fcb->subvol->parent = fileref->fcb->subvol->id;
 
-    free_fileref(Vcb, fr);
+    free_fileref(fr);
 
     // change fcb's INODE_ITEM
 
@@ -641,11 +641,11 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
     if (NT_SUCCESS(Status)) {
         if (!fr2->deleted) {
             WARN("file already exists\n");
-            free_fileref(Vcb, fr2);
+            free_fileref(fr2);
             Status = STATUS_OBJECT_NAME_COLLISION;
             goto end3;
         } else
-            free_fileref(Vcb, fr2);
+            free_fileref(fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("open_fileref returned %08x\n", Status);
         goto end3;
@@ -726,7 +726,7 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
             Status = STATUS_SUCCESS;
         } else {
             send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
-            free_fileref(Vcb, fr);
+            free_fileref(fr);
         }
     }
 
@@ -866,11 +866,11 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     if (NT_SUCCESS(Status)) {
         if (!fr2->deleted) {
             WARN("file already exists\n");
-            free_fileref(Vcb, fr2);
+            free_fileref(fr2);
             Status = STATUS_OBJECT_NAME_COLLISION;
             goto end;
         } else
-            free_fileref(Vcb, fr2);
+            free_fileref(fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("open_fileref returned %08x\n", Status);
         goto end;
@@ -1005,6 +1005,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     acquire_fcb_lock_exclusive(Vcb);
     InsertTailList(&r->fcbs, &rootfcb->list_entry);
     InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
+    r->fcbs_version++;
     release_fcb_lock(Vcb);
 
     rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
@@ -1049,9 +1050,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     if (!fr) {
         ERR("out of memory\n");
 
-        acquire_fcb_lock_exclusive(Vcb);
-        free_fcb(Vcb, rootfcb);
-        release_fcb_lock(Vcb);
+        reap_fcb(rootfcb);
 
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
@@ -1073,9 +1072,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     fr->fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
     if (!fr->fcb->hash_ptrs) {
         ERR("out of memory\n");
-        acquire_fcb_lock_exclusive(Vcb);
-        free_fileref(Vcb, fr);
-        release_fcb_lock(Vcb);
+        free_fileref(fr);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
@@ -1085,18 +1082,16 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     fr->fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
     if (!fr->fcb->hash_ptrs_uc) {
         ERR("out of memory\n");
-        acquire_fcb_lock_exclusive(Vcb);
-        free_fileref(Vcb, fr);
-        release_fcb_lock(Vcb);
+        free_fileref(fr);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
 
     RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
 
-    ExAcquireResourceExclusiveLite(&fileref->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
     InsertTailList(&fileref->children, &fr->list_entry);
-    ExReleaseResourceLite(&fileref->nonpaged->children_lock);
+    ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
 
     increase_fileref_refcount(fileref);
 
@@ -1154,11 +1149,8 @@ end:
     }
 
 end2:
-    if (fr) {
-        acquire_fcb_lock_exclusive(Vcb);
-        free_fileref(Vcb, fr);
-        release_fcb_lock(Vcb);
-    }
+    if (fr)
+        free_fileref(fr);
 
     return Status;
 }
@@ -2233,15 +2225,15 @@ static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
     if (Vcb->locked)
         return STATUS_SUCCESS;
 
-    acquire_fcb_lock_exclusive(Vcb);
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
 
     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
         Status = STATUS_ACCESS_DENIED;
-        release_fcb_lock(Vcb);
+        ExReleaseResourceLite(&Vcb->fileref_lock);
         goto end;
     }
 
-    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
 
     if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
@@ -3768,8 +3760,6 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     name.Length = name.MaximumLength = bmn->namelen;
     name.Buffer = bmn->name;
 
-    acquire_fcb_lock_exclusive(Vcb);
-
     Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, TRUE);
     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("find_file_in_dir returned %08x\n", Status);
@@ -3782,36 +3772,6 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
         goto end;
     }
 
-    if (bmn->inode == 0) {
-        inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
-        lastle = parfcb->subvol->fcbs.Blink;
-    } else {
-        if (bmn->inode > (UINT64)parfcb->subvol->lastinode) {
-            inode = parfcb->subvol->lastinode = bmn->inode;
-            lastle = parfcb->subvol->fcbs.Blink;
-        } else {
-            LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
-
-            lastle = parfcb->subvol->fcbs.Blink;;
-            while (le != &parfcb->subvol->fcbs) {
-                struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
-
-                if (fcb2->inode == bmn->inode && !fcb2->deleted) {
-                    WARN("inode collision\n");
-                    Status = STATUS_INVALID_PARAMETER;
-                    goto end;
-                } else if (fcb2->inode > bmn->inode) {
-                    lastle = fcb2->list_entry.Blink;
-                    break;
-                }
-
-                le = le->Flink;
-            }
-
-            inode = bmn->inode;
-        }
-    }
-
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
 
@@ -3897,8 +3857,6 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
 
     InterlockedIncrement(&parfcb->refcount);
     fcb->subvol = parfcb->subvol;
-    fcb->inode = inode;
-    fcb->type = bmn->type;
 
     SeCaptureSubjectContext(&subjcont);
 
@@ -3907,7 +3865,7 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
 
     if (!NT_SUCCESS(Status)) {
         ERR("SeAssignSecurityEx returned %08x\n", Status);
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         goto end;
     }
 
@@ -3922,10 +3880,52 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
 
     find_gid(fcb, parfcb, &subjcont);
 
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
+    acquire_fcb_lock_exclusive(Vcb);
+
+    if (bmn->inode == 0) {
+        inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
+        lastle = parfcb->subvol->fcbs.Blink;
+    } else {
+        if (bmn->inode > (UINT64)parfcb->subvol->lastinode) {
+            inode = parfcb->subvol->lastinode = bmn->inode;
+            lastle = parfcb->subvol->fcbs.Blink;
+        } else {
+            LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
+
+            lastle = parfcb->subvol->fcbs.Blink;;
+            while (le != &parfcb->subvol->fcbs) {
+                struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+                if (fcb2->inode == bmn->inode && !fcb2->deleted) {
+                    release_fcb_lock(Vcb);
+                    ExReleaseResourceLite(&Vcb->fileref_lock);
+
+                    WARN("inode collision\n");
+                    Status = STATUS_INVALID_PARAMETER;
+                    goto end;
+                } else if (fcb2->inode > bmn->inode) {
+                    lastle = fcb2->list_entry.Blink;
+                    break;
+                }
+
+                le = le->Flink;
+            }
+
+            inode = bmn->inode;
+        }
+    }
+
+    fcb->inode = inode;
+    fcb->type = bmn->type;
+
     fileref = create_fileref(Vcb);
     if (!fileref) {
+        release_fcb_lock(Vcb);
+        ExReleaseResourceLite(&Vcb->fileref_lock);
+
         ERR("out of memory\n");
-        free_fcb(Vcb, fcb);
+        reap_fcb(fcb);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
@@ -3933,16 +3933,16 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     fileref->fcb = fcb;
 
     fcb->created = TRUE;
-    mark_fcb_dirty(fcb);
-
     fileref->created = TRUE;
-    mark_fileref_dirty(fileref);
 
     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
     fcb->subvol->root_item.ctime = now;
 
     fileref->parent = parfileref;
 
+    mark_fcb_dirty(fcb);
+    mark_fileref_dirty(fileref);
+
     Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8, &name, fcb->type, &dc);
     if (!NT_SUCCESS(Status))
         WARN("add_dir_child returned %08x\n", Status);
@@ -3950,17 +3950,20 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     fileref->dc = dc;
     dc->fileref = fileref;
 
-    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
     InsertTailList(&parfileref->children, &fileref->list_entry);
-    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+    ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
 
     increase_fileref_refcount(parfileref);
 
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs) {
+            release_fcb_lock(Vcb);
+            ExReleaseResourceLite(&Vcb->fileref_lock);
+
             ERR("out of memory\n");
-            free_fileref(Vcb, fileref);
+            free_fileref(fileref);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
@@ -3969,8 +3972,11 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
 
         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs_uc) {
+            release_fcb_lock(Vcb);
+            ExReleaseResourceLite(&Vcb->fileref_lock);
+
             ERR("out of memory\n");
-            free_fileref(Vcb, fileref);
+            free_fileref(fileref);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
@@ -3995,7 +4001,11 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     if (!parccb->user_set_write_time)
         parfcb->inode_item.st_mtime = now;
 
+    parfcb->subvol->fcbs_version++;
+
     ExReleaseResourceLite(parfcb->Header.Resource);
+    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
 
     parfcb->inode_item_changed = TRUE;
     mark_fcb_dirty(parfcb);
@@ -4008,7 +4018,6 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     Status = STATUS_SUCCESS;
 
 end:
-    release_fcb_lock(Vcb);
 
     ExFreePool(utf8.Buffer);
 
@@ -4458,11 +4467,11 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, UINT64 id, WCHAR* out, UL
         return STATUS_INTERNAL_ERROR;
     }
 
-    acquire_fcb_lock_shared(Vcb);
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
 
     Status = open_fileref_by_inode(Vcb, r, r->root_item.objid, &fr, Irp);
     if (!NT_SUCCESS(Status)) {
-        release_fcb_lock(Vcb);
+        ExReleaseResourceLite(&Vcb->fileref_lock);
         ERR("open_fileref_by_inode returned %08x\n", Status);
         return Status;
     }
@@ -4478,9 +4487,9 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, UINT64 id, WCHAR* out, UL
     else
         ERR("fileref_get_filename returned %08x\n", Status);
 
-    free_fileref(Vcb, fr);
+    free_fileref(fr);
 
-    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
 
     return Status;
 }
index fbb535f..e122f72 100644 (file)
@@ -161,7 +161,7 @@ static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject) {
 
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
 
-    acquire_fcb_lock_exclusive(Vcb);
+    ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
 
     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
         Status = STATUS_ACCESS_DENIED;
@@ -175,7 +175,7 @@ static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject) {
     }
 
 end:
-    release_fcb_lock(Vcb);
+    ExReleaseResourceLite(&Vcb->fileref_lock);
     ExReleaseResourceLite(&Vcb->tree_lock);
 
     return STATUS_SUCCESS;
@@ -187,8 +187,6 @@ NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-    acquire_fcb_lock_exclusive(Vcb);
-
     if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
@@ -216,8 +214,6 @@ NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
     Status = STATUS_SUCCESS;
 end:
-    release_fcb_lock(Vcb);
-
     ExReleaseResourceLite(&Vcb->tree_lock);
 
     return Status;
index 2eb19b1..1abfbf4 100644 (file)
@@ -3359,7 +3359,7 @@ NTSTATUS NTAPI drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     if (Irp->Flags & IRP_PAGING_IO)
         wait = TRUE;
 
-    if (!(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer->DataSectionObject) {
+    if (!(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
         IO_STATUS_BLOCK iosb;
 
         CcFlushCache(FileObject->SectionObjectPointer, &IrpSp->Parameters.Read.ByteOffset, IrpSp->Parameters.Read.Length, &iosb);
index 65e7d50..5ec2813 100644 (file)
@@ -1,6 +1,7 @@
-//{{NO_DEPENDENCIES}}
+//{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
 // Used by btrfs.rc
+//
 
 // Next default values for new objects
 // 
index 7333a1e..67a4387 100644 (file)
@@ -483,8 +483,6 @@ void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lo
         }
 
         if (remove) {
-            PDEVICE_OBJECT pdo;
-
             if (vde->name.Buffer)
                 ExFreePool(vde->name.Buffer);
 
@@ -493,11 +491,7 @@ void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lo
 
             ExDeleteResourceLite(&pdode->child_lock);
 
-            pdo = vde->pdo;
             IoDeleteDevice(vde->device);
-
-            if (!no_pnp)
-                IoDeleteDevice(pdo);
         }
     } else
         ExReleaseResourceLite(&pdode->child_lock);
index 9785e12..0283386 100644 (file)
 
 #include "btrfs_drv.h"
 
-NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT64 generation, PIRP Irp) {
-    UINT8* buf;
-    NTSTATUS Status;
+NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, UINT8* buf, root* r, tree** pt) {
     tree_header* th;
     tree* t;
     tree_data* td;
-    chunk* c;
     UINT8 h;
     BOOL inserted;
     LIST_ENTRY* le;
 
-    buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
-    if (!buf) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, &c, Irp, generation, FALSE, NormalPagePriority);
-    if (!NT_SUCCESS(Status)) {
-        ERR("read_data returned 0x%08x\n", Status);
-        ExFreePool(buf);
-        return Status;
-    }
-
     th = (tree_header*)buf;
 
     t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
     if (!t) {
         ERR("out of memory\n");
-        ExFreePool(buf);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    if (th->level > 0) {
+        t->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+        if (!t->nonpaged) {
+            ERR("out of memory\n");
+            ExFreePool(t);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        ExInitializeFastMutex(&t->nonpaged->mutex);
+    } else
+        t->nonpaged = NULL;
+
     RtlCopyMemory(&t->header, th, sizeof(tree_header));
     t->hash = calc_crc32c(0xffffffff, (UINT8*)&addr, sizeof(UINT64));
     t->has_address = TRUE;
@@ -73,7 +68,6 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
         if ((t->header.num_items * sizeof(leaf_node)) + sizeof(tree_header) > Vcb->superblock.node_size) {
             ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items);
             ExFreePool(t);
-            ExFreePool(buf);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -82,7 +76,6 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
             if (!td) {
                 ERR("out of memory\n");
                 ExFreePool(t);
-                ExFreePool(buf);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -97,7 +90,6 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
                 ERR("overlarge item in tree %llx: %u > %u\n", addr, ln[i].size, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node));
                 ExFreeToPagedLookasideList(&t->Vcb->tree_data_lookaside, td);
                 ExFreePool(t);
-                ExFreePool(buf);
                 return STATUS_INTERNAL_ERROR;
             }
 
@@ -119,7 +111,6 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
         if ((t->header.num_items * sizeof(internal_node)) + sizeof(tree_header) > Vcb->superblock.node_size) {
             ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items);
             ExFreePool(t);
-            ExFreePool(buf);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -128,7 +119,6 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
             if (!td) {
                 ERR("out of memory\n");
                 ExFreePool(t);
-                ExFreePool(buf);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
@@ -145,9 +135,10 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
 
         t->size = t->header.num_items * sizeof(internal_node);
         t->buf = NULL;
-        ExFreePool(buf);
     }
 
+    ExAcquireFastMutex(&Vcb->trees_list_mutex);
+
     InsertTailList(&Vcb->trees, &t->list_entry);
 
     h = t->hash >> 24;
@@ -190,6 +181,8 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
     if (!Vcb->trees_ptrs[h] || t->list_entry_hash.Flink == Vcb->trees_ptrs[h])
         Vcb->trees_ptrs[h] = &t->list_entry_hash;
 
+    ExReleaseFastMutex(&Vcb->trees_list_mutex);
+
     TRACE("returning %p\n", t);
 
     *pt = t;
@@ -197,10 +190,79 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT6
     return STATUS_SUCCESS;
 }
 
-static tree* free_tree2(tree* t) {
+static NTSTATUS do_load_tree2(device_extension* Vcb, tree_holder* th, UINT8* buf, root* r, tree* t, tree_data* td) {
+    if (!th->tree) {
+        NTSTATUS Status;
+        tree* nt;
+
+        Status = load_tree(Vcb, th->address, buf, r, &nt);
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_tree returned %08x\n", Status);
+            return Status;
+        }
+
+        nt->parent = t;
+
+#ifdef DEBUG_PARANOID
+        if (t && t->header.level <= nt->header.level) int3;
+#endif
+
+        nt->paritem = td;
+
+        th->tree = nt;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, PIRP Irp) {
+    NTSTATUS Status;
+    UINT8* buf;
+    chunk* c;
+
+    buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+    if (!buf) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = read_data(Vcb, th->address, Vcb->superblock.node_size, NULL, TRUE, buf, NULL,
+                       &c, Irp, th->generation, FALSE, NormalPagePriority);
+    if (!NT_SUCCESS(Status)) {
+        ERR("read_data returned 0x%08x\n", Status);
+        ExFreePool(buf);
+        return Status;
+    }
+
+    if (t)
+        ExAcquireFastMutex(&t->nonpaged->mutex);
+    else
+        ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+    Status = do_load_tree2(Vcb, th, buf, r, t, td);
+
+    if (t)
+        ExReleaseFastMutex(&t->nonpaged->mutex);
+    else
+        ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+
+    if (!th->tree || th->tree->buf != buf)
+        ExFreePool(buf);
+
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_load_tree2 returned %08x\n", Status);
+        return Status;
+    }
+
+    return Status;
+}
+
+void free_tree(tree* t) {
     tree* par;
     root* r = t->root;
 
+    // No need to acquire lock, as this is only ever called while Vcb->tree_lock held exclusively
+
     par = t->parent;
 
     if (r && r->treeholder.tree != t)
@@ -245,59 +307,10 @@ static tree* free_tree2(tree* t) {
     if (t->buf)
         ExFreePool(t->buf);
 
-    ExFreePool(t);
-
-    return NULL;
-}
-
-NTSTATUS do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp) {
-    BOOL ret;
-
-    ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
-
-    if (!th->tree) {
-        NTSTATUS Status;
-        tree* nt;
-
-        Status = load_tree(Vcb, th->address, r, &nt, th->generation, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("load_tree returned %08x\n", Status);
-            ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
-            return Status;
-        }
-
-        nt->parent = t;
-
-#ifdef DEBUG_PARANOID
-        if (t && t->header.level <= nt->header.level) int3;
-#endif
-
-        nt->paritem = td;
-
-        th->tree = nt;
-
-        ret = TRUE;
-    } else
-        ret = FALSE;
-
-    ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
-
-    *loaded = ret;
+    if (t->nonpaged)
+        ExFreePool(t->nonpaged);
 
-    return STATUS_SUCCESS;
-}
-
-tree* free_tree(tree* t) {
-    tree* ret;
-    root* r = t->root;
-
-    ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
-
-    ret = free_tree2(t);
-
-    ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
-
-    return ret;
+    ExFreePool(t);
 }
 
 static __inline tree_data* first_item(tree* t) {
@@ -504,7 +517,6 @@ static NTSTATUS find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr*
         return STATUS_SUCCESS;
     } else {
         NTSTATUS Status;
-        BOOL loaded;
 
         while (td && td->treeholder.tree && IsListEmpty(&td->treeholder.tree->itemlist)) {
             td = prev_item(t, td);
@@ -520,7 +532,7 @@ static NTSTATUS find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr*
         }
 
         if (!td->treeholder.tree) {
-            Status = do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, Irp);
+            Status = do_load_tree(Vcb, &td->treeholder, t->root, t, td, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("do_load_tree returned %08x\n", Status);
                 return Status;
@@ -536,10 +548,9 @@ static NTSTATUS find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr*
 NTSTATUS find_item(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _Out_ traverse_ptr* tp,
                    _In_ const KEY* searchkey, _In_ BOOL ignore, _In_opt_ PIRP Irp) {
     NTSTATUS Status;
-    BOOL loaded;
 
     if (!r->treeholder.tree) {
-        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
+        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
@@ -556,10 +567,9 @@ NTSTATUS find_item(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension
 
 NTSTATUS find_item_to_level(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, UINT8 level, PIRP Irp) {
     NTSTATUS Status;
-    BOOL loaded;
 
     if (!r->treeholder.tree) {
-        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
+        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
@@ -583,7 +593,6 @@ BOOL find_next_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
     tree* t;
     tree_data *td = NULL, *next;
     NTSTATUS Status;
-    BOOL loaded;
 
     next = next_item(tp->tree, tp->item);
 
@@ -623,10 +632,12 @@ BOOL find_next_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
     if (!t)
         return FALSE;
 
-    Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("do_load_tree returned %08x\n", Status);
-        return FALSE;
+    if (!td->treeholder.tree) {
+        Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_load_tree returned %08x\n", Status);
+            return FALSE;
+        }
     }
 
     t = td->treeholder.tree;
@@ -636,10 +647,12 @@ BOOL find_next_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
 
         fi = first_item(t);
 
-        Status = do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_load_tree returned %08x\n", Status);
-            return FALSE;
+        if (!fi->treeholder.tree) {
+            Status = do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("do_load_tree returned %08x\n", Status);
+                return FALSE;
+            }
         }
 
         t = fi->treeholder.tree;
@@ -686,7 +699,6 @@ BOOL find_prev_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
     tree* t;
     tree_data* td;
     NTSTATUS Status;
-    BOOL loaded;
 
     // FIXME - support ignore flag
     if (prev_item(tp->tree, tp->item)) {
@@ -709,10 +721,12 @@ BOOL find_prev_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
 
     td = prev_item(t->parent, t->paritem);
 
-    Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("do_load_tree returned %08x\n", Status);
-        return FALSE;
+    if (!td->treeholder.tree) {
+        Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_load_tree returned %08x\n", Status);
+            return FALSE;
+        }
     }
 
     t = td->treeholder.tree;
@@ -722,10 +736,12 @@ BOOL find_prev_item(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
 
         li = last_item(t);
 
-        Status = do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_load_tree returned %08x\n", Status);
-            return FALSE;
+        if (!li->treeholder.tree) {
+            Status = do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("do_load_tree returned %08x\n", Status);
+                return FALSE;
+            }
         }
 
         t = li->treeholder.tree;
@@ -756,7 +772,7 @@ void free_trees_root(device_extension* Vcb, root* r) {
 
                     empty = FALSE;
 
-                    free_tree2(t);
+                    free_tree(t);
                     if (top && r->treeholder.tree == t)
                         r->treeholder.tree = NULL;
 
@@ -793,7 +809,7 @@ void free_trees(device_extension* Vcb) {
 
                 empty = FALSE;
 
-                free_tree2(t);
+                free_tree(t);
                 if (top && r->treeholder.tree == t)
                     r->treeholder.tree = NULL;
 
@@ -808,6 +824,9 @@ void free_trees(device_extension* Vcb) {
         if (empty)
             break;
     }
+
+    reap_filerefs(Vcb, Vcb->root_fileref);
+    reap_fcbs(Vcb);
 }
 
 #ifdef _MSC_VER
@@ -859,9 +878,7 @@ NTSTATUS insert_tree_item(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock)
     if (Status == STATUS_NOT_FOUND) {
         if (r) {
             if (!r->treeholder.tree) {
-                BOOL loaded;
-
-                Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
+                Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, Irp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("do_load_tree returned %08x\n", Status);
                     return Status;
index fa33221..4f7dc63 100644 (file)
@@ -774,6 +774,30 @@ static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) {
     return Status;
 }
 
+static NTSTATUS vol_query_stable_guid(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    MOUNTDEV_STABLE_GUID* mdsg;
+    pdo_device_extension* pdode;
+
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID)) {
+        Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    mdsg = Irp->AssociatedIrp.SystemBuffer;
+
+    if (!vde->pdo)
+        return STATUS_INVALID_PARAMETER;
+
+    pdode = vde->pdode;
+
+    RtlCopyMemory(&mdsg->StableGuid, &pdode->uuid, sizeof(BTRFS_UUID));
+
+    Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
+
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     volume_device_extension* vde = DeviceObject->DeviceExtension;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -797,8 +821,7 @@ NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
             break;
 
         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
-            TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
-            break;
+            return vol_query_stable_guid(vde, Irp);
 
         case IOCTL_MOUNTDEV_LINK_CREATED:
             TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n");
@@ -811,12 +834,12 @@ NTSTATUS vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
             return vol_is_dynamic(Irp);
 
         case IOCTL_VOLUME_ONLINE:
-            TRACE("unhandled control code IOCTL_VOLUME_ONLINE\n");
-            break;
+            Irp->IoStatus.Information = 0;
+            return STATUS_SUCCESS;
 
         case IOCTL_VOLUME_POST_ONLINE:
-            TRACE("unhandled control code IOCTL_VOLUME_POST_ONLINE\n");
-            break;
+            Irp->IoStatus.Information = 0;
+            return STATUS_SUCCESS;
 
         case IOCTL_DISK_GET_DRIVE_GEOMETRY:
             return vol_get_drive_geometry(DeviceObject, Irp);