From 62e630de4c8c43542a04a848a0f2abf89e15f4ed Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Tue, 12 Nov 2019 19:32:46 +0100 Subject: [PATCH] [BTRFS] Upgrade to 1.5 CORE-16494 --- drivers/filesystems/btrfs/balance.c | 26 +- drivers/filesystems/btrfs/boot.c | 140 ++- drivers/filesystems/btrfs/btrfs.c | 539 +++++++----- drivers/filesystems/btrfs/btrfs.h | 1 + drivers/filesystems/btrfs/btrfs.rc | 8 +- drivers/filesystems/btrfs/btrfs_drv.h | 110 ++- drivers/filesystems/btrfs/btrfsioctl.h | 1 + drivers/filesystems/btrfs/cache.c | 24 +- drivers/filesystems/btrfs/create.c | 157 +++- drivers/filesystems/btrfs/devctrl.c | 15 + drivers/filesystems/btrfs/dirctrl.c | 4 +- drivers/filesystems/btrfs/fastio.c | 28 +- drivers/filesystems/btrfs/fileinfo.c | 1033 ++++++++++++++++++++++- drivers/filesystems/btrfs/flushthread.c | 23 +- drivers/filesystems/btrfs/fsctl.c | 217 +++-- drivers/filesystems/btrfs/fsrtl.c | 2 +- drivers/filesystems/btrfs/pnp.c | 56 +- drivers/filesystems/btrfs/read.c | 5 +- drivers/filesystems/btrfs/registry.c | 9 +- drivers/filesystems/btrfs/reparse.c | 8 +- drivers/filesystems/btrfs/scrub.c | 5 +- drivers/filesystems/btrfs/search.c | 22 + drivers/filesystems/btrfs/security.c | 12 +- drivers/filesystems/btrfs/send.c | 5 +- drivers/filesystems/btrfs/volume.c | 290 ++++--- drivers/filesystems/btrfs/write.c | 40 +- media/doc/README.FSD | 2 +- 27 files changed, 2216 insertions(+), 566 deletions(-) diff --git a/drivers/filesystems/btrfs/balance.c b/drivers/filesystems/btrfs/balance.c index b7794125deb..1a2c6a2dc6a 100644 --- a/drivers/filesystems/btrfs/balance.c +++ b/drivers/filesystems/btrfs/balance.c @@ -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; diff --git a/drivers/filesystems/btrfs/boot.c b/drivers/filesystems/btrfs/boot.c index f73fc38b4e8..34fee170ec7 100755 --- a/drivers/filesystems/btrfs/boot.c +++ b/drivers/filesystems/btrfs/boot.c @@ -27,11 +27,26 @@ 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); + } } diff --git a/drivers/filesystems/btrfs/btrfs.c b/drivers/filesystems/btrfs/btrfs.c index 2977667de9a..2080c5d70f7 100644 --- a/drivers/filesystems/btrfs/btrfs.c +++ b/drivers/filesystems/btrfs/btrfs.c @@ -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 - mprus.Length) / sizeof(WCHAR)]; - name.Length = name.MaximumLength = mprus.Length; + name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)]; + name.Length = name.MaximumLength = usmpr.Length; - blacklist = FsRtlAreNamesEqual(&name, &mprus, true, NULL); + blacklist = FsRtlAreNamesEqual(&name, &usmpr, true, NULL); } - if (!blacklist && entry->FullDllName.Length >= cmdus.Length) { + if (!blacklist && entry->FullDllName.Length >= uscmd.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 - uscmd.Length) / sizeof(WCHAR)]; + name.Length = name.MaximumLength = uscmd.Length; - blacklist = FsRtlAreNamesEqual(&name, &cmdus, true, NULL); + blacklist = FsRtlAreNamesEqual(&name, &uscmd, true, NULL); } - if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) { + if (!blacklist && entry->FullDllName.Length >= usfsutil.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 - usfsutil.Length) / sizeof(WCHAR)]; + name.Length = name.MaximumLength = usfsutil.Length; - blacklist = FsRtlAreNamesEqual(&name, &fsutilus, true, NULL); + blacklist = FsRtlAreNamesEqual(&name, &usfsutil, true, NULL); + } + + if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) { + UNICODE_STRING name; + + name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)]; + name.Length = name.MaximumLength = usstorsvc.Length; + + 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,13 +5209,109 @@ exit: return Status; } +void do_shutdown(PIRP Irp) { + LIST_ENTRY* le; + bus_device_extension* bde; + + shutting_down = true; + KeSetEvent(&mountmgr_thread_event, 0, false); + + le = VcbList.Flink; + while (le != &VcbList) { + LIST_ENTRY* le2 = le->Flink; + + device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); + volume_device_extension* vde = Vcb->vde; + + TRACE("shutting down Vcb %p\n", Vcb); + + if (vde) + InterlockedIncrement(&vde->open_count); + + 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("IoGetDeviceObjectPointer returned %08x\n", Status); + else { + remove_drive_letter(mountmgr, &vde->name); + + ObDereferenceObject(mountmgrfo); + } + + vde->removing = true; + + IoAcquireVpbSpinLock(&irql); + vde->device->Vpb->DeviceObject = vde->device; + IoReleaseVpbSpinLock(irql); + + if (InterlockedDecrement(&vde->open_count) == 0) + free_vol(vde); + } + + le = le2; + } + +#ifdef _DEBUG + if (comfo) { + ObDereferenceObject(comfo); + comdo = NULL; + comfo = NULL; + } +#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; - LIST_ENTRY* le; FsRtlEnterFileSystem(); @@ -5221,45 +5326,7 @@ static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PI Status = STATUS_SUCCESS; - 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); - - TRACE("shutting down Vcb %p\n", Vcb); - - ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); - Vcb->removing = true; - open_files = Vcb->open_files > 0; - - if (Vcb->need_write && !Vcb->readonly) { - Status = do_write(Vcb, Irp); - if (!NT_SUCCESS(Status)) - ERR("do_write returned %08x\n", Status); - } - - free_trees(Vcb); - - ExReleaseResourceLite(&Vcb->tree_lock); - - if (!open_files) - uninit(Vcb); - - le = le2; - } - -#ifdef _DEBUG - if (comfo) { - ObDereferenceObject(comfo); - comdo = NULL; - comfo = NULL; - } -#endif + do_shutdown(Irp); end: Irp->IoStatus.Status = Status; @@ -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(°raded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL); + InitializeObjectAttributes(&system_thread_attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = PsCreateSystemThread(°raded_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, ¬ification_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); diff --git a/drivers/filesystems/btrfs/btrfs.h b/drivers/filesystems/btrfs/btrfs.h index f55ef82683e..780bb5d0bab 100644 --- a/drivers/filesystems/btrfs/btrfs.h +++ b/drivers/filesystems/btrfs/btrfs.h @@ -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 diff --git a/drivers/filesystems/btrfs/btrfs.rc b/drivers/filesystems/btrfs/btrfs.rc index c899d599b5d..a594c6014d1 100644 --- a/drivers/filesystems/btrfs/btrfs.rc +++ b/drivers/filesystems/btrfs/btrfs.rc @@ -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" diff --git a/drivers/filesystems/btrfs/btrfs_drv.h b/drivers/filesystems/btrfs/btrfs_drv.h index 67cb24902c7..e24e4980244 100644 --- a/drivers/filesystems/btrfs/btrfs_drv.h +++ b/drivers/filesystems/btrfs/btrfs_drv.h @@ -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 (*tFsRtlUpdateDiskCounters)(ULONG64 BytesRead, ULONG64 BytesWritten); +typedef VOID (__stdcall *tFsRtlUpdateDiskCounters)(ULONG64 BytesRead, ULONG64 BytesWritten); -typedef NTSTATUS (*tIoUnregisterPlugPlayNotificationEx)(PVOID NotificationEntry); +typedef NTSTATUS (__stdcall *tIoUnregisterPlugPlayNotificationEx)(PVOID NotificationEntry); -typedef NTSTATUS (*tFsRtlGetEcpListFromIrp)(PIRP Irp, PECP_LIST* EcpList); +typedef NTSTATUS (__stdcall *tFsRtlGetEcpListFromIrp)(PIRP Irp, PECP_LIST* EcpList); -typedef NTSTATUS (*tFsRtlGetNextExtraCreateParameter)(PECP_LIST EcpList, PVOID CurrentEcpContext, LPGUID NextEcpType, - PVOID* NextEcpContext, ULONG* NextEcpContextSize); +typedef NTSTATUS (__stdcall *tFsRtlGetNextExtraCreateParameter)(PECP_LIST EcpList, PVOID CurrentEcpContext, LPGUID NextEcpType, + PVOID* NextEcpContext, ULONG* NextEcpContextSize); -typedef NTSTATUS (*tFsRtlValidateReparsePointBuffer)(ULONG BufferLength, PREPARSE_DATA_BUFFER ReparseBuffer); +typedef NTSTATUS (__stdcall *tFsRtlValidateReparsePointBuffer)(ULONG BufferLength, PREPARSE_DATA_BUFFER ReparseBuffer); + +typedef BOOLEAN (__stdcall *tFsRtlCheckLockForOplockRequest)(PFILE_LOCK FileLock, PLARGE_INTEGER AllocationSize); + +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 diff --git a/drivers/filesystems/btrfs/btrfsioctl.h b/drivers/filesystems/btrfs/btrfsioctl.h index e08713119c6..0ba18cf837a 100644 --- a/drivers/filesystems/btrfs/btrfsioctl.h +++ b/drivers/filesystems/btrfs/btrfsioctl.h @@ -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; diff --git a/drivers/filesystems/btrfs/cache.c b/drivers/filesystems/btrfs/cache.c index 42ce02c03da..90b0e2a3547 100644 --- a/drivers/filesystems/btrfs/cache.c +++ b/drivers/filesystems/btrfs/cache.c @@ -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; } diff --git a/drivers/filesystems/btrfs/create.c b/drivers/filesystems/btrfs/create.c index 039fe287f13..4eebef939b1 100644 --- a/drivers/filesystems/btrfs/create.c +++ b/drivers/filesystems/btrfs/create.c @@ -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); diff --git a/drivers/filesystems/btrfs/devctrl.c b/drivers/filesystems/btrfs/devctrl.c index 99c2e542d5e..e3c69c97124 100644 --- a/drivers/filesystems/btrfs/devctrl.c +++ b/drivers/filesystems/btrfs/devctrl.c @@ -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; diff --git a/drivers/filesystems/btrfs/dirctrl.c b/drivers/filesystems/btrfs/dirctrl.c index 41a68268add..e6e96fe5fa0 100644 --- a/drivers/filesystems/btrfs/dirctrl.c +++ b/drivers/filesystems/btrfs/dirctrl.c @@ -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; diff --git a/drivers/filesystems/btrfs/fastio.c b/drivers/filesystems/btrfs/fastio.c index 79604b469ad..5c2b8aaaf8b 100644 --- a/drivers/filesystems/btrfs/fastio.c +++ b/drivers/filesystems/btrfs/fastio.c @@ -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) diff --git a/drivers/filesystems/btrfs/fileinfo.c b/drivers/filesystems/btrfs/fileinfo.c index 445f4948e59..206a5529deb 100644 --- a/drivers/filesystems/btrfs/fileinfo.c +++ b/drivers/filesystems/btrfs/fileinfo.c @@ -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; + fcb* fcb = 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; diff --git a/drivers/filesystems/btrfs/flushthread.c b/drivers/filesystems/btrfs/flushthread.c index ef847a59cca..456c8541370 100644 --- a/drivers/filesystems/btrfs/flushthread.c +++ b/drivers/filesystems/btrfs/flushthread.c @@ -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); } diff --git a/drivers/filesystems/btrfs/fsctl.c b/drivers/filesystems/btrfs/fsctl.c index 039e828e962..52698383d3f 100644 --- a/drivers/filesystems/btrfs/fsctl.c +++ b/drivers/filesystems/btrfs/fsctl.c @@ -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; } +static NTSTATUS fsctl_oplock(device_extension* Vcb, PIRP* Pirp) { + NTSTATUS Status; + PIRP Irp = *Pirp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + 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; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + 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 + +#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 + +#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; + } + + ExAcquireResourceSharedLite(&Vcb->tree_lock, true); + + 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); + + 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); + +#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; + } + + 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: #if (NTDDI_VERSION >= NTDDI_WIN7) case FSCTL_REQUEST_OPLOCK: - WARN("STUB: FSCTL_REQUEST_OPLOCK\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; #endif - - case FSCTL_REQUEST_OPLOCK_LEVEL_1: - WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_REQUEST_OPLOCK_LEVEL_2: - WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_REQUEST_BATCH_OPLOCK: - WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: - WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_OPLOCK_BREAK_ACK_NO_2: - WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_OPBATCH_ACK_CLOSE_PENDING: - WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_OPLOCK_BREAK_NOTIFY: - WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - case FSCTL_REQUEST_FILTER_OPLOCK: - WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; + 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: diff --git a/drivers/filesystems/btrfs/fsrtl.c b/drivers/filesystems/btrfs/fsrtl.c index 8654d84e5ae..fab85bc6572 100644 --- a/drivers/filesystems/btrfs/fsrtl.c +++ b/drivers/filesystems/btrfs/fsrtl.c @@ -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; diff --git a/drivers/filesystems/btrfs/pnp.c b/drivers/filesystems/btrfs/pnp.c index 2b012ed002c..3313d82831b 100644 --- a/drivers/filesystems/btrfs/pnp.c +++ b/drivers/filesystems/btrfs/pnp.c @@ -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; diff --git a/drivers/filesystems/btrfs/read.c b/drivers/filesystems/btrfs/read.c index 5934821beb2..ae7a270bb18 100644 --- a/drivers/filesystems/btrfs/read.c +++ b/drivers/filesystems/btrfs/read.c @@ -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 diff --git a/drivers/filesystems/btrfs/registry.c b/drivers/filesystems/btrfs/registry.c index a13baf35aba..398d700b637 100644 --- a/drivers/filesystems/btrfs/registry.c +++ b/drivers/filesystems/btrfs/registry.c @@ -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)); diff --git a/drivers/filesystems/btrfs/reparse.c b/drivers/filesystems/btrfs/reparse.c index fb6a98808b8..a926bbd4a73 100644 --- a/drivers/filesystems/btrfs/reparse.c +++ b/drivers/filesystems/btrfs/reparse.c @@ -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)) diff --git a/drivers/filesystems/btrfs/scrub.c b/drivers/filesystems/btrfs/scrub.c index d1994b7fe63..e4d70d02e5e 100644 --- a/drivers/filesystems/btrfs/scrub.c +++ b/drivers/filesystems/btrfs/scrub.c @@ -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; diff --git a/drivers/filesystems/btrfs/search.c b/drivers/filesystems/btrfs/search.c index a1a4217aec9..3d824d9f767 100644 --- a/drivers/filesystems/btrfs/search.c +++ b/drivers/filesystems/btrfs/search.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/drivers/filesystems/btrfs/security.c b/drivers/filesystems/btrfs/security.c index 42ff987723a..bf5cff0fdfc 100644 --- a/drivers/filesystems/btrfs/security.c +++ b/drivers/filesystems/btrfs/security.c @@ -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); diff --git a/drivers/filesystems/btrfs/send.c b/drivers/filesystems/btrfs/send.c index 4737be0a85a..2090131dc99 100644 --- a/drivers/filesystems/btrfs/send.c +++ b/drivers/filesystems/btrfs/send.c @@ -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; diff --git a/drivers/filesystems/btrfs/volume.c b/drivers/filesystems/btrfs/volume.c index d50852b5952..0718bb8d9de 100644 --- a/drivers/filesystems/btrfs/volume.c +++ b/drivers/filesystems/btrfs/volume.c @@ -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,20 +1021,118 @@ end: } typedef struct { - PIO_WORKITEM work_item; - pdo_device_extension* pdode; -} drive_letter_callback_context; + LIST_ENTRY list_entry; + UNICODE_STRING name; + NTSTATUS Status; + BTRFS_UUID uuid; +} drive_letter_removal; + +static void drive_letter_callback2(pdo_device_extension* pdode, PDEVICE_OBJECT mountmgr) { + LIST_ENTRY* le; + LIST_ENTRY dlrlist; + + InitializeListHead(&dlrlist); + + ExAcquireResourceExclusiveLite(&pdode->child_lock, true); + + le = pdode->children.Flink; + + while (le != &pdode->children) { + drive_letter_removal* dlr; + + volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); + + dlr = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_removal), ALLOC_TAG); + if (!dlr) { + ERR("out of memory\n"); + + while (!IsListEmpty(&dlrlist)) { + dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); + + ExFreePool(dlr->name.Buffer); + ExFreePool(dlr); + } + + ExReleaseResourceLite(&pdode->child_lock); + return; + } + + dlr->name.Length = dlr->name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR)); + dlr->name.Buffer = ExAllocatePoolWithTag(PagedPool, dlr->name.Length, ALLOC_TAG); + + if (!dlr->name.Buffer) { + ERR("out of memory\n"); + + ExFreePool(dlr); + + while (!IsListEmpty(&dlrlist)) { + dlr = CONTAINING_RECORD(RemoveHeadList(&dlrlist), drive_letter_removal, list_entry); + + ExFreePool(dlr->name.Buffer); + ExFreePool(dlr); + } + + ExReleaseResourceLite(&pdode->child_lock); + 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(&pdode->child_lock); + + le = dlrlist.Flink; + while (le != &dlrlist) { + drive_letter_removal* dlr = CONTAINING_RECORD(le, drive_letter_removal, list_entry); + + dlr->Status = remove_drive_letter(mountmgr, &dlr->name); + + if (!NT_SUCCESS(dlr->Status) && dlr->Status != STATUS_NOT_FOUND) + WARN("remove_drive_letter returned %08x\n", dlr->Status); + + le = le->Flink; + } + + // 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(PDEVICE_OBJECT DeviceObject, PVOID con) { - drive_letter_callback_context* context = con; +static void __stdcall drive_letter_callback(pdo_device_extension* pdode) { NTSTATUS Status; UNICODE_STRING mmdevpath; PDEVICE_OBJECT mountmgr; PFILE_OBJECT mountmgrfo; - LIST_ENTRY* le; - - UNUSED(DeviceObject); RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr); @@ -1020,86 +1141,9 @@ static void __stdcall drive_letter_callback(PDEVICE_OBJECT DeviceObject, PVOID c 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); - - if (pdode == context->pdode) { - LIST_ENTRY* le2; - - ExAcquireResourceExclusiveLite(&pdode->child_lock, true); - - le2 = pdode->children.Flink; - - while (le2 != &pdode->children) { - UNICODE_STRING name; - - volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); - - name.Length = name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR)); - name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG); - - if (!name.Buffer) { - ERR("out of memory\n"); - - ExReleaseResourceLite(&pdode->child_lock); - ExReleaseResourceLite(&pdo_list_lock); - ObDereferenceObject(mountmgrfo); - IoFreeWorkItem(context->work_item); - return; - } - - RtlCopyMemory(name.Buffer, L"\\??", 3 * sizeof(WCHAR)); - RtlCopyMemory(&name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length); - - Status = remove_drive_letter(mountmgr, &name); - - if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) - WARN("remove_drive_letter returned %08x\n", Status); - - ExFreePool(name.Buffer); - - vc->had_drive_letter = NT_SUCCESS(Status); - - le2 = le2->Flink; - } - - ExReleaseResourceLite(&pdode->child_lock); - ExReleaseResourceLite(&pdo_list_lock); - ObDereferenceObject(mountmgrfo); - IoFreeWorkItem(context->work_item); - return; - } - - le = le->Flink; - } - - ExReleaseResourceLite(&pdo_list_lock); + drive_letter_callback2(pdode, mountmgr); ObDereferenceObject(mountmgrfo); - IoFreeWorkItem(context->work_item); -} - -static void add_drive_letter_work_item(pdo_device_extension* pdode) { - PIO_WORKITEM work_item; - drive_letter_callback_context* context; - - work_item = IoAllocateWorkItem(master_devobj); - - context = ExAllocatePoolWithTag(PagedPool, sizeof(drive_letter_callback_context), ALLOC_TAG); - - if (!context) { - ERR("out of memory\n"); - IoFreeWorkItem(work_item); - return; - } - - context->work_item = work_item; - context->pdode = pdode; - - IoQueueWorkItem(work_item, drive_letter_callback, DelayedWorkQueue, context); } 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); diff --git a/drivers/filesystems/btrfs/write.c b/drivers/filesystems/btrfs/write.c index c982e939b17..96ae287e02c 100644 --- a/drivers/filesystems/btrfs/write.c +++ b/drivers/filesystems/btrfs/write.c @@ -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) diff --git a/media/doc/README.FSD b/media/doc/README.FSD index 534e264b010..9ce6e3a7d29 100644 --- a/media/doc/README.FSD +++ b/media/doc/README.FSD @@ -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