[BTRFS] Upgrade to 1.5
authorPierre Schweitzer <pierre@reactos.org>
Tue, 12 Nov 2019 18:32:46 +0000 (19:32 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 12 Nov 2019 18:35:43 +0000 (19:35 +0100)
CORE-16494

27 files changed:
drivers/filesystems/btrfs/balance.c
drivers/filesystems/btrfs/boot.c
drivers/filesystems/btrfs/btrfs.c
drivers/filesystems/btrfs/btrfs.h
drivers/filesystems/btrfs/btrfs.rc
drivers/filesystems/btrfs/btrfs_drv.h
drivers/filesystems/btrfs/btrfsioctl.h
drivers/filesystems/btrfs/cache.c
drivers/filesystems/btrfs/create.c
drivers/filesystems/btrfs/devctrl.c
drivers/filesystems/btrfs/dirctrl.c
drivers/filesystems/btrfs/fastio.c
drivers/filesystems/btrfs/fileinfo.c
drivers/filesystems/btrfs/flushthread.c
drivers/filesystems/btrfs/fsctl.c
drivers/filesystems/btrfs/fsrtl.c
drivers/filesystems/btrfs/pnp.c
drivers/filesystems/btrfs/read.c
drivers/filesystems/btrfs/registry.c
drivers/filesystems/btrfs/reparse.c
drivers/filesystems/btrfs/scrub.c
drivers/filesystems/btrfs/search.c
drivers/filesystems/btrfs/security.c
drivers/filesystems/btrfs/send.c
drivers/filesystems/btrfs/volume.c
drivers/filesystems/btrfs/write.c
media/doc/README.FSD

index b779412..1a2c6a2 100644 (file)
@@ -1037,6 +1037,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                 tw->address = mr->new_address;
                 tw->length = Vcb->superblock.node_size;
                 tw->data = (uint8_t*)mr->data;
+                tw->allocated = false;
 
                 if (IsListEmpty(&tree_writes))
                     InsertTailList(&tree_writes, &tw->list_entry);
@@ -1089,6 +1090,10 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 end:
     while (!IsListEmpty(&tree_writes)) {
         tree_write* tw = CONTAINING_RECORD(RemoveHeadList(&tree_writes), tree_write, list_entry);
+
+        if (tw->allocated)
+            ExFreePool(tw->data);
+
         ExFreePool(tw);
     }
 
@@ -1203,6 +1208,9 @@ end:
             ExFreePool(ref);
         }
 
+        if (mr->data)
+            ExFreePool(mr->data);
+
         ExFreePool(mr);
     }
 
@@ -2187,6 +2195,9 @@ end:
             ExFreePool(ref);
         }
 
+        if (mr->data)
+            ExFreePool(mr->data);
+
         ExFreePool(mr);
     }
 
@@ -3499,6 +3510,7 @@ end:
 NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
     NTSTATUS Status;
     btrfs_start_balance* bsb = (btrfs_start_balance*)data;
+    OBJECT_ATTRIBUTES oa;
     uint8_t i;
 
     if (length < sizeof(btrfs_start_balance) || !data)
@@ -3599,7 +3611,9 @@ NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESS
     Vcb->balance.status = STATUS_SUCCESS;
     KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused);
 
-    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         return Status;
@@ -3613,6 +3627,7 @@ NTSTATUS look_for_balance_item(_Requires_lock_held_(_Curr_->tree_lock) device_ex
     traverse_ptr tp;
     NTSTATUS Status;
     BALANCE_ITEM* bi;
+    OBJECT_ATTRIBUTES oa;
     int i;
 
     searchkey.obj_id = BALANCE_ITEM_ID;
@@ -3679,7 +3694,9 @@ NTSTATUS look_for_balance_item(_Requires_lock_held_(_Curr_->tree_lock) device_ex
     Vcb->balance.status = STATUS_SUCCESS;
     KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused);
 
-    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         return Status;
@@ -3783,6 +3800,7 @@ NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESS
     NTSTATUS Status;
     int i;
     uint64_t num_rw_devices;
+    OBJECT_ATTRIBUTES oa;
 
     TRACE("(%p, %p, %x)\n", Vcb, data, length);
 
@@ -3876,7 +3894,9 @@ NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESS
     Vcb->balance.status = STATUS_SUCCESS;
     KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused);
 
-    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         dev->reloc = false;
index f73fc38..34fee17 100755 (executable)
 
 extern ERESOURCE pdo_list_lock;
 extern LIST_ENTRY pdo_list;
+extern ERESOURCE boot_lock;
+extern PDRIVER_OBJECT drvobj;
 
 #ifndef _MSC_VER
 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw
 #endif
 
+// Not in any headers? Windbg knows about it though.
+#define DOE_START_PENDING 0x10
+
+// Just as much as we need - the version in mingw is truncated still further
+typedef struct {
+    CSHORT Type;
+    USHORT Size;
+    PDEVICE_OBJECT DeviceObject;
+    ULONG PowerFlags;
+    void* Dope;
+    ULONG ExtensionFlags;
+} DEVOBJ_EXTENSION2;
+
 static bool get_system_root_partition(uint32_t* disk_num, uint32_t* partition_num) {
     NTSTATUS Status;
     HANDLE h;
@@ -191,6 +206,65 @@ static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID
         ERR("IoCreateSymbolicLink returned %08x\n", Status);
 }
 
+static void mountmgr_notification(BTRFS_UUID* uuid) {
+    UNICODE_STRING mmdevpath;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PDEVICE_OBJECT mountmgr;
+    ULONG mmtnlen;
+    MOUNTMGR_TARGET_NAME* mmtn;
+    WCHAR* w;
+#ifdef __REACTOS__
+    unsigned int i;
+#endif
+
+    RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        return;
+    }
+
+    mmtnlen = offsetof(MOUNTMGR_TARGET_NAME, DeviceName[0]) + sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
+
+    mmtn = ExAllocatePoolWithTag(NonPagedPool, mmtnlen, ALLOC_TAG);
+    if (!mmtn) {
+        ERR("out of memory\n");
+        return;
+    }
+
+    mmtn->DeviceNameLength = sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR));
+
+    RtlCopyMemory(mmtn->DeviceName, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
+
+    w = &mmtn->DeviceName[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
+
+#ifndef __REACTOS__
+    for (unsigned int i = 0; i < 16; i++) {
+#else
+    for (i = 0; i < 16; i++) {
+#endif
+        *w = hex_digit(uuid->uuid[i] >> 4); w++;
+        *w = hex_digit(uuid->uuid[i] & 0xf); w++;
+
+        if (i == 3 || i == 5 || i == 7 || i == 9) {
+            *w = L'-';
+            w++;
+        }
+    }
+
+    *w = L'}';
+
+    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, mmtn, mmtnlen, NULL, 0, false, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08x\n", Status);
+        ExFreePool(mmtn);
+        return;
+    }
+
+    ExFreePool(mmtn);
+}
+
 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
  * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
  * create so that RAID works correctly.
@@ -205,9 +279,14 @@ void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULO
     uint32_t disk_num, partition_num;
     LIST_ENTRY* le;
     bool done = false;
+    PDEVICE_OBJECT pdo_to_add = NULL;
 
     TRACE("(%p, %p, %u)\n", DriverObject, Context, Count);
 
+    // wait for any PNP notifications in progress to finish
+    ExAcquireResourceExclusiveLite(&boot_lock, TRUE);
+    ExReleaseResourceLite(&boot_lock);
+
     if (!get_system_root_partition(&disk_num, &partition_num))
         return;
 
@@ -230,19 +309,76 @@ void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULO
             if (vc->disk_num == disk_num && vc->part_num == partition_num) {
                 change_symlink(disk_num, partition_num, &pdode->uuid);
                 done = true;
+
+                if (!pdode->vde)
+                    pdo_to_add = pdode->pdo;
+
                 break;
             }
 
             le2 = le2->Flink;
         }
 
-        ExReleaseResourceLite(&pdode->child_lock);
+        if (done) {
+            le2 = pdode->children.Flink;
+
+            while (le2 != &pdode->children) {
+                volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+
+                /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of
+                 * all of our underlying partition objects - otherwise IopMountVolume
+                 * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails
+                 * to mount one. */
+                if (vc->devobj) {
+                    PDEVICE_OBJECT dev = vc->devobj;
+
+                    ObReferenceObject(dev);
+
+                    while (dev) {
+                        PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev);
+
+                        dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION;
+
+                        ObDereferenceObject(dev);
+
+                        dev = dev2;
+                    }
+                }
+
+                le2 = le2->Flink;
+            }
+
+            ExReleaseResourceLite(&pdode->child_lock);
 
-        if (done)
             break;
+        }
+
+        ExReleaseResourceLite(&pdode->child_lock);
 
         le = le->Flink;
     }
 
     ExReleaseResourceLite(&pdo_list_lock);
+
+    // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls
+    // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our
+    // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile
+    // will return STATUS_NO_SUCH_DEVICE.
+    if (pdo_to_add) {
+        pdo_device_extension* pdode = pdo_to_add->DeviceExtension;
+
+        AddDevice(drvobj, pdo_to_add);
+
+        // To stop Windows sneakily setting DOE_START_PENDING
+        pdode->dont_report = true;
+
+        if (pdo_to_add->DeviceObjectExtension) {
+            ((DEVOBJ_EXTENSION2*)pdo_to_add->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
+
+            if (pdode && pdode->vde && pdode->vde->device)
+                ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
+        }
+
+        mountmgr_notification(&pdode->uuid);
+    }
 }
index 2977667..2080c5d 100644 (file)
@@ -86,6 +86,7 @@ uint32_t mount_no_trim = 0;
 uint32_t mount_clear_cache = 0;
 uint32_t mount_allow_degraded = 0;
 uint32_t mount_readonly = 0;
+uint32_t mount_no_root_dir = 0;
 uint32_t no_pnp = 0;
 bool log_started = false;
 UNICODE_STRING log_device, log_file, registry_path;
@@ -98,6 +99,8 @@ tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
 tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;
 tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;
 tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
+tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest;
+tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks;
 bool diskacc = false;
 void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
 ERESOURCE pdo_list_lock, mapping_lock;
@@ -107,6 +110,7 @@ HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
 bool degraded_wait = true;
 KEVENT mountmgr_thread_event;
 bool shutting_down = false;
+ERESOURCE boot_lock;
 
 #ifdef _DEBUG
 PFILE_OBJECT comfo = NULL;
@@ -284,31 +288,6 @@ static void __stdcall DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
 
     TRACE("(%p)\n", DriverObject);
 
-    free_cache();
-
-    IoUnregisterFileSystem(DriverObject->DeviceObject);
-
-    if (notification_entry2) {
-        if (fIoUnregisterPlugPlayNotificationEx)
-            fIoUnregisterPlugPlayNotificationEx(notification_entry2);
-        else
-            IoUnregisterPlugPlayNotification(notification_entry2);
-    }
-
-    if (notification_entry3) {
-        if (fIoUnregisterPlugPlayNotificationEx)
-            fIoUnregisterPlugPlayNotificationEx(notification_entry3);
-        else
-            IoUnregisterPlugPlayNotification(notification_entry3);
-    }
-
-    if (notification_entry) {
-        if (fIoUnregisterPlugPlayNotificationEx)
-            fIoUnregisterPlugPlayNotificationEx(notification_entry);
-        else
-            IoUnregisterPlugPlayNotification(notification_entry);
-    }
-
     dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
 
@@ -504,7 +483,6 @@ static NTSTATUS __stdcall drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP
 
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
 
-    // FIXME - unmount if called for volume
     // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
 
     Status = close_file(IrpSp->FileObject, Irp);
@@ -560,6 +538,8 @@ static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _I
         goto end;
     }
 
+    FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
     Irp->IoStatus.Information = 0;
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
@@ -615,6 +595,8 @@ static void calculate_total_space(_In_ device_extension* Vcb, _Out_ uint64_t* to
 }
 
 #ifndef __REACTOS__
+#define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
+
 // This function exists because we have to lie about our FS type in certain situations.
 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
@@ -631,17 +613,10 @@ static bool lie_about_fs_type() {
     ULONG_PTR wow64info;
 #endif
 
-    static const WCHAR mpr[] = L"MPR.DLL";
-    static const WCHAR cmd[] = L"CMD.EXE";
-    static const WCHAR fsutil[] = L"FSUTIL.EXE";
-    UNICODE_STRING mprus, cmdus, fsutilus;
-
-    mprus.Buffer = (WCHAR*)mpr;
-    mprus.Length = mprus.MaximumLength = sizeof(mpr) - sizeof(WCHAR);
-    cmdus.Buffer = (WCHAR*)cmd;
-    cmdus.Length = cmdus.MaximumLength = sizeof(cmd) - sizeof(WCHAR);
-    fsutilus.Buffer = (WCHAR*)fsutil;
-    fsutilus.Length = fsutilus.MaximumLength = sizeof(fsutil) - sizeof(WCHAR);
+    INIT_UNICODE_STRING(mpr, L"MPR.DLL");
+    INIT_UNICODE_STRING(cmd, L"CMD.EXE");
+    INIT_UNICODE_STRING(fsutil, L"FSUTIL.EXE");
+    INIT_UNICODE_STRING(storsvc, L"STORSVC.DLL");
 
     if (!PsGetCurrentProcess())
         return false;
@@ -673,31 +648,40 @@ static bool lie_about_fs_type() {
         LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
         bool blacklist = false;
 
-        if (entry->FullDllName.Length >= mprus.Length) {
+        if (entry->FullDllName.Length >= usmpr.Length) {
+            UNICODE_STRING name;
+
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = usmpr.Length;
+
+            blacklist = FsRtlAreNamesEqual(&name, &usmpr, true, NULL);
+        }
+
+        if (!blacklist && entry->FullDllName.Length >= uscmd.Length) {
             UNICODE_STRING name;
 
-            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
-            name.Length = name.MaximumLength = mprus.Length;
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - uscmd.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = uscmd.Length;
 
-            blacklist = FsRtlAreNamesEqual(&name, &mprus, true, NULL);
+            blacklist = FsRtlAreNamesEqual(&name, &uscmd, true, NULL);
         }
 
-        if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
+        if (!blacklist && entry->FullDllName.Length >= usfsutil.Length) {
             UNICODE_STRING name;
 
-            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
-            name.Length = name.MaximumLength = cmdus.Length;
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usfsutil.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = usfsutil.Length;
 
-            blacklist = FsRtlAreNamesEqual(&name, &cmdus, true, NULL);
+            blacklist = FsRtlAreNamesEqual(&name, &usfsutil, true, NULL);
         }
 
-        if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) {
+        if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) {
             UNICODE_STRING name;
 
-            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - fsutilus.Length) / sizeof(WCHAR)];
-            name.Length = name.MaximumLength = fsutilus.Length;
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = usstorsvc.Length;
 
-            blacklist = FsRtlAreNamesEqual(&name, &fsutilus, true, NULL);
+            blacklist = FsRtlAreNamesEqual(&name, &usstorsvc, true, NULL);
         }
 
         if (blacklist) {
@@ -1451,95 +1435,6 @@ end:
     return Status;
 }
 
-static WCHAR* file_desc_fcb(_In_ fcb* fcb) {
-    char s[60];
-    NTSTATUS Status;
-    UNICODE_STRING us;
-    ANSI_STRING as;
-
-    if (fcb->debug_desc)
-        return fcb->debug_desc;
-
-    if (fcb == fcb->Vcb->volume_fcb)
-        return L"volume FCB";
-
-    fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
-    if (!fcb->debug_desc)
-        return L"(memory error)";
-
-    // I know this is pretty hackish...
-    // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
-    // without the CRT, which breaks drivers.
-
-    sprintf(s, "subvol %x, inode %x", (uint32_t)fcb->subvol->id, (uint32_t)fcb->inode);
-
-    as.Buffer = s;
-    as.Length = as.MaximumLength = (USHORT)strlen(s);
-
-    us.Buffer = fcb->debug_desc;
-    us.MaximumLength = 60 * sizeof(WCHAR);
-    us.Length = 0;
-
-    Status = RtlAnsiStringToUnicodeString(&us, &as, false);
-    if (!NT_SUCCESS(Status))
-        return L"(RtlAnsiStringToUnicodeString error)";
-
-    us.Buffer[us.Length / sizeof(WCHAR)] = 0;
-
-    return fcb->debug_desc;
-}
-
-WCHAR* file_desc_fileref(_In_ file_ref* fileref) {
-    NTSTATUS Status;
-    UNICODE_STRING fn;
-    ULONG reqlen;
-
-    if (fileref->debug_desc)
-        return fileref->debug_desc;
-
-    fn.Length = fn.MaximumLength = 0;
-    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
-    if (Status != STATUS_BUFFER_OVERFLOW)
-        return L"ERROR";
-
-    if (reqlen > 0xffff - sizeof(WCHAR))
-        return L"(too long)";
-
-    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
-    if (!fileref->debug_desc)
-        return L"(memory error)";
-
-    fn.Buffer = fileref->debug_desc;
-    fn.Length = 0;
-    fn.MaximumLength = (USHORT)(reqlen + sizeof(WCHAR));
-
-    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
-    if (!NT_SUCCESS(Status)) {
-        ExFreePool(fileref->debug_desc);
-        fileref->debug_desc = NULL;
-        return L"ERROR";
-    }
-
-    fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
-
-    return fileref->debug_desc;
-}
-
-_Ret_z_
-WCHAR* file_desc(_In_ PFILE_OBJECT FileObject) {
-    fcb* fcb = FileObject->FsContext;
-    ccb* ccb = FileObject->FsContext2;
-    file_ref* fileref = ccb ? ccb->fileref : NULL;
-
-    if (fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
-        return L"(paging file)";
-
-    if (fileref)
-        return file_desc_fileref(fileref);
-    else
-        return file_desc_fcb(fcb);
-}
-
 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
     UNICODE_STRING fn;
     NTSTATUS Status;
@@ -1580,7 +1475,7 @@ void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match,
     ExFreePool(fn.Buffer);
 }
 
-void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
+static void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
     fcb* fcb = fileref->fcb;
     LIST_ENTRY* le;
     NTSTATUS Status;
@@ -1661,6 +1556,61 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
     ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
 }
 
+typedef struct {
+    file_ref* fileref;
+    ULONG filter_match;
+    ULONG action;
+    PUNICODE_STRING stream;
+    PIO_WORKITEM work_item;
+} notification_fcb;
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+static void __stdcall notification_work_item(PDEVICE_OBJECT DeviceObject, PVOID con) {
+    notification_fcb* nf = con;
+
+    UNUSED(DeviceObject);
+
+    ExAcquireResourceSharedLite(&nf->fileref->fcb->Vcb->tree_lock, TRUE); // protect us from fileref being reaped
+
+    send_notification_fcb(nf->fileref, nf->filter_match, nf->action, nf->stream);
+
+    free_fileref(nf->fileref);
+
+    ExReleaseResourceLite(&nf->fileref->fcb->Vcb->tree_lock);
+
+    IoFreeWorkItem(nf->work_item);
+
+    ExFreePool(nf);
+}
+
+void queue_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
+    notification_fcb* nf;
+    PIO_WORKITEM work_item;
+
+    nf = ExAllocatePoolWithTag(PagedPool, sizeof(notification_fcb), ALLOC_TAG);
+    if (!nf) {
+        ERR("out of memory\n");
+        return;
+    }
+
+    work_item = IoAllocateWorkItem(master_devobj);
+    if (!work_item) {
+        ERR("out of memory\n");
+        ExFreePool(nf);
+        return;
+    }
+
+    InterlockedIncrement(&fileref->refcount);
+
+    nf->fileref = fileref;
+    nf->filter_match = filter_match;
+    nf->action = action;
+    nf->stream = stream;
+    nf->work_item = work_item;
+
+    IoQueueWorkItem(work_item, notification_work_item, DelayedWorkQueue, nf);
+}
+
 void mark_fcb_dirty(_In_ fcb* fcb) {
     if (!fcb->dirty) {
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -1746,9 +1696,6 @@ void reap_fcb(fcb* fcb) {
     if (fcb->adsdata.Buffer)
         ExFreePool(fcb->adsdata.Buffer);
 
-    if (fcb->debug_desc)
-        ExFreePool(fcb->debug_desc);
-
     while (!IsListEmpty(&fcb->extents)) {
         LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
@@ -1795,6 +1742,7 @@ void reap_fcb(fcb* fcb) {
         ExFreePool(fcb->hash_ptrs_uc);
 
     FsRtlUninitializeFileLock(&fcb->lock);
+    FsRtlUninitializeOplock(fcb_oplock(fcb));
 
     if (fcb->pool_type == NonPagedPool)
         ExFreePool(fcb);
@@ -1842,9 +1790,6 @@ void reap_fileref(device_extension* Vcb, file_ref* fr) {
 
     // FIXME - do delete if needed
 
-    if (fr->debug_desc)
-        ExFreePool(fr->debug_desc);
-
     ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
 
     ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
@@ -1869,6 +1814,9 @@ void reap_fileref(device_extension* Vcb, file_ref* fr) {
 
     free_fcb(fr->fcb);
 
+    if (fr->oldutf8.Buffer)
+        ExFreePool(fr->oldutf8.Buffer);
+
     ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
 }
 
@@ -1911,7 +1859,7 @@ static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
 
     ccb = FileObject->FsContext2;
 
-    TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
+    TRACE("close called for fcb %p)\n", fcb);
 
     // FIXME - make sure notification gets sent if file is being deleted
 
@@ -1980,9 +1928,13 @@ void uninit(_In_ device_extension* Vcb) {
         ExReleaseResourceLite(&Vcb->tree_lock);
     }
 
+    if (Vcb->vde && Vcb->vde->mounted_device == Vcb->devobj)
+        Vcb->vde->mounted_device = NULL;
+
     IoAcquireVpbSpinLock(&irql);
     Vcb->Vpb->Flags &= ~VPB_MOUNTED;
     Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
+    Vcb->Vpb->DeviceObject = NULL;
     IoReleaseVpbSpinLock(irql);
 
     RemoveEntryList(&Vcb->list_entry);
@@ -2071,6 +2023,21 @@ void uninit(_In_ device_extension* Vcb) {
         le = le->Flink;
     }
 
+    while (!IsListEmpty(&Vcb->all_fcbs)) {
+        fcb* fcb = CONTAINING_RECORD(Vcb->all_fcbs.Flink, struct _fcb, list_entry_all);
+
+        reap_fcb(fcb);
+    }
+
+    while (!IsListEmpty(&Vcb->sys_chunks)) {
+        sys_chunk* sc = CONTAINING_RECORD(RemoveHeadList(&Vcb->sys_chunks), sys_chunk, list_entry);
+
+        if (sc->data)
+            ExFreePool(sc->data);
+
+        ExFreePool(sc);
+    }
+
     while (!IsListEmpty(&Vcb->roots)) {
         root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
 
@@ -2111,8 +2078,6 @@ void uninit(_In_ device_extension* Vcb) {
         ExFreePool(c);
     }
 
-    // FIXME - free any open fcbs?
-
     while (!IsListEmpty(&Vcb->devices)) {
         device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
 
@@ -2156,6 +2121,11 @@ void uninit(_In_ device_extension* Vcb) {
     ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
 
     ZwClose(Vcb->flush_thread_handle);
+
+    if (Vcb->devobj->AttachedDevice)
+        IoDetachDevice(Vcb->devobj);
+
+    IoDeleteDevice(Vcb->devobj);
 }
 
 static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
@@ -2422,6 +2392,8 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
         goto exit;
     }
 
+    FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
     // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
     // messages belonging to other devices.
 
@@ -2435,7 +2407,7 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
         fileref = ccb ? ccb->fileref : NULL;
 
         TRACE("cleanup called for FileObject %p\n", FileObject);
-        TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
+        TRACE("fileref %p, refcount = %u, open_count = %u\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
 
         ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
 
@@ -2475,7 +2447,8 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
 
         if (fileref && (oc == 0 || (fileref->delete_on_close && fileref->posix_delete))) {
             if (!fcb->Vcb->removing) {
-                if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) { // last handle closed on POSIX-deleted file
+                if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref &&
+                    fcb != fcb->Vcb->volume_fcb && !fcb->ads) { // last handle closed on POSIX-deleted file
                     LIST_ENTRY rollback;
 
                     InitializeListHead(&rollback);
@@ -2647,9 +2620,8 @@ ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
             break;
     }
 
-    if (dotfile) {
+    if (dotfile || (r->id == BTRFS_ROOT_FSTREE && inode == SUBVOL_ROOT_INODE))
         att |= FILE_ATTRIBUTE_HIDDEN;
-    }
 
     att |= FILE_ATTRIBUTE_ARCHIVE;
 
@@ -3883,7 +3855,7 @@ static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
 }
 
 _Ret_maybenull_
-static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
+root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
     LIST_ENTRY* le;
 
     static const char fn[] = "default";
@@ -3974,7 +3946,7 @@ end:
 void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
     TRACE("(%p, %p)\n", FileObject, ccfs);
 
-    CcInitializeCacheMap(FileObject, ccfs, false, cache_callbacks, FileObject);
+    CcInitializeCacheMap(FileObject, ccfs, false, &cache_callbacks, FileObject);
 
     if (diskacc)
         fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
@@ -3998,6 +3970,7 @@ uint32_t get_num_of_processors() {
 
 static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
     device_extension* Vcb = DeviceObject->DeviceExtension;
+    OBJECT_ATTRIBUTES oa;
     ULONG i;
 
     Vcb->calcthreads.num_threads = get_num_of_processors();
@@ -4014,13 +3987,15 @@ static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
 
     RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
 
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
         NTSTATUS Status;
 
         Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
         KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, false);
 
-        Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, NULL, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
+        Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, &oa, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
         if (!NT_SUCCESS(Status)) {
             ULONG j;
 
@@ -4201,12 +4176,7 @@ static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool*
         pnp_name.Length = 0;
     }
 
-    if (pnp_name.Length == 0)
-        *pno_pnp = true;
-    else {
-        *pno_pnp = false;
-        volume_arrival(drvobj, &pnp_name);
-    }
+    *pno_pnp = pnp_name.Length == 0;
 
     if (pnp_name.Buffer)
         ExFreePool(pnp_name.Buffer);
@@ -4223,7 +4193,6 @@ static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT f
     NTSTATUS Status;
     ULONG to_read;
     superblock* sb;
-    PDEVICE_OBJECT device2;
 
     if (!device)
         return false;
@@ -4257,14 +4226,20 @@ static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT f
         }
     }
 
-    device2 = device;
+    ObReferenceObject(device);
 
-    do {
-        device2->Flags &= ~DO_VERIFY_VOLUME;
-        device2 = IoGetLowerDeviceObject(device2);
-    } while (device2);
+    while (device) {
+        PDEVICE_OBJECT device2 = IoGetLowerDeviceObject(device);
+
+        device->Flags &= ~DO_VERIFY_VOLUME;
+
+        ObDereferenceObject(device);
+
+        device = device2;
+    }
 
     ExFreePool(sb);
+
     return true;
 }
 
@@ -4286,6 +4261,9 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     pdo_device_extension* pdode = NULL;
     volume_child* vc;
     uint64_t readobjsize;
+    OBJECT_ATTRIBUTES oa;
+    device_extension* real_devext;
+    KIRQL irql;
 
     TRACE("(%p, %p)\n", DeviceObject, Irp);
 
@@ -4297,6 +4275,12 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
 
+    real_devext = IrpSp->Parameters.MountVolume.Vpb->RealDevice->DeviceExtension;
+
+    // Make sure we're not trying to mount the PDO
+    if (IrpSp->Parameters.MountVolume.Vpb->RealDevice->DriverObject == drvobj && real_devext->type == VCB_TYPE_PDO)
+        return STATUS_UNRECOGNIZED_VOLUME;
+
     if (!is_btrfs_volume(DeviceToMount)) {
         bool not_pnp = false;
 
@@ -4313,8 +4297,17 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
         pdo = DeviceToMount;
 
-        while (IoGetLowerDeviceObject(pdo)) {
-            pdo = IoGetLowerDeviceObject(pdo);
+        ObReferenceObject(pdo);
+
+        while (true) {
+            PDEVICE_OBJECT pdo2 = IoGetLowerDeviceObject(pdo);
+
+            ObDereferenceObject(pdo);
+
+            if (!pdo2)
+                break;
+            else
+                pdo = pdo2;
         }
 
         ExAcquireResourceSharedLite(&pdo_list_lock, true);
@@ -4797,7 +4790,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     Vcb->root_file->FsContext2 = root_ccb;
 
     _SEH2_TRY {
-        CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), false, cache_callbacks, Vcb->root_file);
+        CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), false, &cache_callbacks, Vcb->root_file);
     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
         Status = _SEH2_GetExceptionCode();
         goto exit;
@@ -4816,17 +4809,23 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
         le = le->Flink;
     }
 
+    IoAcquireVpbSpinLock(&irql);
+
     NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
     IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
     IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
     NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
-    NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+    NewDeviceObject->Vpb->ReferenceCount++;
+
+    IoReleaseVpbSpinLock(irql);
 
     KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, false);
 
-    Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, &oa, NULL, NULL, flush_thread, NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         goto exit;
@@ -4851,6 +4850,8 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     if (vde)
         vde->mounted_device = NewDeviceObject;
 
+    Vcb->devobj = NewDeviceObject;
+
     ExInitializeResourceLite(&Vcb->send_load_lock);
 
 exit:
@@ -5166,7 +5167,7 @@ _Function_class_(DRIVER_DISPATCH)
 static NTSTATUS __stdcall drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    fcb* fcb = IrpSp->FileObject->FsContext;
+    fcb* fcb = IrpSp->FileObject ? IrpSp->FileObject->FsContext : NULL;
     device_extension* Vcb = DeviceObject->DeviceExtension;
     bool top_level;
 
@@ -5185,6 +5186,14 @@ static NTSTATUS __stdcall drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In
 
     TRACE("lock control\n");
 
+    if (!fcb) {
+        ERR("fcb was NULL\n");
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
+    }
+
+    FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
     Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
@@ -5200,55 +5209,53 @@ exit:
     return Status;
 }
 
-_Dispatch_type_(IRP_MJ_SHUTDOWN)
-_Function_class_(DRIVER_DISPATCH)
-static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
-    NTSTATUS Status;
-    bool top_level;
-    device_extension* Vcb = DeviceObject->DeviceExtension;
+void do_shutdown(PIRP Irp) {
     LIST_ENTRY* le;
-
-    FsRtlEnterFileSystem();
-
-    TRACE("shutdown\n");
-
-    top_level = is_top_level(Irp);
-
-    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
-        Status = vol_shutdown(DeviceObject, Irp);
-        goto end;
-    }
-
-    Status = STATUS_SUCCESS;
+    bus_device_extension* bde;
 
     shutting_down = true;
     KeSetEvent(&mountmgr_thread_event, 0, false);
 
     le = VcbList.Flink;
     while (le != &VcbList) {
-        bool open_files;
         LIST_ENTRY* le2 = le->Flink;
 
-        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+        device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+        volume_device_extension* vde = Vcb->vde;
 
         TRACE("shutting down Vcb %p\n", Vcb);
 
-        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
-        Vcb->removing = true;
-        open_files = Vcb->open_files > 0;
+        if (vde)
+            InterlockedIncrement(&vde->open_count);
 
-        if (Vcb->need_write && !Vcb->readonly) {
-            Status = do_write(Vcb, Irp);
+        dismount_volume(Vcb, true, Irp);
+
+        if (vde) {
+            NTSTATUS Status;
+            UNICODE_STRING mmdevpath;
+            PDEVICE_OBJECT mountmgr;
+            PFILE_OBJECT mountmgrfo;
+            KIRQL irql;
+
+            RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+            Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
             if (!NT_SUCCESS(Status))
-                ERR("do_write returned %08x\n", Status);
-        }
+                ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+            else {
+                remove_drive_letter(mountmgr, &vde->name);
+
+                ObDereferenceObject(mountmgrfo);
+            }
 
-        free_trees(Vcb);
+            vde->removing = true;
 
-        ExReleaseResourceLite(&Vcb->tree_lock);
+            IoAcquireVpbSpinLock(&irql);
+            vde->device->Vpb->DeviceObject = vde->device;
+            IoReleaseVpbSpinLock(irql);
 
-        if (!open_files)
-            uninit(Vcb);
+            if (InterlockedDecrement(&vde->open_count) == 0)
+                free_vol(vde);
+        }
 
         le = le2;
     }
@@ -5261,6 +5268,66 @@ static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PI
     }
 #endif
 
+    IoUnregisterFileSystem(master_devobj);
+
+    if (notification_entry2) {
+        if (fIoUnregisterPlugPlayNotificationEx)
+            fIoUnregisterPlugPlayNotificationEx(notification_entry2);
+        else
+            IoUnregisterPlugPlayNotification(notification_entry2);
+
+        notification_entry2 = NULL;
+    }
+
+    if (notification_entry3) {
+        if (fIoUnregisterPlugPlayNotificationEx)
+            fIoUnregisterPlugPlayNotificationEx(notification_entry3);
+        else
+            IoUnregisterPlugPlayNotification(notification_entry3);
+
+        notification_entry3 = NULL;
+    }
+
+    if (notification_entry) {
+        if (fIoUnregisterPlugPlayNotificationEx)
+            fIoUnregisterPlugPlayNotificationEx(notification_entry);
+        else
+            IoUnregisterPlugPlayNotification(notification_entry);
+
+        notification_entry = NULL;
+    }
+
+    bde = busobj->DeviceExtension;
+
+    if (bde->attached_device)
+        IoDetachDevice(bde->attached_device);
+
+    IoDeleteDevice(busobj);
+    IoDeleteDevice(master_devobj);
+}
+
+_Dispatch_type_(IRP_MJ_SHUTDOWN)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
+    NTSTATUS Status;
+    bool top_level;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+
+    FsRtlEnterFileSystem();
+
+    TRACE("shutdown\n");
+
+    top_level = is_top_level(Irp);
+
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_shutdown(DeviceObject, Irp);
+        goto end;
+    }
+
+    Status = STATUS_SUCCESS;
+
+    do_shutdown(Irp);
+
 end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
@@ -5380,7 +5447,7 @@ exit:
     return Status;
 }
 
-bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix) {
+bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix, _In_ bool stream) {
     ULONG i;
 
     if (us->Length < sizeof(WCHAR))
@@ -5391,7 +5458,8 @@ bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix) {
 
     for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
         if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
-            (!posix && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
+            (!posix && (us->Buffer[i] == '/' || us->Buffer[i] == ':')) ||
+            (!posix && !stream && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == '"' ||
             us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
             return false;
     }
@@ -5518,7 +5586,11 @@ static void init_serial(bool first_time) {
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
 
         if (first_time) {
-            Status = PsCreateSystemThread(&serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, NULL);
+            OBJECT_ATTRIBUTES oa;
+
+            InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+            Status = PsCreateSystemThread(&serial_thread_handle, 0, &oa, NULL, NULL, serial_thread, NULL);
             if (!NT_SUCCESS(Status)) {
                 ERR("PsCreateSystemThread returned %08x\n", Status);
                 return;
@@ -5692,7 +5764,12 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
         goto end;
     }
 
-    ExAcquireResourceSharedLite(&pdode->child_lock, true);
+    ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
+
+    if (pdode->vde) { // if already done, return success
+        Status = STATUS_SUCCESS;
+        goto end2;
+    }
 
     volname.Length = volname.MaximumLength = (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) + ((36 + 1) * sizeof(WCHAR));
     volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
@@ -5736,6 +5813,7 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
     vde->pdo = PhysicalDeviceObject;
     vde->pdode = pdode;
     vde->removing = false;
+    vde->dead = false;
     vde->open_count = 0;
 
     Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
@@ -5775,7 +5853,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
     control_device_extension* cde;
     bus_device_extension* bde;
     HANDLE regh;
-    OBJECT_ATTRIBUTES oa;
+    OBJECT_ATTRIBUTES oa, system_thread_attributes;
     ULONG dispos;
 
     InitializeListHead(&uid_map_list);
@@ -5844,12 +5922,16 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
         RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
         fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
+
+        RtlInitUnicodeString(&name, L"FsRtlCheckLockForOplockRequest");
+        fFsRtlCheckLockForOplockRequest = (tFsRtlCheckLockForOplockRequest)MmGetSystemRoutineAddress(&name);
     } else {
         fPsUpdateDiskCounters = NULL;
         fCcCopyReadEx = NULL;
         fCcCopyWriteEx = NULL;
         fCcSetAdditionalCacheAttributesEx = NULL;
         fFsRtlUpdateDiskCounters = NULL;
+        fFsRtlCheckLockForOplockRequest = NULL;
     }
 
     if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN7)) {
@@ -5857,8 +5939,13 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
         RtlInitUnicodeString(&name, L"IoUnregisterPlugPlayNotificationEx");
         fIoUnregisterPlugPlayNotificationEx = (tIoUnregisterPlugPlayNotificationEx)MmGetSystemRoutineAddress(&name);
-    } else
+
+        RtlInitUnicodeString(&name, L"FsRtlAreThereCurrentOrInProgressFileLocks");
+        fFsRtlAreThereCurrentOrInProgressFileLocks = (tFsRtlAreThereCurrentOrInProgressFileLocks)MmGetSystemRoutineAddress(&name);
+    } else {
         fIoUnregisterPlugPlayNotificationEx = NULL;
+        fFsRtlAreThereCurrentOrInProgressFileLocks = NULL;
+    }
 
     if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_VISTA)) {
         UNICODE_STRING name;
@@ -5935,11 +6022,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
         return Status;
     }
 
-    Status = init_cache();
-    if (!NT_SUCCESS(Status)) {
-        ERR("init_cache returned %08x\n", Status);
-        return Status;
-    }
+    init_cache();
 
     InitializeListHead(&VcbList);
     ExInitializeResourceLite(&global_loading_lock);
@@ -5990,10 +6073,14 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
 
-    Status = PsCreateSystemThread(&degraded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL);
+    InitializeObjectAttributes(&system_thread_attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&degraded_wait_handle, 0, &system_thread_attributes, NULL, NULL, degraded_wait_thread, NULL);
     if (!NT_SUCCESS(Status))
         WARN("PsCreateSystemThread returned %08x\n", Status);
 
+    ExInitializeResourceLite(&boot_lock);
+
     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                             (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
     if (!NT_SUCCESS(Status))
@@ -6013,7 +6100,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, false);
 
-    Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, NULL, NULL, NULL, mountmgr_thread, NULL);
+    Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, &system_thread_attributes, NULL, NULL, mountmgr_thread, NULL);
     if (!NT_SUCCESS(Status))
         WARN("PsCreateSystemThread returned %08x\n", Status);
 
index f55ef82..780bb5d 100644 (file)
@@ -52,6 +52,7 @@ static const uint64_t superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0
 #define BTRFS_ROOT_CHUNK        3
 #define BTRFS_ROOT_DEVTREE      4
 #define BTRFS_ROOT_FSTREE       5
+#define BTRFS_ROOT_TREEDIR      6
 #define BTRFS_ROOT_CHECKSUM     7
 #define BTRFS_ROOT_UUID         9
 #define BTRFS_ROOT_FREE_SPACE   0xa
index c899d59..a594c60 100644 (file)
@@ -51,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,4,0,0
- PRODUCTVERSION 1,4,0,0
+ FILEVERSION 1,5,0,0
+ PRODUCTVERSION 1,5,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "1.4"
+            VALUE "FileVersion", "1.5"
             VALUE "InternalName", "btrfs"
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.4"
+            VALUE "ProductVersion", "1.5"
         END
     END
     BLOCK "VarFileInfo"
index 67cb249..e24e498 100644 (file)
@@ -136,6 +136,12 @@ C_ASSERT(sizeof(bool) == 1);
 #define finally if (1)
 #endif
 
+#ifndef __REACTOS__
+#ifdef __GNUC__
+#define InterlockedIncrement64(a) __sync_add_and_fetch(a, 1)
+#endif
+#endif
+
 #ifndef FILE_SUPPORTS_BLOCK_REFCOUNTING
 #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000
 #endif
@@ -248,6 +254,7 @@ typedef struct {
     UNICODE_STRING name_uc;
     ULONG size;
     struct _file_ref* fileref;
+    bool root_dir;
     LIST_ENTRY list_entry_index;
     LIST_ENTRY list_entry_hash;
     LIST_ENTRY list_entry_hash_uc;
@@ -285,7 +292,6 @@ typedef struct _fcb {
     PKTHREAD lazy_writer_thread;
     ULONG atts;
     SHARE_ACCESS share_access;
-    WCHAR* debug_desc;
     bool csum_loaded;
     LIST_ENTRY extents;
     ANSI_STRING reparse_xattr;
@@ -299,6 +305,7 @@ typedef struct _fcb {
     bool marked_as_orphan;
     bool case_sensitive;
     bool case_sensitive_set;
+    OPLOCK oplock;
 
     LIST_ENTRY dir_children_index;
     LIST_ENTRY dir_children_hash;
@@ -344,7 +351,6 @@ typedef struct _file_ref {
     LONG refcount;
     LONG open_count;
     struct _file_ref* parent;
-    WCHAR* debug_desc;
     dir_child* dc;
 
     bool dirty;
@@ -652,6 +658,7 @@ typedef struct {
     bool no_trim;
     bool clear_cache;
     bool allow_degraded;
+    bool no_root_dir;
 } mount_options;
 
 #define VCB_TYPE_FS         1
@@ -729,6 +736,7 @@ typedef struct _device_extension {
     uint32_t type;
     mount_options options;
     PVPB Vpb;
+    PDEVICE_OBJECT devobj;
     struct _volume_device_extension* vde;
     LIST_ENTRY devices;
 #ifdef DEBUG_CHUNK_LOCKS
@@ -846,6 +854,7 @@ typedef struct _volume_device_extension {
     UNICODE_STRING bus_name;
     PDEVICE_OBJECT attached_device;
     bool removing;
+    bool dead;
     LONG open_count;
 } volume_device_extension;
 
@@ -855,6 +864,7 @@ typedef struct pdo_device_extension {
     volume_device_extension* vde;
     PDEVICE_OBJECT pdo;
     bool removable;
+    bool dont_report;
 
     uint64_t num_children;
     uint64_t children_loaded;
@@ -912,6 +922,7 @@ typedef struct {
     uint32_t length;
     uint8_t* data;
     chunk* c;
+    bool allocated;
     LIST_ENTRY list_entry;
 } tree_write;
 
@@ -1082,9 +1093,9 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
 void uninit(_In_ device_extension* Vcb);
 NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
                    _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ bool Override, _Out_opt_ IO_STATUS_BLOCK* iosb);
-bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix);
+bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix, _In_ bool stream);
 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream);
-void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream);
+void queue_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream);
 
 #ifdef DEBUG_CHUNK_LOCKS
 #define acquire_chunk_lock(c, Vcb) { ExAcquireResourceExclusiveLite(&c->lock, true); InterlockedIncrement(&Vcb->chunk_locks_held); }
@@ -1094,9 +1105,6 @@ void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
 #define release_chunk_lock(c, Vcb) ExReleaseResourceLite(&(c)->lock)
 #endif
 
-_Ret_z_
-WCHAR* file_desc(_In_ PFILE_OBJECT FileObject);
-WCHAR* file_desc_fileref(_In_ file_ref* fileref);
 void mark_fcb_dirty(_In_ fcb* fcb);
 void mark_fileref_dirty(_In_ file_ref* fileref);
 NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_ bool make_orphan, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback);
@@ -1122,6 +1130,11 @@ NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src,
 NTSTATUS utf16_to_utf8(char* dest, ULONG dest_max, ULONG* dest_len, WCHAR* src, ULONG src_len);
 uint32_t get_num_of_processors();
 
+_Ret_maybenull_
+root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp);
+
+void do_shutdown(PIRP Irp);
+
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
 #else
@@ -1143,6 +1156,7 @@ extern uint32_t mount_no_trim;
 extern uint32_t mount_clear_cache;
 extern uint32_t mount_allow_degraded;
 extern uint32_t mount_readonly;
+extern uint32_t mount_no_root_dir;
 extern uint32_t no_pnp;
 
 #ifdef _DEBUG
@@ -1274,9 +1288,8 @@ void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lo
                          _In_ volume_child* vc, _In_ bool skip_dev);
 
 // in cache.c
-NTSTATUS init_cache();
-void free_cache();
-extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
+void init_cache();
+extern CACHE_MANAGER_CALLBACKS cache_callbacks;
 
 // in write.c
 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, bool wait, bool deferred_write);
@@ -1398,6 +1411,7 @@ void do_unlock_volume(device_extension* Vcb);
 void trim_whole_device(device* dev);
 void flush_subvol_fcbs(root* subvol);
 bool fcb_is_inline(fcb* fcb);
+NTSTATUS dismount_volume(device_extension* Vcb, bool shutdown, PIRP Irp);
 
 // in flushthread.c
 
@@ -1552,6 +1566,8 @@ NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devp
 _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE)
 NTSTATUS __stdcall pnp_removal(PVOID NotificationStructure, PVOID Context);
 
+void free_vol(volume_device_extension* vde);
+
 // in scrub.c
 NTSTATUS start_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode);
 NTSTATUS query_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode, void* data, ULONG length);
@@ -1564,7 +1580,7 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJ
 NTSTATUS read_send_buffer(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, ULONG_PTR* retlen, KPROCESSOR_MODE processor_mode);
 
 // in fsrtl.c
-NTSTATUS compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer);
+NTSTATUS __stdcall compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer);
 
 // in boot.c
 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count);
@@ -1572,7 +1588,43 @@ void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULO
 // based on function in sys/sysmacros.h
 #define makedev(major, minor) (((minor) & 0xFF) | (((major) & 0xFFF) << 8) | (((uint64_t)((minor) & ~0xFF)) << 12) | (((uint64_t)((major) & ~0xFFF)) << 32))
 
-#define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
+// not in mingw yet
+#ifndef _MSC_VER
+typedef struct {
+    FSRTL_COMMON_FCB_HEADER DUMMYSTRUCTNAME;
+    PFAST_MUTEX FastMutex;
+    LIST_ENTRY FilterContexts;
+    EX_PUSH_LOCK PushLock;
+    PVOID* FileContextSupportPointer;
+    union {
+        OPLOCK Oplock;
+        PVOID ReservedForRemote;
+    };
+    PVOID ReservedContext;
+} FSRTL_ADVANCED_FCB_HEADER_NEW;
+
+#define FSRTL_FCB_HEADER_V2 2
+
+#else
+#define FSRTL_ADVANCED_FCB_HEADER_NEW FSRTL_ADVANCED_FCB_HEADER
+#endif
+
+static __inline POPLOCK fcb_oplock(fcb* fcb) {
+    if (fcb->Header.Version >= FSRTL_FCB_HEADER_V2)
+        return &((FSRTL_ADVANCED_FCB_HEADER_NEW*)&fcb->Header)->Oplock;
+    else
+        return &fcb->oplock;
+}
+
+static __inline FAST_IO_POSSIBLE fast_io_possible(fcb* fcb) {
+    if (!FsRtlOplockIsFastIoPossible(fcb_oplock(fcb)))
+        return FastIoIsNotPossible;
+
+    if (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly)
+        return FastIoIsPossible;
+
+    return FastIoIsQuestionable;
+}
 
 static __inline void print_open_trees(device_extension* Vcb) {
     LIST_ENTRY* le = Vcb->trees.Flink;
@@ -1738,16 +1790,16 @@ static __inline uint64_t fcb_alloc_size(fcb* fcb) {
         return sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
 }
 
-typedef BOOLEAN (*tPsIsDiskCountersEnabled)();
+typedef BOOLEAN (__stdcall *tPsIsDiskCountersEnabled)();
 
-typedef VOID (*tPsUpdateDiskCounters)(PEPROCESS Process, ULONG64 BytesRead, ULONG64 BytesWritten,
-                                      ULONG ReadOperationCount, ULONG WriteOperationCount, ULONG FlushOperationCount);
+typedef VOID (__stdcall *tPsUpdateDiskCounters)(PEPROCESS Process, ULONG64 BytesRead, ULONG64 BytesWritten,
+                                                ULONG ReadOperationCount, ULONG WriteOperationCount, ULONG FlushOperationCount);
 
-typedef BOOLEAN (*tCcCopyWriteEx)(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
-                                  PVOID Buffer, PETHREAD IoIssuerThread);
+typedef BOOLEAN (__stdcall *tCcCopyWriteEx)(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
+                                            PVOID Buffer, PETHREAD IoIssuerThread);
 
-typedef BOOLEAN (*tCcCopyReadEx)(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
-                                 PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PETHREAD IoIssuerThread);
+typedef BOOLEAN (__stdcall *tCcCopyReadEx)(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
+                                           PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PETHREAD IoIssuerThread);
 
 #ifndef CC_ENABLE_DISK_IO_ACCOUNTING
 #define CC_ENABLE_DISK_IO_ACCOUNTING 0x00000010
@@ -1758,22 +1810,26 @@ typedef struct _ECP_LIST ECP_LIST;
 typedef struct _ECP_LIST *PECP_LIST;
 #endif
 
-typedef VOID (*tCcSetAdditionalCacheAttributesEx)(PFILE_OBJECT FileObject, ULONG Flags);
+typedef VOID (__stdcall *tCcSetAdditionalCacheAttributesEx)(PFILE_OBJECT FileObject, ULONG Flags);
+
+typedef VOID (__stdcall *tFsRtlUpdateDiskCounters)(ULONG64 BytesRead, ULONG64 BytesWritten);
+
+typedef NTSTATUS (__stdcall *tIoUnregisterPlugPlayNotificationEx)(PVOID NotificationEntry);
 
-typedef VOID (*tFsRtlUpdateDiskCounters)(ULONG64 BytesRead, ULONG64 BytesWritten);
+typedef NTSTATUS (__stdcall *tFsRtlGetEcpListFromIrp)(PIRP Irp, PECP_LIST* EcpList);
 
-typedef NTSTATUS (*tIoUnregisterPlugPlayNotificationEx)(PVOID NotificationEntry);
+typedef NTSTATUS (__stdcall *tFsRtlGetNextExtraCreateParameter)(PECP_LIST EcpList, PVOID CurrentEcpContext, LPGUID NextEcpType,
+                                                                PVOID* NextEcpContext, ULONG* NextEcpContextSize);
 
-typedef NTSTATUS (*tFsRtlGetEcpListFromIrp)(PIRP Irp, PECP_LIST* EcpList);
+typedef NTSTATUS (__stdcall *tFsRtlValidateReparsePointBuffer)(ULONG BufferLength, PREPARSE_DATA_BUFFER ReparseBuffer);
 
-typedef NTSTATUS (*tFsRtlGetNextExtraCreateParameter)(PECP_LIST EcpList, PVOID CurrentEcpContext, LPGUID NextEcpType,
-                                                      PVOID* NextEcpContext, ULONG* NextEcpContextSize);
+typedef BOOLEAN (__stdcall *tFsRtlCheckLockForOplockRequest)(PFILE_LOCK FileLock, PLARGE_INTEGER AllocationSize);
 
-typedef NTSTATUS (*tFsRtlValidateReparsePointBuffer)(ULONG BufferLength, PREPARSE_DATA_BUFFER ReparseBuffer);
+typedef BOOLEAN (__stdcall *tFsRtlAreThereCurrentOrInProgressFileLocks)(PFILE_LOCK FileLock);
 
 #ifndef __REACTOS__
 #ifndef _MSC_VER
-PEPROCESS PsGetThreadProcess(_In_ PETHREAD Thread); // not in mingw
+PEPROCESS __stdcall PsGetThreadProcess(_In_ PETHREAD Thread); // not in mingw
 #endif
 
 // not in DDK headers - taken from winternl.h
index e087131..0ba18cf 100644 (file)
@@ -36,6 +36,7 @@
 #define FSCTL_BTRFS_SEND_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x846, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_READ_SEND_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x847, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_RESIZE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x848, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define IOCTL_BTRFS_UNLOAD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x849, METHOD_NEITHER, FILE_ANY_ACCESS)
 
 typedef struct {
     uint64_t subvol;
index 42ce02c..90b0e2a 100644 (file)
@@ -17,7 +17,7 @@
 
 #include "btrfs_drv.h"
 
-CACHE_MANAGER_CALLBACKS* cache_callbacks;
+CACHE_MANAGER_CALLBACKS cache_callbacks;
 
 static BOOLEAN __stdcall acquire_for_lazy_write(PVOID Context, BOOLEAN Wait) {
     PFILE_OBJECT FileObject = Context;
@@ -82,21 +82,9 @@ static void __stdcall release_from_read_ahead(PVOID Context) {
         IoSetTopLevelIrp(NULL);
 }
 
-NTSTATUS init_cache() {
-    cache_callbacks = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_MANAGER_CALLBACKS), ALLOC_TAG);
-    if (!cache_callbacks) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    cache_callbacks->AcquireForLazyWrite = acquire_for_lazy_write;
-    cache_callbacks->ReleaseFromLazyWrite = release_from_lazy_write;
-    cache_callbacks->AcquireForReadAhead = acquire_for_read_ahead;
-    cache_callbacks->ReleaseFromReadAhead = release_from_read_ahead;
-
-    return STATUS_SUCCESS;
-}
-
-void free_cache() {
-    ExFreePool(cache_callbacks);
+void init_cache() {
+    cache_callbacks.AcquireForLazyWrite = acquire_for_lazy_write;
+    cache_callbacks.ReleaseFromLazyWrite = release_from_lazy_write;
+    cache_callbacks.AcquireForReadAhead = acquire_for_read_ahead;
+    cache_callbacks.ReleaseFromReadAhead = release_from_read_ahead;
 }
index 039fe28..4eebef9 100644 (file)
@@ -28,6 +28,9 @@ extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
 
 static const WCHAR datastring[] = L"::$DATA";
 
+static const char root_dir[] = "$Root";
+static const WCHAR root_dir_utf16[] = L"$Root";
+
 // Windows 10
 #define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED   0x0002
 #define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT               0x0100
@@ -101,6 +104,7 @@ fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
     ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
 
     FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
+    FsRtlInitializeOplock(fcb_oplock(fcb));
 
     InitializeListHead(&fcb->extents);
     InitializeListHead(&fcb->hardlinks);
@@ -470,6 +474,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
     ULONG num_children = 0;
+    uint64_t max_index = 2;
 
     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
     if (!fcb->hash_ptrs) {
@@ -538,6 +543,9 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
         dc->index = tp.item->key.offset;
         dc->type = di->type;
         dc->fileref = NULL;
+        dc->root_dir = false;
+
+        max_index = dc->index;
 
         dc->utf8.MaximumLength = dc->utf8.Length = di->n;
         dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
@@ -592,6 +600,68 @@ cont:
             break;
     }
 
+    if (!Vcb->options.no_root_dir && fcb->inode == SUBVOL_ROOT_INODE) {
+        root* top_subvol;
+
+        if (Vcb->root_fileref && Vcb->root_fileref->fcb)
+            top_subvol = Vcb->root_fileref->fcb->subvol;
+        else
+            top_subvol = find_default_subvol(Vcb, NULL);
+
+        if (fcb->subvol == top_subvol && top_subvol->id != BTRFS_ROOT_FSTREE) {
+            dir_child* dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+            if (!dc) {
+                ERR("out of memory\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            dc->key.obj_id = BTRFS_ROOT_FSTREE;
+            dc->key.obj_type = TYPE_ROOT_ITEM;
+            dc->key.offset = 0;
+            dc->index = max_index + 1;
+            dc->type = BTRFS_TYPE_DIRECTORY;
+            dc->fileref = NULL;
+            dc->root_dir = true;
+
+            dc->utf8.MaximumLength = dc->utf8.Length = sizeof(root_dir) - sizeof(char);
+            dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir) - sizeof(char), ALLOC_TAG);
+            if (!dc->utf8.Buffer) {
+                ERR("out of memory\n");
+                ExFreePool(dc);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            RtlCopyMemory(dc->utf8.Buffer, root_dir, sizeof(root_dir) - sizeof(char));
+
+            dc->name.MaximumLength = dc->name.Length = sizeof(root_dir_utf16) - sizeof(WCHAR);
+            dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir_utf16) - sizeof(WCHAR), ALLOC_TAG);
+            if (!dc->name.Buffer) {
+                ERR("out of memory\n");
+                ExFreePool(dc->utf8.Buffer);
+                ExFreePool(dc);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            RtlCopyMemory(dc->name.Buffer, root_dir_utf16, sizeof(root_dir_utf16) - sizeof(WCHAR));
+
+            Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                ExFreePool(dc->utf8.Buffer);
+                ExFreePool(dc->name.Buffer);
+                ExFreePool(dc);
+                goto cont;
+            }
+
+            dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
+            dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
+
+            InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+
+            insert_dir_child_into_hash_lists(fcb, dc);
+        }
+    }
+
     return STATUS_SUCCESS;
 }
 
@@ -1239,6 +1309,9 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
         }
     }
 
+    if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE)
+        fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
+
     subvol->fcbs_version++;
 
     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
@@ -1517,7 +1590,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
                 return STATUS_SUCCESS;
             }
 
-            if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id)) {
+            if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id && !dc->root_dir)) {
                 fcb = Vcb->dummy_fcb;
                 InterlockedIncrement(&fcb->refcount);
             } else {
@@ -1639,8 +1712,8 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
     }
 
     if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
-        WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
-             file_desc_fileref(related), fnus->Length / sizeof(WCHAR), fnus->Buffer);
+        WARN("passed related fileref which isn't a directory (fnus = %.*S)\n",
+             fnus->Length / sizeof(WCHAR), fnus->Buffer);
         return STATUS_OBJECT_PATH_NOT_FOUND;
     }
 
@@ -1690,7 +1763,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
         bool cs = case_sensitive;
 
         if (!cs) {
-            if (streampart)
+            if (streampart && sf->parent)
                 cs = sf->parent->fcb->case_sensitive;
             else
                 cs = sf->fcb->case_sensitive;
@@ -2223,7 +2296,7 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
 
 #ifdef DEBUG_FCB_REFCOUNTS
     rc = InterlockedIncrement(&parfileref->fcb->refcount);
-    WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
+    WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
 #else
     InterlockedIncrement(&parfileref->fcb->refcount);
 #endif
@@ -2441,9 +2514,9 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
         }
 
 #ifndef __REACTOS__
-        UINT32 dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+        uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
 #else
-        dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+        dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
 #endif
 
         if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
@@ -2512,7 +2585,7 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
 
     *pfr = fileref;
 
-    TRACE("created new file %S in subvol %I64x, inode %I64x\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
+    TRACE("created new file in subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
 
     return STATUS_SUCCESS;
 }
@@ -2557,7 +2630,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
         UNICODE_STRING fpus2;
 
-        if (!is_file_name_valid(fpus, false))
+        if (!is_file_name_valid(fpus, false, true))
             return STATUS_OBJECT_NAME_INVALID;
 
         fpus2.Length = fpus2.MaximumLength = fpus->Length;
@@ -2590,7 +2663,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
             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);
+            queue_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
         }
 
         ExFreePool(fpus2.Buffer);
@@ -2655,7 +2728,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
 
 #ifdef DEBUG_FCB_REFCOUNTS
     rc = InterlockedIncrement(&parfileref->fcb->refcount);
-    WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
+    WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
 #else
     InterlockedIncrement(&parfileref->fcb->refcount);
 #endif
@@ -2920,9 +2993,17 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
                 Status = fFsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx, &ctxsize);
 
                 if (NT_SUCCESS(Status)) {
-                    if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID) && ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT)) {
-                        acec = ctx;
-                        break;
+                    if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID)) {
+                        if (ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT))
+                            acec = ctx;
+                        else {
+                            ERR("GUID_ECP_ATOMIC_CREATE context was too short: %u bytes, expected %u\n", ctxsize,
+                                sizeof(ATOMIC_CREATE_ECP_CONTEXT));
+                        }
+                    } else {
+                        WARN("unhandled ECP {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", type.Data1, type.Data2,
+                             type.Data3, type.Data4[0], type.Data4[1], type.Data4[2], type.Data4[3], type.Data4[4], type.Data4[5],
+                             type.Data4[6], type.Data4[7]);
                     }
                 }
             } while (NT_SUCCESS(Status));
@@ -3015,7 +3096,7 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
     } else {
         ACCESS_MASK granted_access;
 
-        if (!is_file_name_valid(&fpus, false)) {
+        if (!is_file_name_valid(&fpus, false, false)) {
             Status = STATUS_OBJECT_NAME_INVALID;
             goto end;
         }
@@ -3056,7 +3137,7 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
         IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
 
         send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
-        send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+        queue_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
     }
 
     FileObject->FsContext = fileref->fcb;
@@ -3140,13 +3221,10 @@ static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _R
 
     fileref->dc->type = fileref->fcb->type;
 
-    goto end2;
-
 end:
     if (fpus.Buffer)
         ExFreePool(fpus.Buffer);
 
-end2:
     if (parfileref && !loaded_related)
         free_fileref(parfileref);
 
@@ -3414,7 +3492,7 @@ static void fcb_load_csums(_Requires_lock_held_(_Curr_->tree_lock) device_extens
     while (le != &fcb->extents) {
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
 
-        if (ext->extent_data.type == EXTENT_TYPE_REGULAR) {
+        if (!ext->ignore && ext->extent_data.type == EXTENT_TYPE_REGULAR) {
             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
             uint64_t len;
 
@@ -3581,7 +3659,7 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
             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));
+        TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u)\n", fileref->fcb->type);
 
         free_fileref(fileref);
 
@@ -3617,6 +3695,15 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
         }
     }
 
+    // FIXME - this can block waiting for network IO, while we're holding fileref_lock and tree_lock
+    Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, NULL, NULL, NULL);
+    if (!NT_SUCCESS(Status)) {
+        WARN("FsRtlCheckOplock returned %08x\n", Status);
+        free_fileref(fileref);
+
+        return Status;
+    }
+
     if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
         ULONG defda, oldatts, filter;
         LARGE_INTEGER time;
@@ -3759,7 +3846,7 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
                     }
 
                     if (dc->fileref) {
-                        send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
+                        queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
 
                         Status = delete_fileref(dc->fileref, NULL, false, NULL, rollback);
                         if (!NT_SUCCESS(Status)) {
@@ -3787,7 +3874,7 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
             fileref->parent->fcb->inode_item_changed = true;
             mark_fcb_dirty(fileref->parent->fcb);
 
-            send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
+            queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
         } else {
             mark_fcb_dirty(fileref->fcb);
 
@@ -3813,7 +3900,7 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
             fileref->fcb->inode_item.st_mtime = now;
             fileref->fcb->inode_item_changed = true;
 
-            send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+            queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
         }
     } else {
         if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
@@ -4222,9 +4309,21 @@ NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock)
                 hl_alloc = true;
             }
         } else {
-            ERR("couldn't find parent for subvol %I64x\n", subvol->id);
-            free_fcb(fcb);
-            return STATUS_INTERNAL_ERROR;
+            if (!Vcb->options.no_root_dir && subvol->id == BTRFS_ROOT_FSTREE && Vcb->root_fileref->fcb->subvol != subvol) {
+                Status = open_fileref_by_inode(Vcb, Vcb->root_fileref->fcb->subvol, SUBVOL_ROOT_INODE, &parfr, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("open_fileref_by_inode returned %08x\n", Status);
+                    free_fcb(fcb);
+                    return Status;
+                }
+
+                name.Length = name.MaximumLength = sizeof(root_dir_utf16) - sizeof(WCHAR);
+                name.Buffer = (WCHAR*)root_dir_utf16;
+            } else {
+                ERR("couldn't find parent for subvol %I64x\n", subvol->id);
+                free_fcb(fcb);
+                return STATUS_INTERNAL_ERROR;
+            }
         }
     } else {
         Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
@@ -4471,7 +4570,7 @@ loaded:
 
     if (NT_SUCCESS(Status)) {
         if (RequestedDisposition == FILE_CREATE) {
-            TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
+            TRACE("file already exists, returning STATUS_OBJECT_NAME_COLLISION\n");
             Status = STATUS_OBJECT_NAME_COLLISION;
 
             free_fileref(fileref);
@@ -4788,7 +4887,7 @@ NTSTATUS __stdcall drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
 
         if (IrpSp->FileObject->RelatedFileObject)
-            TRACE("related file = %S\n", file_desc(IrpSp->FileObject->RelatedFileObject));
+            TRACE("related file = %p\n", IrpSp->FileObject->RelatedFileObject);
 
         // Don't lock again if we're being called from within CcCopyRead etc.
         skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
index 99c2e54..e3c69c9 100644 (file)
@@ -212,6 +212,17 @@ static NTSTATUS probe_volume(void* data, ULONG length, KPROCESSOR_MODE processor
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS ioctl_unload(PIRP Irp) {
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_LOAD_DRIVER_PRIVILEGE), Irp->RequestorMode)) {
+        ERR("insufficient privileges\n");
+        return STATUS_PRIVILEGE_NOT_HELD;
+    }
+
+    do_shutdown(Irp);
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS control_ioctl(PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
@@ -225,6 +236,10 @@ static NTSTATUS control_ioctl(PIRP Irp) {
             Status = probe_volume(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
             break;
 
+        case IOCTL_BTRFS_UNLOAD:
+            Status = ioctl_unload(Irp);
+            break;
+
         default:
             TRACE("unhandled ioctl %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
             Status = STATUS_NOT_IMPLEMENTED;
index 41a6826..e6e96fe 100644 (file)
@@ -209,7 +209,7 @@ static NTSTATUS query_dir_item(fcb* fcb, ccb* ccb, void* buf, LONG* len, PIRP Ir
             le = le->Flink;
         }
 
-        if (r && r->parent != fcb->subvol->id)
+        if (r && r->parent != fcb->subvol->id && (!de->dc || !de->dc->root_dir))
             r = NULL;
 
         inode = SUBVOL_ROOT_INODE;
@@ -1115,7 +1115,7 @@ static NTSTATUS notify_change_directory(device_extension* Vcb, PIRP Irp) {
 
     // FIXME - raise exception if FCB marked for deletion?
 
-    TRACE("%S\n", file_desc(FileObject));
+    TRACE("FileObject %p\n", FileObject);
 
     if (ccb->filename.Length == 0) {
         ULONG reqlen;
index 79604b4..5c2b8aa 100644 (file)
@@ -336,15 +336,33 @@ static NTSTATUS __stdcall fast_io_release_for_ccflush(PFILE_OBJECT FileObject, P
 
 _Function_class_(FAST_IO_WRITE)
 static BOOLEAN __stdcall fast_io_write(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, ULONG LockKey, PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
-    if (FsRtlCopyWrite(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject)) {
-        fcb* fcb = FileObject->FsContext;
+    fcb* fcb = FileObject->FsContext;
+    bool ret;
 
-        fcb->inode_item.st_size = fcb->Header.FileSize.QuadPart;
+    FsRtlEnterFileSystem();
 
-        return true;
+    if (!ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, Wait)) {
+        FsRtlExitFileSystem();
+        return false;
     }
 
-    return false;
+    if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, Wait)) {
+        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+        FsRtlExitFileSystem();
+        return false;
+    }
+
+    ret = FsRtlCopyWrite(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
+
+    if (ret)
+        fcb->inode_item.st_size = fcb->Header.FileSize.QuadPart;
+
+    ExReleaseResourceLite(fcb->Header.Resource);
+    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
+    FsRtlExitFileSystem();
+
+    return ret;
 }
 
 _Function_class_(FAST_IO_LOCK)
index 445f494..206a552 100644 (file)
@@ -210,7 +210,7 @@ static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJ
         return STATUS_INVALID_PARAMETER;
     }
 
-    TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes);
+    TRACE("file = %p, attributes = %x\n", FileObject, fbi->FileAttributes);
 
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
@@ -344,7 +344,7 @@ static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJ
     }
 
     if (filter != 0)
-        send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+        queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
 
     Status = STATUS_SUCCESS;
 
@@ -376,7 +376,7 @@ static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFI
 
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
-    TRACE("changing delete_on_close to %s for %S (fcb %p)\n", flags & FILE_DISPOSITION_DELETE ? "true" : "false", file_desc(FileObject), fcb);
+    TRACE("changing delete_on_close to %s for fcb %p\n", flags & FILE_DISPOSITION_DELETE ? "true" : "false", fcb);
 
     if (fcb->ads) {
         if (fileref->parent)
@@ -397,6 +397,12 @@ static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFI
         goto end;
     }
 
+    if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
+        WARN("not allowing \\$Root to be deleted\n");
+        Status = STATUS_ACCESS_DENIED;
+        goto end;
+    }
+
     // FIXME - can we skip this bit for subvols?
     if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
         TRACE("directory not empty\n");
@@ -1179,8 +1185,6 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
         InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
         ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
 
-        me->dummyfileref->debug_desc = me->fileref->debug_desc;
-
         if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
             me->dummyfileref->fcb->fileref = me->dummyfileref;
 
@@ -1498,9 +1502,958 @@ void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
     }
 }
 
+static NTSTATUS rename_stream_to_file(device_extension* Vcb, file_ref* fileref, ccb* ccb, ULONG flags,
+                                      PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    file_ref* ofr;
+    ANSI_STRING adsdata;
+    dir_child* dc;
+    fcb* dummyfcb;
+
+    if (fileref->fcb->type != BTRFS_TYPE_FILE)
+        return STATUS_INVALID_PARAMETER;
+
+    if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+        WARN("trying to rename stream on readonly file\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
+        WARN("insufficient permissions\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) // file will always exist
+        return STATUS_OBJECT_NAME_COLLISION;
+
+    // FIXME - POSIX overwrites of stream?
+
+    ofr = fileref->parent;
+
+    if (ofr->open_count > 0) {
+        WARN("trying to overwrite open file\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    if (ofr->fcb->inode_item.st_size > 0) {
+        WARN("can only overwrite existing stream if it is zero-length\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    dummyfcb = create_fcb(Vcb, PagedPool);
+    if (!dummyfcb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // copy parent fcb onto this one
+
+    fileref->fcb->subvol = ofr->fcb->subvol;
+    fileref->fcb->inode = ofr->fcb->inode;
+    fileref->fcb->hash = ofr->fcb->hash;
+    fileref->fcb->type = ofr->fcb->type;
+    fileref->fcb->inode_item = ofr->fcb->inode_item;
+
+    fileref->fcb->sd = ofr->fcb->sd;
+    ofr->fcb->sd = NULL;
+
+    fileref->fcb->deleted = ofr->fcb->deleted;
+    fileref->fcb->atts = ofr->fcb->atts;
+
+    fileref->fcb->reparse_xattr = ofr->fcb->reparse_xattr;
+    ofr->fcb->reparse_xattr.Buffer = NULL;
+    ofr->fcb->reparse_xattr.Length = ofr->fcb->reparse_xattr.MaximumLength = 0;
+
+    fileref->fcb->ea_xattr = ofr->fcb->ea_xattr;
+    ofr->fcb->ea_xattr.Buffer = NULL;
+    ofr->fcb->ea_xattr.Length = ofr->fcb->ea_xattr.MaximumLength = 0;
+
+    fileref->fcb->ealen = ofr->fcb->ealen;
+
+    while (!IsListEmpty(&ofr->fcb->hardlinks)) {
+        InsertTailList(&fileref->fcb->hardlinks, RemoveHeadList(&ofr->fcb->hardlinks));
+    }
+
+    fileref->fcb->inode_item_changed = true;
+    fileref->fcb->prop_compression = ofr->fcb->prop_compression;
+
+    while (!IsListEmpty(&ofr->fcb->xattrs)) {
+        InsertTailList(&fileref->fcb->xattrs, RemoveHeadList(&ofr->fcb->xattrs));
+    }
+
+    fileref->fcb->marked_as_orphan = ofr->fcb->marked_as_orphan;
+    fileref->fcb->case_sensitive = ofr->fcb->case_sensitive;
+    fileref->fcb->case_sensitive_set = ofr->fcb->case_sensitive_set;
+
+    while (!IsListEmpty(&ofr->fcb->dir_children_index)) {
+        InsertTailList(&fileref->fcb->dir_children_index, RemoveHeadList(&ofr->fcb->dir_children_index));
+    }
+
+    while (!IsListEmpty(&ofr->fcb->dir_children_hash)) {
+        InsertTailList(&fileref->fcb->dir_children_hash, RemoveHeadList(&ofr->fcb->dir_children_hash));
+    }
+
+    while (!IsListEmpty(&ofr->fcb->dir_children_hash_uc)) {
+        InsertTailList(&fileref->fcb->dir_children_hash_uc, RemoveHeadList(&ofr->fcb->dir_children_hash_uc));
+    }
+
+    fileref->fcb->hash_ptrs = ofr->fcb->hash_ptrs;
+    fileref->fcb->hash_ptrs_uc = ofr->fcb->hash_ptrs_uc;
+
+    ofr->fcb->hash_ptrs = NULL;
+    ofr->fcb->hash_ptrs_uc = NULL;
+
+    fileref->fcb->sd_dirty = ofr->fcb->sd_dirty;
+    fileref->fcb->sd_deleted = ofr->fcb->sd_deleted;
+    fileref->fcb->atts_changed = ofr->fcb->atts_changed;
+    fileref->fcb->atts_deleted = ofr->fcb->atts_deleted;
+    fileref->fcb->extents_changed = true;
+    fileref->fcb->reparse_xattr_changed = ofr->fcb->reparse_xattr_changed;
+    fileref->fcb->ea_changed = ofr->fcb->ea_changed;
+    fileref->fcb->prop_compression_changed = ofr->fcb->prop_compression_changed;
+    fileref->fcb->xattrs_changed = ofr->fcb->xattrs_changed;
+    fileref->fcb->created = ofr->fcb->created;
+    fileref->fcb->ads = false;
+
+    if (fileref->fcb->adsxattr.Buffer) {
+        ExFreePool(fileref->fcb->adsxattr.Buffer);
+        fileref->fcb->adsxattr.Length = fileref->fcb->adsxattr.MaximumLength = 0;
+        fileref->fcb->adsxattr.Buffer = NULL;
+    }
+
+    adsdata = fileref->fcb->adsdata;
+
+    fileref->fcb->adsdata.Buffer = NULL;
+    fileref->fcb->adsdata.Length = fileref->fcb->adsdata.MaximumLength = 0;
+
+    InsertHeadList(ofr->fcb->list_entry.Blink, &fileref->fcb->list_entry);
+
+    if (fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] == &ofr->fcb->list_entry)
+        fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] = &fileref->fcb->list_entry;
+
+    RemoveEntryList(&ofr->fcb->list_entry);
+    ofr->fcb->list_entry.Flink = ofr->fcb->list_entry.Blink = NULL;
+
+    mark_fcb_dirty(fileref->fcb);
+
+    // mark old parent fcb so it gets ignored by flush_fcb
+    ofr->fcb->created = true;
+    ofr->fcb->deleted = true;
+
+    mark_fcb_dirty(ofr->fcb);
+
+    // copy parent fileref onto this one
+
+    fileref->oldutf8 = ofr->oldutf8;
+    ofr->oldutf8.Buffer = NULL;
+    ofr->oldutf8.Length = ofr->oldutf8.MaximumLength = 0;
+
+    fileref->oldindex = ofr->oldindex;
+    fileref->delete_on_close = ofr->delete_on_close;
+    fileref->posix_delete = ofr->posix_delete;
+    fileref->deleted = ofr->deleted;
+    fileref->created = ofr->created;
+
+    fileref->parent = ofr->parent;
+
+    RemoveEntryList(&fileref->list_entry);
+    InsertHeadList(ofr->list_entry.Blink, &fileref->list_entry);
+    RemoveEntryList(&ofr->list_entry);
+    ofr->list_entry.Flink = ofr->list_entry.Blink = NULL;
+
+    while (!IsListEmpty(&ofr->children)) {
+        file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&ofr->children), file_ref, list_entry);
+
+        free_fileref(fr->parent);
+
+        fr->parent = fileref;
+        InterlockedIncrement(&fileref->refcount);
+
+        InsertTailList(&fileref->children, &fr->list_entry);
+    }
+
+    dc = fileref->dc;
+
+    fileref->dc = ofr->dc;
+    fileref->dc->fileref = fileref;
+
+    mark_fileref_dirty(fileref);
+
+    // mark old parent fileref so it gets ignored by flush_fileref
+    ofr->created = true;
+    ofr->deleted = true;
+
+    // write file data
+
+    fileref->fcb->inode_item.st_size = adsdata.Length;
+
+    if (adsdata.Length > 0) {
+        bool make_inline = adsdata.Length <= Vcb->options.max_inline;
+
+        if (make_inline) {
+            EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), ALLOC_TAG);
+            if (!ed) {
+                ERR("out of memory\n");
+                ExFreePool(adsdata.Buffer);
+                reap_fcb(dummyfcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            ed->generation = Vcb->superblock.generation;
+            ed->decoded_size = adsdata.Length;
+            ed->compression = BTRFS_COMPRESSION_NONE;
+            ed->encryption = BTRFS_ENCRYPTION_NONE;
+            ed->encoding = BTRFS_ENCODING_NONE;
+            ed->type = EXTENT_TYPE_INLINE;
+
+            RtlCopyMemory(ed->data, adsdata.Buffer, adsdata.Length);
+
+            ExFreePool(adsdata.Buffer);
+
+            Status = add_extent_to_fcb(fileref->fcb, 0, ed, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), false, NULL, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("add_extent_to_fcb returned %08x\n", Status);
+                ExFreePool(ed);
+                reap_fcb(dummyfcb);
+                return Status;
+            }
+
+            ExFreePool(ed);
+        } else if (adsdata.Length % Vcb->superblock.sector_size) {
+            char* newbuf = ExAllocatePoolWithTag(PagedPool, (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size), ALLOC_TAG);
+            if (!newbuf) {
+                ERR("out of memory\n");
+                ExFreePool(adsdata.Buffer);
+                reap_fcb(dummyfcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            RtlCopyMemory(newbuf, adsdata.Buffer, adsdata.Length);
+            RtlZeroMemory(newbuf + adsdata.Length, (uint16_t)(sector_align(adsdata.Length, Vcb->superblock.sector_size) - adsdata.Length));
+
+            ExFreePool(adsdata.Buffer);
+
+            adsdata.Buffer = newbuf;
+            adsdata.Length = adsdata.MaximumLength = (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size);
+        }
+
+        if (!make_inline) {
+            Status = do_write_file(fileref->fcb, 0, adsdata.Length, adsdata.Buffer, Irp, false, 0, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("do_write_file returned %08x\n", Status);
+                ExFreePool(adsdata.Buffer);
+                reap_fcb(dummyfcb);
+                return Status;
+            }
+
+            ExFreePool(adsdata.Buffer);
+        }
+
+        fileref->fcb->inode_item.st_blocks = adsdata.Length;
+        fileref->fcb->inode_item_changed = true;
+    }
+
+    RemoveEntryList(&dc->list_entry_index);
+
+    if (dc->utf8.Buffer)
+        ExFreePool(dc->utf8.Buffer);
+
+    if (dc->name.Buffer)
+        ExFreePool(dc->name.Buffer);
+
+    if (dc->name_uc.Buffer)
+        ExFreePool(dc->name_uc.Buffer);
+
+    ExFreePool(dc);
+
+    // FIXME - csums?
+
+    // add dummy deleted xattr with old name
+
+    dummyfcb->Vcb = Vcb;
+    dummyfcb->subvol = fileref->fcb->subvol;
+    dummyfcb->inode = fileref->fcb->inode;
+    dummyfcb->adsxattr = fileref->fcb->adsxattr;
+    dummyfcb->adshash = fileref->fcb->adshash;
+    dummyfcb->ads = true;
+    dummyfcb->deleted = true;
+
+    // FIXME - dummyfileref as well?
+
+    mark_fcb_dirty(dummyfcb);
+
+    free_fcb(dummyfcb);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS rename_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
+                              ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UNICODE_STRING fn;
+    file_ref* sf = NULL;
+    uint16_t newmaxlen;
+    ULONG utf8len;
+    ANSI_STRING utf8;
+    UNICODE_STRING utf16, utf16uc;
+    ANSI_STRING adsxattr;
+    uint32_t crc32;
+    fcb* dummyfcb;
+
+    static const WCHAR datasuf[] = L":$DATA";
+    static const char xapref[] = "user.";
+
+    if (!fileref) {
+        ERR("fileref not set\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (!fileref->parent) {
+        ERR("fileref->parent not set\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (fri->FileNameLength < sizeof(WCHAR)) {
+        WARN("filename too short\n");
+        return STATUS_OBJECT_NAME_INVALID;
+    }
+
+    if (fri->FileName[0] != ':') {
+        WARN("destination filename must begin with a colon\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
+        WARN("insufficient permissions\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    fn.Buffer = &fri->FileName[1];
+    fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
+
+    // remove :$DATA suffix
+    if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
+        RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
+        fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
+
+    if (fn.Length == 0)
+        return rename_stream_to_file(Vcb, fileref, ccb, flags, Irp, rollback);
+
+    if (!is_file_name_valid(&fn, false, true)) {
+        WARN("invalid stream name %.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
+        return STATUS_OBJECT_NAME_INVALID;
+    }
+
+    if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+        WARN("trying to rename stream on readonly file\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    Status = open_fileref_child(Vcb, fileref->parent, &fn, fileref->parent->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
+    if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+        if (Status == STATUS_SUCCESS) {
+            if (fileref == sf || sf->deleted) {
+                free_fileref(sf);
+                sf = NULL;
+            } else {
+                if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
+                    Status = STATUS_OBJECT_NAME_COLLISION;
+                    goto end;
+                }
+
+                // FIXME - POSIX overwrites of stream?
+
+                if (sf->open_count > 0) {
+                    WARN("trying to overwrite open file\n");
+                    Status = STATUS_ACCESS_DENIED;
+                    goto end;
+                }
+
+                if (sf->fcb->adsdata.Length > 0) {
+                    WARN("can only overwrite existing stream if it is zero-length\n");
+                    Status = STATUS_INVALID_PARAMETER;
+                    goto end;
+                }
+
+                Status = delete_fileref(sf, NULL, false, Irp, rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_fileref returned %08x\n", Status);
+                    goto end;
+                }
+            }
+        } else {
+            ERR("open_fileref_child returned %08x\n", Status);
+            goto end;
+        }
+    }
+
+    Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status))
+        goto end;
+
+    utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
+    utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
+    if (!utf8.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+
+    Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status)) {
+        ExFreePool(utf8.Buffer);
+        goto end;
+    }
+
+    adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
+    adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
+    if (!adsxattr.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        goto end;
+    }
+
+    RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
+    RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
+
+    // don't allow if it's one of our reserved names
+
+    if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
+        Status = STATUS_OBJECT_NAME_INVALID;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    utf16.Length = utf16.MaximumLength = fn.Length;
+    utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
+    if (!utf16.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
+
+    newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
+                offsetof(DIR_ITEM, name[0]);
+
+    if (newmaxlen < adsxattr.Length) {
+        WARN("cannot rename as data too long\n");
+        Status = STATUS_INVALID_PARAMETER;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    newmaxlen -= adsxattr.Length;
+
+    if (newmaxlen < fileref->fcb->adsdata.Length) {
+        WARN("cannot rename as data too long\n");
+        Status = STATUS_INVALID_PARAMETER;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n");
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    // add dummy deleted xattr with old name
+
+    dummyfcb = create_fcb(Vcb, PagedPool);
+    if (!dummyfcb) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(utf16uc.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    dummyfcb->Vcb = Vcb;
+    dummyfcb->subvol = fileref->fcb->subvol;
+    dummyfcb->inode = fileref->fcb->inode;
+    dummyfcb->adsxattr = fileref->fcb->adsxattr;
+    dummyfcb->adshash = fileref->fcb->adshash;
+    dummyfcb->ads = true;
+    dummyfcb->deleted = true;
+
+    mark_fcb_dirty(dummyfcb);
+
+    free_fcb(dummyfcb);
+
+    // change fcb values
+
+    fileref->dc->utf8 = utf8;
+    fileref->dc->name = utf16;
+    fileref->dc->name_uc = utf16uc;
+
+    crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
+
+    fileref->fcb->adsxattr = adsxattr;
+    fileref->fcb->adshash = crc32;
+    fileref->fcb->adsmaxlen = newmaxlen;
+
+    fileref->fcb->created = true;
+
+    mark_fcb_dirty(fileref->fcb);
+
+    Status = STATUS_SUCCESS;
+
+end:
+    if (sf)
+        free_fileref(sf);
+
+    return Status;
+}
+
+static NTSTATUS rename_file_to_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
+                                      ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UNICODE_STRING fn;
+    file_ref* sf = NULL;
+    uint16_t newmaxlen;
+    ULONG utf8len;
+    ANSI_STRING utf8;
+    UNICODE_STRING utf16, utf16uc;
+    ANSI_STRING adsxattr, adsdata;
+    uint32_t crc32;
+    fcb* dummyfcb;
+    file_ref* dummyfileref;
+    dir_child* dc;
+    LIST_ENTRY* le;
+
+    static const WCHAR datasuf[] = L":$DATA";
+    static const char xapref[] = "user.";
+
+    if (!fileref) {
+        ERR("fileref not set\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (fri->FileNameLength < sizeof(WCHAR)) {
+        WARN("filename too short\n");
+        return STATUS_OBJECT_NAME_INVALID;
+    }
+
+    if (fri->FileName[0] != ':') {
+        WARN("destination filename must begin with a colon\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
+        WARN("insufficient permissions\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    if (fileref->fcb->type != BTRFS_TYPE_FILE)
+        return STATUS_INVALID_PARAMETER;
+
+    fn.Buffer = &fri->FileName[1];
+    fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
+
+    // remove :$DATA suffix
+    if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
+        RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
+        fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
+
+    if (fn.Length == 0) {
+        WARN("not allowing overwriting file with itself\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (!is_file_name_valid(&fn, false, true)) {
+        WARN("invalid stream name %.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
+        return STATUS_OBJECT_NAME_INVALID;
+    }
+
+    if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+        WARN("trying to rename stream on readonly file\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    Status = open_fileref_child(Vcb, fileref, &fn, fileref->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
+    if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+        if (Status == STATUS_SUCCESS) {
+            if (fileref == sf || sf->deleted) {
+                free_fileref(sf);
+                sf = NULL;
+            } else {
+                if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
+                    Status = STATUS_OBJECT_NAME_COLLISION;
+                    goto end;
+                }
+
+                // FIXME - POSIX overwrites of stream?
+
+                if (sf->open_count > 0) {
+                    WARN("trying to overwrite open file\n");
+                    Status = STATUS_ACCESS_DENIED;
+                    goto end;
+                }
+
+                if (sf->fcb->adsdata.Length > 0) {
+                    WARN("can only overwrite existing stream if it is zero-length\n");
+                    Status = STATUS_INVALID_PARAMETER;
+                    goto end;
+                }
+
+                Status = delete_fileref(sf, NULL, false, Irp, rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_fileref returned %08x\n", Status);
+                    goto end;
+                }
+            }
+        } else {
+            ERR("open_fileref_child returned %08x\n", Status);
+            goto end;
+        }
+    }
+
+    Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status))
+        goto end;
+
+    utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
+    utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
+    if (!utf8.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+
+    Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status)) {
+        ExFreePool(utf8.Buffer);
+        goto end;
+    }
+
+    adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
+    adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
+    if (!adsxattr.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        goto end;
+    }
+
+    RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
+    RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
+
+    // don't allow if it's one of our reserved names
+
+    if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
+        (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
+        Status = STATUS_OBJECT_NAME_INVALID;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    utf16.Length = utf16.MaximumLength = fn.Length;
+    utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
+    if (!utf16.Buffer) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
+
+    newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
+                offsetof(DIR_ITEM, name[0]);
+
+    if (newmaxlen < adsxattr.Length) {
+        WARN("cannot rename as data too long\n");
+        Status = STATUS_INVALID_PARAMETER;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    newmaxlen -= adsxattr.Length;
+
+    if (newmaxlen < fileref->fcb->inode_item.st_size) {
+        WARN("cannot rename as data too long\n");
+        Status = STATUS_INVALID_PARAMETER;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n");
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(adsxattr.Buffer);
+        goto end;
+    }
+
+    // read existing file data
+
+    if (fileref->fcb->inode_item.st_size > 0) {
+        ULONG bytes_read;
+
+        adsdata.Length = adsdata.MaximumLength = (uint16_t)fileref->fcb->inode_item.st_size;
+
+        adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, adsdata.MaximumLength, ALLOC_TAG);
+        if (!adsdata.Buffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            ExFreePool(utf8.Buffer);
+            ExFreePool(utf16.Buffer);
+            ExFreePool(utf16uc.Buffer);
+            ExFreePool(adsxattr.Buffer);
+            goto end;
+        }
+
+        Status = read_file(fileref->fcb, (uint8_t*)adsdata.Buffer, 0, adsdata.Length, &bytes_read, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            ExFreePool(utf8.Buffer);
+            ExFreePool(utf16.Buffer);
+            ExFreePool(utf16uc.Buffer);
+            ExFreePool(adsxattr.Buffer);
+            ExFreePool(adsdata.Buffer);
+            goto end;
+        }
+
+        if (bytes_read < fileref->fcb->inode_item.st_size) {
+            ERR("short read\n");
+            Status = STATUS_INTERNAL_ERROR;
+            ExFreePool(utf8.Buffer);
+            ExFreePool(utf16.Buffer);
+            ExFreePool(utf16uc.Buffer);
+            ExFreePool(adsxattr.Buffer);
+            ExFreePool(adsdata.Buffer);
+            goto end;
+        }
+    } else
+        adsdata.Buffer = NULL;
+
+    dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+    if (!dc) {
+        ERR("short read\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(utf16uc.Buffer);
+        ExFreePool(adsxattr.Buffer);
+
+        if (adsdata.Buffer)
+            ExFreePool(adsdata.Buffer);
+
+        goto end;
+    }
+
+    // add dummy deleted fcb with old name
+
+    Status = duplicate_fcb(fileref->fcb, &dummyfcb);
+    if (!NT_SUCCESS(Status)) {
+        ERR("duplicate_fcb returned %08x\n", Status);
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(utf16uc.Buffer);
+        ExFreePool(adsxattr.Buffer);
+
+        if (adsdata.Buffer)
+            ExFreePool(adsdata.Buffer);
+
+        ExFreePool(dc);
+
+        goto end;
+    }
+
+    dummyfileref = create_fileref(Vcb);
+    if (!dummyfileref) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExFreePool(utf8.Buffer);
+        ExFreePool(utf16.Buffer);
+        ExFreePool(utf16uc.Buffer);
+        ExFreePool(adsxattr.Buffer);
+
+        if (adsdata.Buffer)
+            ExFreePool(adsdata.Buffer);
+
+        ExFreePool(dc);
+
+        reap_fcb(dummyfcb);
+
+        goto end;
+    }
+
+    dummyfileref->fcb = dummyfcb;
+
+    dummyfcb->Vcb = Vcb;
+    dummyfcb->subvol = fileref->fcb->subvol;
+    dummyfcb->inode = fileref->fcb->inode;
+    dummyfcb->hash = fileref->fcb->hash;
+
+    if (fileref->fcb->inode_item.st_size > 0) {
+        Status = excise_extents(Vcb, dummyfcb, 0, sector_align(fileref->fcb->inode_item.st_size, Vcb->superblock.sector_size),
+                                Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("excise_extents returned %08x\n", Status);
+            ExFreePool(utf8.Buffer);
+            ExFreePool(utf16.Buffer);
+            ExFreePool(utf16uc.Buffer);
+            ExFreePool(adsxattr.Buffer);
+            ExFreePool(adsdata.Buffer);
+            ExFreePool(dc);
+
+            reap_fileref(Vcb, dummyfileref);
+            reap_fcb(dummyfcb);
+
+            goto end;
+        }
+
+        dummyfcb->inode_item.st_size = 0;
+        dummyfcb->Header.AllocationSize.QuadPart = 0;
+        dummyfcb->Header.FileSize.QuadPart = 0;
+        dummyfcb->Header.ValidDataLength.QuadPart = 0;
+    }
+
+    dummyfcb->hash_ptrs = fileref->fcb->hash_ptrs;
+    dummyfcb->hash_ptrs_uc = fileref->fcb->hash_ptrs_uc;
+    dummyfcb->created = fileref->fcb->created;
+
+    le = fileref->fcb->extents.Flink;
+    while (le != &fileref->fcb->extents) {
+        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+        ext->ignore = true;
+
+        le = le->Flink;
+    }
+
+    while (!IsListEmpty(&fileref->fcb->dir_children_index)) {
+        InsertTailList(&dummyfcb->dir_children_index, RemoveHeadList(&fileref->fcb->dir_children_index));
+    }
+
+    while (!IsListEmpty(&fileref->fcb->dir_children_hash)) {
+        InsertTailList(&dummyfcb->dir_children_hash, RemoveHeadList(&fileref->fcb->dir_children_hash));
+    }
+
+    while (!IsListEmpty(&fileref->fcb->dir_children_hash_uc)) {
+        InsertTailList(&dummyfcb->dir_children_hash_uc, RemoveHeadList(&fileref->fcb->dir_children_hash_uc));
+    }
+
+    InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all);
+
+    InsertHeadList(fileref->fcb->list_entry.Blink, &dummyfcb->list_entry);
+
+    if (fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] == &fileref->fcb->list_entry)
+        fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] = &dummyfcb->list_entry;
+
+    RemoveEntryList(&fileref->fcb->list_entry);
+    fileref->fcb->list_entry.Flink = fileref->fcb->list_entry.Blink = NULL;
+
+    mark_fcb_dirty(dummyfcb);
+
+    // create dummy fileref
+
+    dummyfileref->oldutf8 = fileref->oldutf8;
+    dummyfileref->oldindex = fileref->oldindex;
+    dummyfileref->delete_on_close = fileref->delete_on_close;
+    dummyfileref->posix_delete = fileref->posix_delete;
+    dummyfileref->deleted = fileref->deleted;
+    dummyfileref->created = fileref->created;
+    dummyfileref->parent = fileref->parent;
+
+    while (!IsListEmpty(&fileref->children)) {
+        file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&fileref->children), file_ref, list_entry);
+
+        free_fileref(fr->parent);
+
+        fr->parent = dummyfileref;
+        InterlockedIncrement(&dummyfileref->refcount);
+
+        InsertTailList(&dummyfileref->children, &fr->list_entry);
+    }
+
+    InsertTailList(fileref->list_entry.Blink, &dummyfileref->list_entry);
+
+    RemoveEntryList(&fileref->list_entry);
+    InsertTailList(&dummyfileref->children, &fileref->list_entry);
+
+    dummyfileref->dc = fileref->dc;
+    dummyfileref->dc->fileref = dummyfileref;
+
+    mark_fileref_dirty(dummyfileref);
+
+    free_fileref(dummyfileref);
+
+    // change fcb values
+
+    fileref->fcb->hash_ptrs = NULL;
+    fileref->fcb->hash_ptrs_uc = NULL;
+
+    fileref->fcb->ads = true;
+
+    fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
+    fileref->oldutf8.Buffer = NULL;
+
+    RtlZeroMemory(dc, sizeof(dir_child));
+
+    dc->utf8 = utf8;
+    dc->name = utf16;
+    dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
+    dc->name_uc = utf16uc;
+    dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
+    dc->fileref = fileref;
+    InsertTailList(&dummyfcb->dir_children_index, &dc->list_entry_index);
+
+    fileref->dc = dc;
+    fileref->parent = dummyfileref;
+
+    crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
+
+    fileref->fcb->adsxattr = adsxattr;
+    fileref->fcb->adshash = crc32;
+    fileref->fcb->adsmaxlen = newmaxlen;
+    fileref->fcb->adsdata = adsdata;
+
+    fileref->fcb->created = true;
+
+    mark_fcb_dirty(fileref->fcb);
+
+    Status = STATUS_SUCCESS;
+
+end:
+    if (sf)
+        free_fileref(sf);
+
+    return Status;
+}
+
 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
     FILE_RENAME_INFORMATION_EX* fri = Irp->AssociatedIrp.SystemBuffer;
-    fcb *fcb = FileObject->FsContext;
+    fcbfcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
     WCHAR* fn;
@@ -1515,6 +2468,9 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     SECURITY_SUBJECT_CONTEXT subjcont;
     ACCESS_MASK access;
     ULONG flags;
+#ifdef __REACTOS__
+    unsigned int i;
+#endif
 
     InitializeListHead(&rollback);
 
@@ -1558,11 +2514,39 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
+    if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
+        WARN("not allowing \\$Root to be renamed\n");
+        Status = STATUS_ACCESS_DENIED;
+        goto end;
+    }
+
     if (fcb->ads) {
-        // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
-        // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
-        // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
-        Status = STATUS_INVALID_PARAMETER;
+        if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
+            IO_STATUS_BLOCK iosb;
+
+            CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
+            if (!NT_SUCCESS(iosb.Status)) {
+                ERR("CcFlushCache returned %08x\n", iosb.Status);
+                Status = iosb.Status;
+                goto end;
+            }
+        }
+
+        Status = rename_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
+        goto end;
+    } else if (fnlen >= 1 && fn[0] == ':') {
+        if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
+            IO_STATUS_BLOCK iosb;
+
+            CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
+            if (!NT_SUCCESS(iosb.Status)) {
+                ERR("CcFlushCache returned %08x\n", iosb.Status);
+                Status = iosb.Status;
+                goto end;
+            }
+        }
+
+        Status = rename_file_to_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
         goto end;
     }
 
@@ -1571,6 +2555,18 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
 
     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
 
+#ifndef __REACTOS__
+    for (unsigned int i = 0 ; i < fnus.Length / sizeof(WCHAR); i++) {
+#else
+    for (i = 0 ; i < fnus.Length / sizeof(WCHAR); i++) {
+#endif
+        if (fnus.Buffer[i] == ':') {
+            TRACE("colon in filename\n");
+            Status = STATUS_OBJECT_NAME_INVALID;
+            goto end;
+        }
+    }
+
     origutf8len = fileref->dc->utf8.Length;
 
     Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
@@ -1602,7 +2598,7 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive,  Irp);
 
     if (NT_SUCCESS(Status)) {
-        TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
+        TRACE("destination file already exists\n");
 
         if (fileref != oldfileref && !oldfileref->deleted) {
             if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
@@ -2237,12 +3233,12 @@ static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFI
             mark_fcb_dirty(fileref->parent->fcb);
         }
 
-        send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
+        queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
 
         goto end;
     }
 
-    TRACE("file: %S\n", file_desc(FileObject));
+    TRACE("file: %p\n", FileObject);
     TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "true" : "false");
     TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n",
         fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
@@ -2301,7 +3297,7 @@ static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFI
 
     fcb->inode_item_changed = true;
     mark_fcb_dirty(fcb);
-    send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
 
     Status = STATUS_SUCCESS;
 
@@ -2332,7 +3328,7 @@ end:
 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
 
-    TRACE("setting the position on %S to %I64x\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
+    TRACE("setting the position on %p to %I64x\n", FileObject, fpi->CurrentByteOffset.QuadPart);
 
     // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
 
@@ -2460,7 +3456,7 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
 
     if (NT_SUCCESS(Status)) {
         if (!oldfileref->deleted) {
-            WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
+            WARN("destination file already exists\n");
 
             if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) {
                 Status = STATUS_OBJECT_NAME_COLLISION;
@@ -2755,7 +3751,7 @@ static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Ir
     fcb->inode_item_changed = true;
     mark_fcb_dirty(fcb);
 
-    send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
 
     Status = STATUS_SUCCESS;
 
@@ -2879,6 +3875,8 @@ NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
 
     TRACE("set information\n");
 
+    FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
     switch (IrpSp->Parameters.SetFile.FileInformationClass) {
         case FileAllocationInformation:
         {
@@ -2951,7 +3949,6 @@ NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
 
         case FileRenameInformation:
             TRACE("FileRenameInformation\n");
-            // FIXME - make this work with streams
             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
             break;
 
index ef847a5..456c854 100644 (file)
@@ -545,6 +545,9 @@ nextdev:
         for (num = 0; num < total_num; num++) {
             if (context.stripes[num].dmdsa)
                 ExFreePool(context.stripes[num].dmdsa);
+
+            if (context.stripes[num].Irp)
+                IoFreeIrp(context.stripes[num].Irp);
         }
 
         ExFreePool(context.stripes);
@@ -1633,13 +1636,14 @@ NTSTATUS do_tree_writes(device_extension* Vcb, LIST_ENTRY* tree_writes, bool no_
                 RtlCopyMemory(data, tw2->data, tw2->length);
                 RtlCopyMemory(&data[tw2->length], tw->data, tw->length);
 
-                if (!no_free)
+                if (!no_free || tw2->allocated)
                     ExFreePool(tw2->data);
 
                 tw2->data = data;
                 tw2->length += tw->length;
+                tw2->allocated = true;
 
-                if (!no_free) // FIXME - what if we allocated this just now?
+                if (!no_free || tw->allocated)
                     ExFreePool(tw->data);
 
                 RemoveEntryList(&tw->list_entry);
@@ -2025,6 +2029,7 @@ static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) {
             tw->address = t->new_address;
             tw->length = Vcb->superblock.node_size;
             tw->data = data;
+            tw->allocated = false;
 
             if (IsListEmpty(&tree_writes))
                 InsertTailList(&tree_writes, &tw->list_entry);
@@ -6482,8 +6487,6 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* batchlist, PIRP Irp
 
         crc32 = calc_crc32c(0xfffffffe, (uint8_t*)name->Buffer, name->Length);
 
-        TRACE("deleting %.*S\n", file_desc_fileref(fileref));
-
         di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + name->Length, ALLOC_TAG);
         if (!di) {
             ERR("out of memory\n");
@@ -6750,6 +6753,9 @@ static void flush_disk_caches(device_extension* Vcb) {
     LIST_ENTRY* le;
     ioctl_context context;
     ULONG num;
+#ifdef __REACTOS__
+    unsigned int i;
+#endif
 
     context.left = 0;
 
@@ -6827,6 +6833,15 @@ nextdev:
 
     KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
 
+#ifndef __REACTOS__
+    for (unsigned int i = 0; i < num; i++) {
+#else
+    for (i = 0; i < num; i++) {
+#endif
+        if (context.stripes[i].Irp)
+            IoFreeIrp(context.stripes[i].Irp);
+    }
+
     ExFreePool(context.stripes);
 }
 
index 039e828..5269838 100644 (file)
@@ -44,6 +44,8 @@
 extern LIST_ENTRY VcbList;
 extern ERESOURCE global_loading_lock;
 extern PDRIVER_OBJECT drvobj;
+extern tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest;
+extern tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks;
 
 static void mark_subvol_dirty(device_extension* Vcb, root* r);
 
@@ -598,7 +600,7 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
     if (is_subvol_readonly(fcb->subvol, Irp))
         return STATUS_ACCESS_DENIED;
 
-    if (!is_file_name_valid(&nameus, posix))
+    if (!is_file_name_valid(&nameus, posix, false))
         return STATUS_OBJECT_NAME_INVALID;
 
     utf8.Buffer = NULL;
@@ -820,7 +822,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     nameus.Length = nameus.MaximumLength = bcs->namelen;
     nameus.Buffer = bcs->name;
 
-    if (!is_file_name_valid(&nameus, bcs->posix))
+    if (!is_file_name_valid(&nameus, bcs->posix, false))
         return STATUS_OBJECT_NAME_INVALID;
 
     utf8.Buffer = NULL;
@@ -1789,7 +1791,7 @@ static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void*
     }
 
     mark_fcb_dirty(fcb);
-    send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
 
     Status = STATUS_SUCCESS;
 
@@ -2059,7 +2061,7 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     fcb->inode_item_changed = true;
     mark_fcb_dirty(fcb);
 
-    send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
 
     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
     fcb->subvol->root_item.ctime = now;
@@ -2568,22 +2570,25 @@ static void update_volumes(device_extension* Vcb) {
     ExReleaseResourceLite(&Vcb->tree_lock);
 }
 
-static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
+NTSTATUS dismount_volume(device_extension* Vcb, bool shutdown, PIRP Irp) {
     NTSTATUS Status;
+    bool open_files;
 
     TRACE("FSCTL_DISMOUNT_VOLUME\n");
 
     if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
         return STATUS_SUCCESS;
 
-    if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
-        WARN("attempting to dismount boot volume or one containing a pagefile\n");
-        return STATUS_ACCESS_DENIED;
-    }
+    if (!shutdown) {
+        if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
+            WARN("attempting to dismount boot volume or one containing a pagefile\n");
+            return STATUS_ACCESS_DENIED;
+        }
 
-    Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
-    if (!NT_SUCCESS(Status)) {
-        WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
+        Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
+        if (!NT_SUCCESS(Status)) {
+            WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
+        }
     }
 
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
@@ -2603,6 +2608,8 @@ static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
 
     Vcb->removing = true;
 
+    open_files = Vcb->open_files > 0;
+
     if (Vcb->vde) {
         update_volumes(Vcb);
         Vcb->vde->mounted_device = NULL;
@@ -2610,6 +2617,9 @@ static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
 
     ExReleaseResourceLite(&Vcb->tree_lock);
 
+    if (!open_files)
+        uninit(Vcb);
+
     return STATUS_SUCCESS;
 }
 
@@ -3647,7 +3657,7 @@ static NTSTATUS duplicate_extents(device_extension* Vcb, PFILE_OBJECT FileObject
 
         if (!ccb->user_set_write_time) {
             fcb->inode_item.st_mtime = now;
-            send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+            queue_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
         }
 
         fcb->inode_item_changed = true;
@@ -4040,7 +4050,7 @@ static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data
     send_notification_fileref(fileref, bmn->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
 
     if (!parccb->user_set_write_time)
-        send_notification_fcb(parfileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+        queue_notification_fcb(parfileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
 
     Status = STATUS_SUCCESS;
 
@@ -4727,6 +4737,7 @@ static NTSTATUS resize_device(device_extension* Vcb, void* data, ULONG len, PIRP
         delta = dev->devitem.num_bytes - br->size;
 
         if (need_balance) {
+            OBJECT_ATTRIBUTES oa;
             int i;
 
             if (Vcb->balance.thread) {
@@ -4751,7 +4762,9 @@ static NTSTATUS resize_device(device_extension* Vcb, void* data, ULONG len, PIRP
 
             space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL);
 
-            Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb);
+            InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+            Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
             if (!NT_SUCCESS(Status)) {
                 ERR("PsCreateSystemThread returned %08x\n", Status);
                 goto end;
@@ -4835,57 +4848,145 @@ end:
     return Status;
 }
 
-NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, uint32_t type) {
+static NTSTATUS fsctl_oplock(device_extension* Vcb, PIRP* Pirp) {
+    NTSTATUS Status;
     PIRP Irp = *Pirp;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    NTSTATUS Status;
+    uint32_t fsctl = IrpSp->Parameters.FileSystemControl.FsControlCode;
+    PFILE_OBJECT FileObject = IrpSp->FileObject;
+    fcb* fcb = FileObject ? FileObject->FsContext : NULL;
+    ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
+    file_ref* fileref = ccb ? ccb->fileref : NULL;
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+    PREQUEST_OPLOCK_INPUT_BUFFER buf = NULL;
+    bool oplock_request = false, oplock_ack = false;
+#else
+    bool oplock_request = false;
+#endif
+    ULONG oplock_count = 0;
+#ifdef __REACTOS__
+    bool shared_request;
+#endif
+
+    if (!fcb) {
+        ERR("fcb was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (!fileref) {
+        ERR("fileref was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_DIRECTORY)
+        return STATUS_INVALID_PARAMETER;
 
-    switch (type) {
 #if (NTDDI_VERSION >= NTDDI_WIN7)
-        case FSCTL_REQUEST_OPLOCK:
-            WARN("STUB: FSCTL_REQUEST_OPLOCK\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+    if (fsctl == FSCTL_REQUEST_OPLOCK) {
+        if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(REQUEST_OPLOCK_INPUT_BUFFER))
+            return STATUS_BUFFER_TOO_SMALL;
+
+        if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER))
+            return STATUS_BUFFER_TOO_SMALL;
+
+        buf = Irp->AssociatedIrp.SystemBuffer;
+
+        // flags are mutually exclusive
+        if (buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_REQUEST && buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_ACK)
+            return STATUS_INVALID_PARAMETER;
+
+        oplock_request = buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
+        oplock_ack = buf->Flags & REQUEST_OPLOCK_INPUT_FLAG_ACK;
+
+        if (!oplock_request && !oplock_ack)
+            return STATUS_INVALID_PARAMETER;
+    }
 #endif
 
-        case FSCTL_REQUEST_OPLOCK_LEVEL_1:
-            WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+    bool shared_request = (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2) || (fsctl == FSCTL_REQUEST_OPLOCK && !(buf->RequestedOplockLevel & OPLOCK_LEVEL_CACHE_WRITE));
+#else
+    shared_request = (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2);
+#endif
 
-        case FSCTL_REQUEST_OPLOCK_LEVEL_2:
-            WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+    if (fcb->type == BTRFS_TYPE_DIRECTORY && (fsctl != FSCTL_REQUEST_OPLOCK || !shared_request)) {
+#else
+    if (fcb->type == BTRFS_TYPE_DIRECTORY && !shared_request) {
+#endif
+        WARN("oplock requests on directories can only be for read or read-handle oplocks\n");
+        return STATUS_INVALID_PARAMETER;
+    }
 
-        case FSCTL_REQUEST_BATCH_OPLOCK:
-            WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
 
-        case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
-            WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+    if (fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_1 || fsctl == FSCTL_REQUEST_BATCH_OPLOCK || fsctl == FSCTL_REQUEST_FILTER_OPLOCK ||
+        fsctl == FSCTL_REQUEST_OPLOCK_LEVEL_2 || oplock_request) {
+        ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
-        case FSCTL_OPLOCK_BREAK_ACK_NO_2:
-            WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+        if (shared_request) {
+            if (fcb->type == BTRFS_TYPE_FILE) {
+                if (fFsRtlCheckLockForOplockRequest)
+                    oplock_count = !fFsRtlCheckLockForOplockRequest(&fcb->lock, &fcb->Header.AllocationSize);
+                else if (fFsRtlAreThereCurrentOrInProgressFileLocks)
+                    oplock_count = fFsRtlAreThereCurrentOrInProgressFileLocks(&fcb->lock);
+                else
+                    oplock_count = FsRtlAreThereCurrentFileLocks(&fcb->lock);
+            }
+        } else
+            oplock_count = fileref->open_count;
+    } else
+        ExAcquireResourceSharedLite(fcb->Header.Resource, true);
 
-        case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
-            WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+    if ((fsctl == FSCTL_REQUEST_FILTER_OPLOCK || fsctl == FSCTL_REQUEST_BATCH_OPLOCK ||
+        (fsctl == FSCTL_REQUEST_OPLOCK && buf->RequestedOplockLevel & OPLOCK_LEVEL_CACHE_HANDLE)) &&
+#else
+    if ((fsctl == FSCTL_REQUEST_FILTER_OPLOCK || fsctl == FSCTL_REQUEST_BATCH_OPLOCK) &&
+#endif
+        fileref->delete_on_close) {
+        ExReleaseResourceLite(fcb->Header.Resource);
+        ExReleaseResourceLite(&Vcb->tree_lock);
+        return STATUS_DELETE_PENDING;
+    }
 
-        case FSCTL_OPLOCK_BREAK_NOTIFY:
-            WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
-            break;
+    Status = FsRtlOplockFsctrl(fcb_oplock(fcb), Irp, oplock_count);
+
+    *Pirp = NULL;
+
+    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
 
+    ExReleaseResourceLite(fcb->Header.Resource);
+    ExReleaseResourceLite(&Vcb->tree_lock);
+
+    return Status;
+}
+
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, uint32_t type) {
+    PIRP Irp = *Pirp;
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    NTSTATUS Status;
+
+    if (IrpSp->FileObject && IrpSp->FileObject->FsContext) {
+        device_extension* Vcb = DeviceObject->DeviceExtension;
+
+        if (Vcb->type == VCB_TYPE_FS)
+            FsRtlCheckOplock(fcb_oplock(IrpSp->FileObject->FsContext), Irp, NULL, NULL, NULL);
+    }
+
+    switch (type) {
+        case FSCTL_REQUEST_OPLOCK_LEVEL_1:
+        case FSCTL_REQUEST_OPLOCK_LEVEL_2:
+        case FSCTL_REQUEST_BATCH_OPLOCK:
+        case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
+        case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
+        case FSCTL_OPLOCK_BREAK_NOTIFY:
+        case FSCTL_OPLOCK_BREAK_ACK_NO_2:
         case FSCTL_REQUEST_FILTER_OPLOCK:
-            WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
-            Status = STATUS_INVALID_DEVICE_REQUEST;
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+        case FSCTL_REQUEST_OPLOCK:
+#endif
+            Status = fsctl_oplock(DeviceObject->DeviceExtension, Pirp);
             break;
 
         case FSCTL_LOCK_VOLUME:
@@ -4897,7 +4998,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, uint32_t type) {
             break;
 
         case FSCTL_DISMOUNT_VOLUME:
-            Status = dismount_volume(DeviceObject->DeviceExtension, Irp);
+            Status = dismount_volume(DeviceObject->DeviceExtension, false, Irp);
             break;
 
         case FSCTL_IS_VOLUME_MOUNTED:
index 8654d84..fab85bc 100644 (file)
@@ -29,7 +29,7 @@ IsEven(IN USHORT Digit)
     return ((Digit & 1) != 1);
 }
 
-NTSTATUS compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer)
+NTSTATUS __stdcall compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer)
 {
     USHORT DataLength;
     ULONG ReparseTag;
index 2b012ed..3313d82 100644 (file)
@@ -185,14 +185,15 @@ NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     ExAcquireResourceExclusiveLite(&Vcb->tree_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;
-        goto end;
+        ExReleaseResourceLite(&Vcb->tree_lock);
+        return STATUS_ACCESS_DENIED;
     }
 
     Status = send_disks_pnp_message(Vcb, IRP_MN_QUERY_REMOVE_DEVICE);
     if (!NT_SUCCESS(Status)) {
         WARN("send_disks_pnp_message returned %08x\n", Status);
-        goto end;
+        ExReleaseResourceLite(&Vcb->tree_lock);
+        return Status;
     }
 
     Vcb->removing = true;
@@ -204,16 +205,17 @@ NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
         if (!NT_SUCCESS(Status)) {
             ERR("do_write returned %08x\n", Status);
-            goto end;
+            ExReleaseResourceLite(&Vcb->tree_lock);
+            return Status;
         }
     }
 
-
-    Status = STATUS_SUCCESS;
-end:
     ExReleaseResourceLite(&Vcb->tree_lock);
 
-    return Status;
+    if (Vcb->open_files == 0)
+        uninit(Vcb);
+
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
@@ -294,7 +296,10 @@ static NTSTATUS bus_query_device_relations(PIRP Irp) {
 
     le = pdo_list.Flink;
     while (le != &pdo_list) {
-        num_children++;
+        pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+        if (!pdode->dont_report)
+            num_children++;
 
         le = le->Flink;
     }
@@ -315,9 +320,11 @@ static NTSTATUS bus_query_device_relations(PIRP Irp) {
     while (le != &pdo_list) {
         pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
 
-        ObReferenceObject(pdode->pdo);
-        dr->Objects[i] = pdode->pdo;
-        i++;
+        if (!pdode->dont_report) {
+            ObReferenceObject(pdode->pdo);
+            dr->Objects[i] = pdode->pdo;
+            i++;
+        }
 
         le = le->Flink;
     }
@@ -539,6 +546,29 @@ static NTSTATUS pdo_device_usage_notification(pdo_device_extension* pdode, PIRP
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS pdo_query_device_relations(PDEVICE_OBJECT pdo, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    PDEVICE_RELATIONS device_relations;
+
+    if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
+        return Irp->IoStatus.Status;
+
+    device_relations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), ALLOC_TAG);
+    if (!device_relations) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    device_relations->Count = 1;
+    device_relations->Objects[0] = pdo;
+
+    ObReferenceObject(pdo);
+
+    Irp->IoStatus.Information = (ULONG_PTR)device_relations;
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     pdo_device_extension* pdode = pdo->DeviceExtension;
@@ -559,6 +589,8 @@ static NTSTATUS pdo_pnp(PDEVICE_OBJECT pdo, PIRP Irp) {
         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
             return pdo_device_usage_notification(pdode, Irp);
 
+        case IRP_MN_QUERY_DEVICE_RELATIONS:
+            return pdo_query_device_relations(pdo, Irp);
     }
 
     return Irp->IoStatus.Status;
index 5934821..ae7a270 100644 (file)
@@ -3114,7 +3114,7 @@ NTSTATUS do_read(PIRP Irp, bool wait, ULONG* bytes_read) {
     if (!fcb || !fcb->Vcb || !fcb->subvol)
         return STATUS_INTERNAL_ERROR;
 
-    TRACE("file = %S (fcb = %p)\n", file_desc(FileObject), fcb);
+    TRACE("fcb = %p\n", fcb);
     TRACE("offset = %I64x, length = %x\n", start, length);
     TRACE("paging_io = %s, no cache = %s\n", Irp->Flags & IRP_PAGING_IO ? "true" : "false", Irp->Flags & IRP_NOCACHE ? "true" : "false");
 
@@ -3318,6 +3318,9 @@ NTSTATUS __stdcall drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit2;
     }
 
+    if (!(Irp->Flags & IRP_PAGING_IO))
+        FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
     wait = IoIsOperationSynchronous(Irp);
 
     // Don't offload jobs when doing paging IO - otherwise this can lead to
index a13baf3..398d700 100644 (file)
@@ -37,7 +37,8 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     BTRFS_UUID* uuid = &Vcb->superblock.uuid;
     mount_options* options = &Vcb->options;
     UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
-                   maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus, allowdegradedus, zstdlevelus;
+                   maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus, allowdegradedus, zstdlevelus,
+                   norootdirus;
     OBJECT_ATTRIBUTES oa;
     NTSTATUS Status;
     ULONG i, j, kvfilen, index, retlen;
@@ -121,6 +122,7 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     RtlInitUnicodeString(&clearcacheus, L"ClearCache");
     RtlInitUnicodeString(&allowdegradedus, L"AllowDegraded");
     RtlInitUnicodeString(&zstdlevelus, L"ZstdLevel");
+    RtlInitUnicodeString(&norootdirus, L"NoRootDir");
 
     do {
         Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
@@ -193,6 +195,10 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
 
                 options->zstd_level = *val;
+            } else if (FsRtlAreNamesEqual(&norootdirus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
+
+                options->no_root_dir = *val;
             }
         } else if (Status != STATUS_NO_MORE_ENTRIES) {
             ERR("ZwEnumerateValueKey returned %08x\n", Status);
@@ -805,6 +811,7 @@ void read_registry(PUNICODE_STRING regpath, bool refresh) {
     get_registry_value(h, L"AllowDegraded", REG_DWORD, &mount_allow_degraded, sizeof(mount_allow_degraded));
     get_registry_value(h, L"Readonly", REG_DWORD, &mount_readonly, sizeof(mount_readonly));
     get_registry_value(h, L"ZstdLevel", REG_DWORD, &mount_zstd_level, sizeof(mount_zstd_level));
+    get_registry_value(h, L"NoRootDir", REG_DWORD, &mount_no_root_dir, sizeof(mount_no_root_dir));
 
     if (!refresh)
         get_registry_value(h, L"NoPNP", REG_DWORD, &no_pnp, sizeof(no_pnp));
index fb6a988..a926bbd 100644 (file)
@@ -414,8 +414,6 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         fcb = fileref->fcb;
     }
 
-    TRACE("%S\n", file_desc(FileObject));
-
     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
@@ -425,7 +423,7 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto end;
     }
 
-    send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
 
 end:
     if (NT_SUCCESS(Status))
@@ -488,8 +486,6 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
 
-    TRACE("%S\n", file_desc(FileObject));
-
     if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
         ERR("buffer was too short\n");
         Status = STATUS_INVALID_PARAMETER;
@@ -619,7 +615,7 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
     Status = STATUS_SUCCESS;
 
-    send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
 
 end:
     if (NT_SUCCESS(Status))
index d1994b7..e4d70d0 100644 (file)
@@ -3265,6 +3265,7 @@ end:
 
 NTSTATUS start_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
     NTSTATUS Status;
+    OBJECT_ATTRIBUTES oa;
 
     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
         return STATUS_PRIVILEGE_NOT_HELD;
@@ -3292,7 +3293,9 @@ NTSTATUS start_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
     Vcb->scrub.error = STATUS_SUCCESS;
     KeInitializeEvent(&Vcb->scrub.event, NotificationEvent, !Vcb->scrub.paused);
 
-    Status = PsCreateSystemThread(&Vcb->scrub.thread, 0, NULL, NULL, NULL, scrub_thread, Vcb);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&Vcb->scrub.thread, 0, &oa, NULL, NULL, scrub_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         return Status;
index a1a4217..3d824d9 100644 (file)
@@ -23,6 +23,7 @@
 #include <windef.h>
 #include <ntddstor.h>
 #include <ntdddisk.h>
+#include <ntddvol.h>
 
 #include <initguid.h>
 #include <wdmguid.h>
@@ -35,6 +36,7 @@ extern HANDLE mountmgr_thread_handle;
 extern bool shutting_down;
 extern PDEVICE_OBJECT busobj;
 extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
+extern ERESOURCE boot_lock;
 
 typedef void (*pnp_callback)(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
 
@@ -270,8 +272,11 @@ void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
 
     UNUSED(DriverObject);
 
+    ExAcquireResourceSharedLite(&boot_lock, TRUE);
+
     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj);
     if (!NT_SUCCESS(Status)) {
+        ExReleaseResourceLite(&boot_lock);
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
         return;
     }
@@ -323,6 +328,8 @@ void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
 
 end:
     ObDereferenceObject(fileobj);
+
+    ExReleaseResourceLite(&boot_lock);
 }
 
 void remove_volume_child(_Inout_ _Requires_exclusive_lock_held_(_Curr_->child_lock) _Releases_exclusive_lock_(_Curr_->child_lock) _In_ volume_device_extension* vde,
@@ -496,8 +503,11 @@ void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
 
     TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
 
+    ExAcquireResourceSharedLite(&boot_lock, TRUE);
+
     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &fileobj, &devobj);
     if (!NT_SUCCESS(Status)) {
+        ExReleaseResourceLite(&boot_lock);
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
         return;
     }
@@ -507,6 +517,10 @@ void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
     if (devobj->DriverObject == DriverObject)
         goto end;
 
+    Status = dev_ioctl(devobj, IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, true, NULL);
+    if (!NT_SUCCESS(Status))
+        TRACE("IOCTL_VOLUME_ONLINE returned %08x\n", Status);
+
     Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status);
@@ -570,6 +584,8 @@ void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
 
 end:
     ObDereferenceObject(fileobj);
+
+    ExReleaseResourceLite(&boot_lock);
 }
 
 void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
@@ -648,6 +664,8 @@ static void __stdcall do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
         ExFreePool(context->name.Buffer);
 
     IoFreeWorkItem(context->work_item);
+
+    ExFreePool(context);
 }
 
 static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING name, pnp_callback func) {
@@ -655,6 +673,10 @@ static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING na
     pnp_callback_context* context;
 
     work_item = IoAllocateWorkItem(master_devobj);
+    if (!work_item) {
+        ERR("out of memory\n");
+        return;
+    }
 
     context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG);
 
index 42ff987..bf5cff0 100644 (file)
@@ -432,7 +432,7 @@ static void get_top_level_sd(fcb* fcb) {
         goto end;
     }
 
-    RtlSetOwnerSecurityDescriptor(&sd, usersid, false);
+    Status = RtlSetOwnerSecurityDescriptor(&sd, usersid, false);
 
     if (!NT_SUCCESS(Status)) {
         ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
@@ -442,11 +442,10 @@ static void get_top_level_sd(fcb* fcb) {
     gid_to_sid(fcb->inode_item.st_gid, &groupsid);
     if (!groupsid) {
         ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
 
-    RtlSetGroupSecurityDescriptor(&sd, groupsid, false);
+    Status = RtlSetGroupSecurityDescriptor(&sd, groupsid, false);
 
     if (!NT_SUCCESS(Status)) {
         ERR("RtlSetGroupSecurityDescriptor returned %08x\n", Status);
@@ -486,7 +485,6 @@ static void get_top_level_sd(fcb* fcb) {
     fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
     if (!fcb->sd) {
         ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
 
@@ -494,6 +492,8 @@ static void get_top_level_sd(fcb* fcb) {
 
     if (!NT_SUCCESS(Status)) {
         ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
+        ExFreePool(fcb->sd);
+        fcb->sd = NULL;
         goto end;
     }
 
@@ -528,6 +528,7 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent, bool look_for_xattr, PIRP Irp) {
                                 &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
     if (!NT_SUCCESS(Status)) {
         ERR("SeAssignSecurityEx returned %08x\n", Status);
+        return;
     }
 
     Status = uid_to_sid(fcb->inode_item.st_uid, &usersid);
@@ -541,6 +542,7 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent, bool look_for_xattr, PIRP Irp) {
     gid_to_sid(fcb->inode_item.st_gid, &groupsid);
     if (!groupsid) {
         ERR("out of memory\n");
+        ExFreePool(usersid);
         return;
     }
 
@@ -733,7 +735,7 @@ static NTSTATUS set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject
 
     mark_fcb_dirty(fcb);
 
-    send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED, NULL);
+    queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED, NULL);
 
 end:
     ExReleaseResourceLite(fcb->Header.Resource);
index 4737be0..2090131 100644 (file)
@@ -3598,6 +3598,7 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJ
     send_info* send;
     ULONG num_clones = 0;
     root** clones = NULL;
+    OBJECT_ATTRIBUTES oa;
 
     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
         return STATUS_INVALID_PARAMETER;
@@ -3810,7 +3811,9 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJ
 
     InterlockedIncrement(&Vcb->running_sends);
 
-    Status = PsCreateSystemThread(&send->thread, 0, NULL, NULL, NULL, send_thread, context);
+    InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = PsCreateSystemThread(&send->thread, 0, &oa, NULL, NULL, send_thread, context);
     if (!NT_SUCCESS(Status)) {
         ERR("PsCreateSystemThread returned %08x\n", Status);
         ccb->send = NULL;
index d50852b..0718bb8 100644 (file)
@@ -31,6 +31,7 @@ extern PDEVICE_OBJECT busobj;
 extern ERESOURCE pdo_list_lock;
 extern LIST_ENTRY pdo_list;
 extern UNICODE_STRING registry_path;
+extern tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
 
 NTSTATUS vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     volume_device_extension* vde = DeviceObject->DeviceExtension;
@@ -46,6 +47,51 @@ NTSTATUS vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     return STATUS_SUCCESS;
 }
 
+void free_vol(volume_device_extension* vde) {
+    PDEVICE_OBJECT pdo;
+
+    vde->dead = true;
+
+    if (vde->mounted_device) {
+        device_extension* Vcb = vde->mounted_device->DeviceExtension;
+
+        Vcb->vde = NULL;
+    }
+
+    if (vde->name.Buffer)
+        ExFreePool(vde->name.Buffer);
+
+    ExDeleteResourceLite(&vde->pdode->child_lock);
+
+    if (vde->pdo->AttachedDevice)
+        IoDetachDevice(vde->pdo);
+
+    while (!IsListEmpty(&vde->pdode->children)) {
+        volume_child* vc = CONTAINING_RECORD(RemoveHeadList(&vde->pdode->children), volume_child, list_entry);
+
+        if (vc->notification_entry) {
+            if (fIoUnregisterPlugPlayNotificationEx)
+                fIoUnregisterPlugPlayNotificationEx(vc->notification_entry);
+            else
+                IoUnregisterPlugPlayNotification(vc->notification_entry);
+        }
+
+        if (vc->pnp_name.Buffer)
+            ExFreePool(vc->pnp_name.Buffer);
+
+        ExFreePool(vc);
+    }
+
+    if (no_pnp)
+        ExFreePool(vde->pdode);
+
+    pdo = vde->pdo;
+    IoDeleteDevice(vde->device);
+
+    if (!no_pnp)
+        IoDeleteDevice(pdo);
+}
+
 NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     volume_device_extension* vde = DeviceObject->DeviceExtension;
     pdo_device_extension* pdode = vde->pdode;
@@ -54,47 +100,22 @@ NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
 
     Irp->IoStatus.Information = 0;
 
+    if (vde->dead)
+        return STATUS_SUCCESS;
+
     ExAcquireResourceExclusiveLite(&pdo_list_lock, true);
 
+    if (vde->dead) {
+        ExReleaseResourceLite(&pdo_list_lock);
+        return STATUS_SUCCESS;
+    }
+
     ExAcquireResourceSharedLite(&pdode->child_lock, true);
 
     if (InterlockedDecrement(&vde->open_count) == 0 && vde->removing) {
-        NTSTATUS Status;
-        UNICODE_STRING mmdevpath;
-        PDEVICE_OBJECT mountmgr;
-        PFILE_OBJECT mountmgrfo;
-        PDEVICE_OBJECT pdo;
-
-        RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
-        Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
-        if (!NT_SUCCESS(Status))
-            ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
-        else {
-            remove_drive_letter(mountmgr, &vde->name);
-
-            ObDereferenceObject(mountmgrfo);
-        }
-
-        if (vde->mounted_device) {
-            device_extension* Vcb = vde->mounted_device->DeviceExtension;
-
-            Vcb->vde = NULL;
-        }
-
-        if (vde->name.Buffer)
-            ExFreePool(vde->name.Buffer);
-
         ExReleaseResourceLite(&pdode->child_lock);
-        ExDeleteResourceLite(&pdode->child_lock);
-
-        if (vde->pdo->AttachedDevice)
-            IoDetachDevice(vde->pdo);
-
-        pdo = vde->pdo;
-        IoDeleteDevice(vde->device);
 
-        if (!no_pnp)
-            IoDeleteDevice(pdo);
+        free_vol(vde);
     } else
         ExReleaseResourceLite(&pdode->child_lock);
 
@@ -767,6 +788,8 @@ static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) {
 
     ExReleaseResourceLite(&pdode->child_lock);
 
+    IoFreeIrp(Irp2);
+
     return Status;
 }
 
@@ -998,108 +1021,129 @@ end:
 }
 
 typedef struct {
-    PIO_WORKITEM work_item;
-    pdo_device_extension* pdode;
-} drive_letter_callback_context;
-
-_Function_class_(IO_WORKITEM_ROUTINE)
-static void __stdcall drive_letter_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
-    drive_letter_callback_context* context = con;
+    LIST_ENTRY list_entry;
+    UNICODE_STRING name;
     NTSTATUS Status;
-    UNICODE_STRING mmdevpath;
-    PDEVICE_OBJECT mountmgr;
-    PFILE_OBJECT mountmgrfo;
-    LIST_ENTRY* le;
-
-    UNUSED(DeviceObject);
+    BTRFS_UUID uuid;
+} drive_letter_removal;
 
-    RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
-    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
-        return;
-    }
-
-    ExAcquireResourceSharedLite(&pdo_list_lock, true);
-
-    le = pdo_list.Flink;
-    while (le != &pdo_list) {
-        pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) {
+    LIST_ENTRY* le;
+    LIST_ENTRY dlrlist;
 
-        if (pdode == context->pdode) {
-            LIST_ENTRY* le2;
+    InitializeListHead(&dlrlist);
 
-            ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
+    ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
 
-            le2 = pdode->children.Flink;
+    le = pdode->children.Flink;
 
-            while (le2 != &pdode->children) {
-                UNICODE_STRING name;
+    while (le != &pdode->children) {
+        drive_letter_removal* dlr;
 
-                volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
 
-                name.Length = name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
-                name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
+        dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG);
+        if (!dlr) {
+            ERR("out of memory\n");
 
-                if (!name.Buffer) {
-                    ERR("out of memory\n");
+            while (!IsListEmpty(&dlrlist)) {
+                dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
 
-                    ExReleaseResourceLite(&pdode->child_lock);
-                    ExReleaseResourceLite(&pdo_list_lock);
-                    ObDereferenceObject(mountmgrfo);
-                    IoFreeWorkItem(context->work_item);
-                    return;
-                }
+                ExFreePool(dlr->name.Buffer);
+                ExFreePool(dlr);
+            }
 
-                RtlCopyMemory(name.Buffer, L"\\??", 3 * sizeof(WCHAR));
-                RtlCopyMemory(&name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
+            ExReleaseResourceLite(&pdode->child_lock);
+            return;
+        }
 
-                Status = remove_drive_letter(mountmgr, &name);
+        dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
+        dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG);
 
-                if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
-                    WARN("remove_drive_letter returned %08x\n", Status);
+        if (!dlr->name.Buffer) {
+            ERR("out of memory\n");
 
-                ExFreePool(name.Buffer);
+            ExFreePool(dlr);
 
-                vc->had_drive_letter = NT_SUCCESS(Status);
+            while (!IsListEmpty(&dlrlist)) {
+                dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
 
-                le2 = le2->Flink;
+                ExFreePool(dlr->name.Buffer);
+                ExFreePool(dlr);
             }
 
             ExReleaseResourceLite(&pdode->child_lock);
-            ExReleaseResourceLite(&pdo_list_lock);
-            ObDereferenceObject(mountmgrfo);
-            IoFreeWorkItem(context->work_item);
             return;
         }
 
+        RtlCopyMemory(dlr->name.Buffer, L"\\??", 3 * sizeof(WCHAR));
+        RtlCopyMemory(&dlr->name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
+
+        dlr->uuid = vc->uuid;
+
+        InsertTailList(&dlrlist, &dlr->list_entry);
+
         le = le->Flink;
     }
 
-    ExReleaseResourceLite(&pdo_list_lock);
+    ExReleaseResourceLite(&pdode->child_lock);
 
-    ObDereferenceObject(mountmgrfo);
-    IoFreeWorkItem(context->work_item);
-}
+    le = dlrlist.Flink;
+    while (le != &dlrlist) {
+        drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry);
 
-static void add_drive_letter_work_item(pdo_device_extension* pdode) {
-    PIO_WORKITEM work_item;
-    drive_letter_callback_context* context;
+        dlr->Status = remove_drive_letter(mountmgr, &dlr->name);
 
-    work_item = IoAllocateWorkItem(master_devobj);
+        if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND)
+            WARN("remove_drive_letter returned %08x\n", dlr->Status);
 
-    context = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_callback_context), ALLOC_TAG);
+        le = le->Flink;
+    }
 
-    if (!context) {
-        ERR("out of memory\n");
-        IoFreeWorkItem(work_item);
+    // set vc->had_drive_letter
+
+    ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
+
+    while (!IsListEmpty(&dlrlist)) {
+        drive_letter_removal* dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry);
+
+        le = pdode->children.Flink;
+
+        while (le != &pdode->children) {
+            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+
+            if (RtlCompareMemory(&vc->uuid, &dlr->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                vc->had_drive_letter = NT_SUCCESS(dlr->Status);
+                break;
+            }
+
+            le = le->Flink;
+        }
+
+        ExFreePool(dlr->name.Buffer);
+        ExFreePool(dlr);
+    }
+
+    ExReleaseResourceLite(&pdode->child_lock);
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+static void __stdcall drive_letter_callback(pdo_device_extension* pdode) {
+    NTSTATUS Status;
+    UNICODE_STRING mmdevpath;
+    PDEVICE_OBJECT mountmgr;
+    PFILE_OBJECT mountmgrfo;
+
+    RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
         return;
     }
 
-    context->work_item = work_item;
-    context->pdode = pdode;
+    drive_letter_callback2(pdode, mountmgr);
 
-    IoQueueWorkItem(work_item, drive_letter_callback, DelayedWorkQueue, context);
+    ObDereferenceObject(mountmgrfo);
 }
 
 void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length, ULONG disk_num, ULONG part_num) {
@@ -1112,6 +1156,7 @@ void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length,
     bool inserted = false, new_pdo = false;
     pdo_device_extension* pdode = NULL;
     PDEVICE_OBJECT pdo = NULL;
+    bool process_drive_letters = false;
 
     if (devpath->Length == 0)
         return;
@@ -1312,7 +1357,7 @@ void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length,
                 WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
         }
 
-        add_drive_letter_work_item(pdode);
+        process_drive_letters = true;
     }
 
     ExReleaseResourceLite(&pdode->child_lock);
@@ -1322,6 +1367,9 @@ void add_volume_device(superblock* sb, PUNICODE_STRING devpath, uint64_t length,
 
     ExReleaseResourceLite(&pdo_list_lock);
 
+    if (process_drive_letters)
+        drive_letter_callback(pdode);
+
     if (new_pdo) {
         if (no_pnp)
             AddDevice(drvobj, pdo);
index c982e93..96ae287 100644 (file)
@@ -4164,7 +4164,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     TRACE("(%p, %p, %I64x, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
 
     if (*length == 0) {
-        WARN("returning success for zero-length write\n");
+        TRACE("returning success for zero-length write\n");
         return STATUS_SUCCESS;
     }
 
@@ -4231,20 +4231,18 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
             acquired_tree_lock = true;
     }
 
-    if (no_cache) {
-        if (pagefile) {
-            if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
-                Status = STATUS_PENDING;
-                goto end;
-            } else
-                acquired_fcb_lock = true;
-        } else if (!ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) {
-            if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) {
-                Status = STATUS_PENDING;
-                goto end;
-            } else
-                acquired_fcb_lock = true;
-        }
+    if (pagefile) {
+        if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
+            Status = STATUS_PENDING;
+            goto end;
+        } else
+            acquired_fcb_lock = true;
+    } else if (!ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) {
+        if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) {
+            Status = STATUS_PENDING;
+            goto end;
+        } else
+            acquired_fcb_lock = true;
     }
 
     newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
@@ -4258,7 +4256,6 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
         if (paging_io) {
             if (off64 >= newlength) {
                 TRACE("paging IO tried to write beyond end of file (file size = %I64x, offset = %I64x, length = %x)\n", newlength, off64, *length);
-                TRACE("filename %S\n", file_desc(FileObject));
                 TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n",
                     fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
                 Status = STATUS_SUCCESS;
@@ -4354,8 +4351,8 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
         } _SEH2_END;
 
         if (changed_length) {
-            send_notification_fcb(fcb->ads ? fileref->parent : fileref, fcb->ads ? FILE_NOTIFY_CHANGE_STREAM_SIZE : FILE_NOTIFY_CHANGE_SIZE,
-                                  fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
+            queue_notification_fcb(fcb->ads ? fileref->parent : fileref, fcb->ads ? FILE_NOTIFY_CHANGE_STREAM_SIZE : FILE_NOTIFY_CHANGE_SIZE,
+                                   fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
         }
 
         goto end;
@@ -4609,8 +4606,8 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     Status = STATUS_SUCCESS;
 
     if (filter != 0)
-        send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED,
-                              fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
+        queue_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED,
+                               fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
 
 end:
     if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) {
@@ -4779,6 +4776,9 @@ NTSTATUS __stdcall drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
             Irp->MdlAddress = NULL;
             Status = STATUS_SUCCESS;
         } else {
+            if (!(Irp->Flags & IRP_PAGING_IO))
+                FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
+
             // Don't offload jobs when doing paging IO - otherwise this can lead to
             // deadlocks in CcCopyWrite.
             if (Irp->Flags & IRP_PAGING_IO)
index 534e264..9ce6e3a 100644 (file)
@@ -3,7 +3,7 @@
 
 The following FSD are shared with: https://github.com/maharmstone/btrfs.
 
-reactos/drivers/filesystems/btrfs           # Synced to 1.4
+reactos/drivers/filesystems/btrfs           # Synced to 1.5
 reactos/dll/shellext/shellbtrfs             # Synced to 1.1
 reactos/sdk/lib/fslib/btrfslib              # Synced to 1.4