Index: reactos/boot/bootdata/hivesys.inf
===================================================================
--- reactos/boot/bootdata/hivesys.inf (revision 70774)
+++ reactos/boot/bootdata/hivesys.inf (working copy)
@@ -1979,6 +2115,13 @@
HKLM,"SYSTEM\CurrentControlSet\Services\Ext2fs\Parameters","Ext3ForceWriting",0x00010001,0x00000001
HKLM,"SYSTEM\CurrentControlSet\Services\Ext2fs\Parameters","AutoMount",0x00010001,0x00000001
+; BTRFS Filesystem driver
+HKLM,"SYSTEM\CurrentControlSet\Services\btrfs","ErrorControl",0x00010001,0x00000000
+HKLM,"SYSTEM\CurrentControlSet\Services\btrfs","Group",0x00000000,"Boot File System"
+HKLM,"SYSTEM\CurrentControlSet\Services\btrfs","ImagePath",0x00020000,"system32\drivers\btrfs.sys"
+HKLM,"SYSTEM\CurrentControlSet\Services\btrfs","Start",0x00010001,0x00000003
+HKLM,"SYSTEM\CurrentControlSet\Services\btrfs","Type",0x00010001,0x00000002
+
; Filesystem Filter Manager driver
HKLM,"SYSTEM\CurrentControlSet\Services\rosfltmgr","ErrorControl",0x00010001,0x00000003
HKLM,"SYSTEM\CurrentControlSet\Services\rosfltmgr","Group",0x00000000,"Boot File System"
Index: reactos/boot/bootdata/txtsetup.sif
===================================================================
--- reactos/boot/bootdata/txtsetup.sif (revision 70774)
+++ reactos/boot/bootdata/txtsetup.sif (working copy)
@@ -67,6 +67,7 @@
wmilib.sys=,,,,,,,,,,,,4
ksecdd.sys=,,,,,,,,,,,,4
mountmgr.sys=,,,,,,x,,,,,,4
+btrfs.sys=,,,,,,x,,,,,,4
[SystemPartitionFiles]
Index: reactos/drivers/filesystems/btrfs/CMakeLists.txt
===================================================================
--- reactos/drivers/filesystems/btrfs/CMakeLists.txt (revision 0)
+++ reactos/drivers/filesystems/btrfs/CMakeLists.txt (working copy)
@@ -0,0 +1,40 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/include/reactos/drivers
+ inc)
+
+list(APPEND SOURCE
+ btrfs.c
+ cache.c
+ crc32c.c
+ create.c
+ dirctrl.c
+ fastio.c
+ fileinfo.c
+ flushthread.c
+ fsctl.c
+ reparse.c
+ search.c
+ security.c
+ treefuncs.c
+ write.c
+ btrfs_drv.h)
+
+add_library(btrfs SHARED ${SOURCE} btrfs.rc)
+
+if(NOT MSVC)
+# add_target_compile_flags(btrfs "-Wno-pointer-sign -Wno-unused-function")
+ replace_compile_flags("-Werror" " ")
+# if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+# add_target_compile_flags(btrfs "-Wno-unused-but-set-variable -Wno-unused-variable -Wno-missing-braces")
+# endif()
+else()
+ #disable warnings: "unreferenced local variable", "initialized, but not used variable", "benign include"
+ replace_compile_flags("/we\"4189\"" " ")
+# add_target_compile_flags(btrfs "/wd\"4189\" /wd\"4142\" /wd\"4101\"")
+endif()
+
+add_definitions(-D__KERNEL__)
+set_module_type(btrfs kernelmodedriver)
+add_importlibs(btrfs ntoskrnl hal)
+add_pch(btrfs btrfs_drv.h SOURCE)
+add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)
Index: reactos/drivers/filesystems/btrfs/btrfs.c
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.c (working copy)
@@ -0,0 +1,4485 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifdef _DEBUG
+#define DEBUG
+#endif
+
+#include "btrfs_drv.h"
+#ifndef __REACTOS__
+#ifndef _MSC_VER
+#include
+#else
+#include
+#endif
+#endif
+#include "btrfs.h"
+#ifndef __REACTOS__
+#include
+#else
+#include
+#endif
+
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
+ BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+#define COMPAT_RO_SUPPORTED 0
+
+enum read_data_status {
+ ReadDataStatus_Pending,
+ ReadDataStatus_Success,
+ ReadDataStatus_Cancelling,
+ ReadDataStatus_Cancelled,
+ ReadDataStatus_Error,
+ ReadDataStatus_CRCError,
+ ReadDataStatus_MissingDevice
+};
+
+struct read_data_context;
+
+typedef struct {
+ struct read_data_context* context;
+ UINT8* buf;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum read_data_status status;
+} read_data_stripe;
+
+typedef struct {
+ KEVENT Event;
+ NTSTATUS Status;
+ chunk* c;
+ UINT32 buflen;
+ UINT64 num_stripes;
+ UINT64 type;
+ read_data_stripe* stripes;
+} read_data_context;
+
+static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
+static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
+
+PDRIVER_OBJECT drvobj;
+PDEVICE_OBJECT devobj;
+#ifndef __REACTOS__
+BOOL have_sse42 = FALSE;
+#endif
+UINT64 num_reads = 0;
+LIST_ENTRY uid_map_list;
+LIST_ENTRY volumes;
+
+#ifdef DEBUG
+PFILE_OBJECT comfo = NULL;
+PDEVICE_OBJECT comdo = NULL;
+#endif
+
+static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
+
+typedef struct {
+ KEVENT Event;
+ IO_STATUS_BLOCK iosb;
+} read_context;
+
+#ifdef DEBUG
+static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_context* context = conptr;
+
+// DbgPrint("dbg_completion\n");
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+#endif
+
+#ifdef DEBUG_LONG_MESSAGES
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
+#else
+void STDCALL _debug_message(const char* func, char* s, ...) {
+#endif
+#ifdef DEBUG
+ LARGE_INTEGER offset;
+// IO_STATUS_BLOCK io;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ PIRP Irp;
+ va_list ap;
+ char *buf2 = NULL, *buf;
+ read_context* context = NULL;
+ UINT32 length;
+
+ if (!comdo) {
+ DbgPrint("comdo is NULL :-(\n");
+ return;
+ }
+
+ buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
+// DbgPrint("buf2 = %p\n", buf2);
+
+ if (!buf2) {
+ DbgPrint("Couldn't allocate buffer in debug_message\n");
+ return;
+ }
+
+// strcpy(buf2, func);
+// strcat(buf2, ": ");
+#ifdef DEBUG_LONG_MESSAGES
+ sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+#else
+ sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+#endif
+ buf = &buf2[strlen(buf2)];
+
+ va_start(ap, s);
+ vsprintf(buf, s, ap);
+
+ length = (UINT32)strlen(buf2);
+
+ offset.u.LowPart = 0;
+ offset.u.HighPart = 0;
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
+// DbgPrint("context = %p\n", context);
+ RtlZeroMemory(context, sizeof(read_context));
+
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+// status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, comdo, buf2, (ULONG)strlen(buf2), &offset, &context->Event, &context->iosb);
+ Irp = IoAllocateIrp(comdo->StackSize, FALSE);
+
+ if (!Irp) {
+ DbgPrint("IoAllocateIrp failed\n");
+ goto exit2;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (comdo->Flags & DO_BUFFERED_IO) {
+ Irp->AssociatedIrp.SystemBuffer = buf2;
+
+ Irp->Flags = IRP_BUFFERED_IO;
+ } else if (comdo->Flags & DO_DIRECT_IO) {
+ Irp->MdlAddress = IoAllocateMdl(buf2, length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ DbgPrint("IoAllocateMdl failed\n");
+ goto exit;
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ Irp->UserBuffer = buf2;
+ }
+
+ IrpSp->Parameters.Write.Length = length;
+ IrpSp->Parameters.Write.ByteOffset = offset;
+
+ Irp->UserIosb = &context->iosb;
+
+ Irp->UserEvent = &context->Event;
+
+ IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
+
+ Status = IoCallDriver(comdo, Irp);
+
+// LARGE_INTEGER timeout;
+// timeout.QuadPart = -1000000; // 100ms
+
+ if (Status == STATUS_PENDING) {
+// KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, /*&timeout*/NULL);
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+ Status = context->iosb.Status;
+ }
+
+ if (comdo->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("failed to write to COM1 - error %08x\n", Status);
+ goto exit;
+// return;
+// } else {
+// DbgPrint("wrote %u bytes\n", io.Information);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+exit2:
+ va_end(ap);
+
+ if (context)
+ ExFreePool(context);
+
+ if (buf2)
+ ExFreePool(buf2);
+#else
+ va_list ap;
+ char *buf2 = NULL, *buf;
+
+ buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
+
+ if (!buf2) {
+ DbgPrint("Couldn't allocate buffer in debug_message\n");
+ return;
+ }
+
+#ifdef DEBUG_LONG_MESSAGES
+ sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+#else
+ sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+#endif
+ buf = &buf2[strlen(buf2)];
+
+ va_start(ap, s);
+ vsprintf(buf, s, ap);
+
+ DbgPrint("%s", buf2);
+
+ va_end(ap);
+
+ if (buf2)
+ ExFreePool(buf2);
+#endif
+}
+
+ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment )
+{
+ if( Alignment & ( Alignment - 1 ) )
+ {
+ //
+ // Alignment not a power of 2
+ // Just returning
+ //
+ return NumberToBeAligned;
+ }
+ if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
+ {
+ NumberToBeAligned = NumberToBeAligned + Alignment;
+ NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
+ }
+ return NumberToBeAligned;
+}
+
+int keycmp(const KEY* key1, const KEY* key2) {
+ if (key1->obj_id < key2->obj_id) {
+ return -1;
+ } else if (key1->obj_id > key2->obj_id) {
+ return 1;
+ }
+
+ if (key1->obj_type < key2->obj_type) {
+ return -1;
+ } else if (key1->obj_type > key2->obj_type) {
+ return 1;
+ }
+
+ if (key1->offset < key2->offset) {
+ return -1;
+ } else if (key1->offset > key2->offset) {
+ return 1;
+ }
+
+ return 0;
+}
+
+BOOL is_top_level(PIRP Irp) {
+ if (!IoGetTopLevelIrp()) {
+ IoSetTopLevelIrp(Irp);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
+ UNICODE_STRING dosdevice_nameW;
+
+ TRACE("DriverUnload\n");
+
+ free_cache();
+
+ IoUnregisterFileSystem(DriverObject->DeviceObject);
+
+ dosdevice_nameW.Buffer = dosdevice_name;
+ dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+
+ IoDeleteSymbolicLink(&dosdevice_nameW);
+ IoDeleteDevice(DriverObject->DeviceObject);
+
+ // FIXME - free volumes
+
+#ifdef DEBUG
+ if (comfo)
+ ObDereferenceObject(comfo);
+#endif
+}
+
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+ KEY searchkey;
+ traverse_ptr tp, prev_tp;
+
+ // get last entry
+ searchkey.obj_id = 0xffffffffffffffff;
+ searchkey.obj_type = 0xff;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ return FALSE;
+ }
+
+ while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = prev_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
+ r->lastinode = tp.item->key.obj_id;
+ free_traverse_ptr(&tp);
+ TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
+ return TRUE;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ r->lastinode = SUBVOL_ROOT_INODE;
+
+ WARN("no INODE_ITEMs in tree %llx\n", r->id);
+
+ return TRUE;
+}
+
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DIR_ITEM* xa;
+ ULONG size, xasize;
+
+ TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ xa = (DIR_ITEM*)tp.item->data;
+ size = tp.item->size;
+
+ while (TRUE) {
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+
+ *datalen = xa->m;
+
+ if (xa->m > 0) {
+ *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
+ RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
+ } else
+ *data = NULL;
+
+ free_traverse_ptr(&tp);
+ return TRUE;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (size > xasize) {
+ size -= xasize;
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ } else
+ break;
+ }
+
+ TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+}
+
+void STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen) {
+ KEY searchkey;
+ traverse_ptr tp;
+ ULONG xasize;
+ DIR_ITEM* xa;
+
+ TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
+
+ if (!keycmp(&tp.item->key, &searchkey)) { // key exists
+ UINT8* newdata;
+ ULONG size = tp.item->size;
+
+ xa = (DIR_ITEM*)tp.item->data;
+
+ while (TRUE) {
+ ULONG oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ UINT64 pos;
+
+ // replace
+ newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
+
+ pos = (UINT8*)xa - tp.item->data;
+ if (pos + oldxasize < tp.item->size) { // copy after changed xattr
+ RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize);
+ }
+
+ if (pos > 0) { // copy before changed xattr
+ RtlCopyMemory(newdata, tp.item->data, pos);
+ xa = (DIR_ITEM*)(newdata + pos);
+ } else
+ xa = (DIR_ITEM*)newdata;
+
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL);
+
+ break;
+ }
+
+ if (xa->m + xa->n >= size) { // FIXME - test this works
+ // not found, add to end of data
+ newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
+
+ RtlCopyMemory(newdata, tp.item->data, tp.item->size);
+
+ xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size);
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL);
+
+ break;
+ } else {
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ size -= oldxasize;
+ }
+ }
+ } else {
+ // add new DIR_ITEM struct
+
+ xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
+
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+}
+
+BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DIR_ITEM* xa;
+
+ TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return FALSE;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) { // key exists
+ ULONG size = tp.item->size;
+
+ xa = (DIR_ITEM*)tp.item->data;
+
+ while (TRUE) {
+ ULONG oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ ULONG newsize;
+ UINT8 *newdata, *dioff;
+
+ newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newsize == 0) {
+ TRACE("xattr %s deleted\n", name);
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+ }
+
+ // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
+ newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
+
+ if ((UINT8*)xa > tp.item->data) {
+ RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data);
+ dioff = newdata + ((UINT8*)xa - tp.item->data);
+ } else {
+ dioff = newdata;
+ }
+
+ if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
+ RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL);
+
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+ }
+
+ if (xa->m + xa->n >= size) { // FIXME - test this works
+ WARN("xattr %s not found\n", name);
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+ } else {
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ size -= oldxasize;
+ }
+ }
+ } else {
+ WARN("xattr %s not found\n", name);
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+ }
+}
+
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize) {
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT8* di2;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (tp.item->size + disize > maxlen) {
+ WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
+ RtlCopyMemory(di2, tp.item->data, tp.item->size);
+ RtlCopyMemory(di2 + tp.item->size, di, disize);
+
+ delete_tree_item(Vcb, &tp);
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL);
+
+ ExFreePool(di);
+ } else {
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode) {
+ KEY searchkey;
+ traverse_ptr tp, prev_tp;
+ UINT64 dirpos;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return 0;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = prev_tp;
+
+ TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ dirpos = tp.item->key.offset + 1;
+ } else
+ dirpos = 2;
+
+ free_traverse_ptr(&tp);
+
+ return dirpos;
+}
+
+static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ BOOL top_level;
+
+ TRACE("close\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (DeviceObject == devobj) {
+ TRACE("Closing file system\n");
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ // FIXME - unmount if called for volume
+ // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
+
+ Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
+
+exit:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG length, ULONG* pbr) {
+ UINT8* xattrdata;
+ UINT16 xattrlen;
+ ULONG readlen;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
+
+ if (pbr) *pbr = 0;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &xattrdata, &xattrlen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (start >= xattrlen) {
+ TRACE("tried to read beyond end of stream\n");
+ Status = STATUS_END_OF_FILE;
+ goto end;
+ }
+
+ if (length == 0) {
+ WARN("tried to read zero bytes\n");
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+
+ if (start + length < xattrlen)
+ readlen = length;
+ else
+ readlen = (ULONG)xattrlen - (ULONG)start;
+
+ RtlCopyMemory(data + start, xattrdata, readlen);
+
+ if (pbr) *pbr = readlen;
+
+ Status = STATUS_SUCCESS;
+
+end:
+ ExFreePool(xattrdata);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_data_stripe* stripe = conptr;
+ read_data_context* context = (read_data_context*)stripe->context;
+ UINT64 i;
+ BOOL complete;
+
+ if (stripe->status == ReadDataStatus_Cancelling) {
+ stripe->status = ReadDataStatus_Cancelled;
+ goto end;
+ }
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ // FIXME - calculate and compare checksum
+
+ stripe->status = ReadDataStatus_Success;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Pending) {
+ context->stripes[i].status = ReadDataStatus_Cancelling;
+ IoCancelIrp(context->stripes[i].Irp);
+ }
+ }
+
+ goto end;
+ } else {
+ stripe->status = ReadDataStatus_Error;
+ }
+
+end:
+ complete = TRUE;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Pending || context->stripes[i].status == ReadDataStatus_Cancelling) {
+ complete = FALSE;
+ break;
+ }
+ }
+
+ if (complete)
+ KeSetEvent(&context->Event, 0, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT8* buf) {
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ read_data_context* context;
+ UINT64 i/*, type*/, offset;
+ NTSTATUS Status;
+ device** devices;
+
+ // FIXME - make this work with RAID
+
+ if (Vcb->log_to_phys_loaded) {
+ chunk* c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ci = c->chunk_item;
+ offset = c->offset;
+ devices = c->devices;
+ }
+
+// if (ci->type & BLOCK_FLAG_DUPLICATE) {
+// type = BLOCK_FLAG_DUPLICATE;
+// } else if (ci->type & BLOCK_FLAG_RAID0) {
+// FIXME("RAID0 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID1) {
+// FIXME("RAID1 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID10) {
+// FIXME("RAID10 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID5) {
+// FIXME("RAID5 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID6) {
+// FIXME("RAID6 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else { // SINGLE
+// type = 0;
+// }
+
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_data_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG);
+ RtlZeroMemory(context->stripes, sizeof(read_data_stripe) * ci->num_stripes);
+
+ context->buflen = length;
+ context->num_stripes = ci->num_stripes;
+// context->type = type;
+
+ // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ if (!devices[i]) {
+ context->stripes[i].status = ReadDataStatus_MissingDevice;
+ context->stripes[i].buf = NULL;
+ } else {
+ context->stripes[i].context = (struct read_data_context*)context;
+ context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+
+ context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+
+ if (!context->stripes[i].Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, length, FALSE, FALSE, NULL);
+ if (!context->stripes[i].Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
+ }
+
+ IrpSp->Parameters.Read.Length = length;
+ IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
+
+ context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+
+ IoSetCompletionRoutine(context->stripes[i].Irp, read_data_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+
+ context->stripes[i].status = ReadDataStatus_Pending;
+ }
+ }
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status != ReadDataStatus_MissingDevice) {
+ IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
+ }
+ }
+
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+
+ // FIXME - if checksum error, write good data over bad
+
+ // check if any of the stripes succeeded
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Success) {
+ RtlCopyMemory(buf, context->stripes[i].buf, length);
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+ }
+
+ // if not, see if we got a checksum error
+
+// for (i = 0; i < ci->num_stripes; i++) {
+// if (context->stripes[i].status == ReadDataStatus_CRCError) {
+// WARN("stripe %llu had a checksum error\n", i);
+//
+// Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+// goto exit;
+// }
+// }
+
+ // failing that, return the first error we encountered
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Error) {
+ Status = context->stripes[i].iosb.Status;
+ goto exit;
+ }
+ }
+
+ // if we somehow get here, return STATUS_INTERNAL_ERROR
+
+ Status = STATUS_INTERNAL_ERROR;
+
+exit:
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].Irp) {
+ if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(context->stripes[i].Irp->MdlAddress);
+ IoFreeMdl(context->stripes[i].Irp->MdlAddress);
+ }
+ IoFreeIrp(context->stripes[i].Irp);
+ }
+
+ if (context->stripes[i].buf)
+ ExFreePool(context->stripes[i].buf);
+ }
+
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ return Status;
+}
+
+NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr) {
+ KEY searchkey;
+ NTSTATUS Status;
+ traverse_ptr tp, next_tp;
+ EXTENT_DATA* ed;
+ UINT64 bytes_read = 0;
+
+ TRACE("(%p, %llx, %llx, %p, %llx, %llx, %p)\n", Vcb, subvol->id, inode, data, start, length, pbr);
+
+ if (pbr)
+ *pbr = 0;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("couldn't find any entries in subvol %llx\n", subvol->id);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+ if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ free_traverse_ptr(&tp);
+ ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+ if (tp.item->key.offset > start) {
+ ERR("first EXTENT_DATA was after offset\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+// while (TRUE) {
+// BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//
+// if (!foundnext || next_tp.item->key.obj_id != inode ||
+// next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset > start) {
+// if (foundnext)
+// free_traverse_ptr(&next_tp);
+//
+// break;
+// }
+//
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+
+ do {
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (tp.item->key.offset + ed->decoded_size < start) {
+ ERR("Tried to read beyond end of file\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_END_OF_FILE;
+ goto exit;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME - compression not yet supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("Encryption not supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("Other encodings not supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ switch (ed->type) {
+ case EXTENT_TYPE_INLINE:
+ {
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT64 read;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ RtlCopyMemory(data + bytes_read, &ed->data[off], read);
+
+ bytes_read += read;
+ length -= read;
+ break;
+ }
+
+ case EXTENT_TYPE_REGULAR:
+ {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT32 to_read, read;
+ UINT8* buf;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ if (ed2->address == 0) {
+ RtlZeroMemory(data + bytes_read, read);
+ } else {
+ to_read = sector_align(read, Vcb->superblock.sector_size);
+
+ buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+
+ // FIXME - load checksums
+
+ Status = read_data(Vcb, ed2->address + ed2->offset + off, to_read, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_data returned %08x\n", Status);
+ ExFreePool(buf);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ RtlCopyMemory(data + bytes_read, buf, read);
+
+ ExFreePool(buf);
+ }
+
+ bytes_read += read;
+ length -= read;
+
+ break;
+ }
+
+ case EXTENT_TYPE_PREALLOC:
+ {
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT32 read;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ RtlZeroMemory(data + bytes_read, read);
+
+ bytes_read += read;
+ length -= read;
+
+ break;
+ }
+
+ default:
+ WARN("Unsupported extent data type %u\n", ed->type);
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (length > 0) {
+ BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (!foundnext)
+ break;
+ else if (next_tp.item->key.obj_id != inode ||
+ next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
+ next_tp.item->key.offset != tp.item->key.offset + ed->decoded_size
+ ) {
+ free_traverse_ptr(&next_tp);
+ break;
+ } else {
+ TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset);
+
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } else
+ break;
+ } while (TRUE);
+
+ free_traverse_ptr(&tp);
+
+ Status = STATUS_SUCCESS;
+ if (pbr)
+ *pbr = bytes_read;
+
+exit:
+ return Status;
+}
+
+// static NTSTATUS STDCALL read_file(PFILE_OBJECT FileObject, PIRP Irp) {
+static NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ UINT8* data;
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ UINT64 start;
+ ULONG length, bytes_read;
+ NTSTATUS Status;
+ BOOL top_level;
+
+// __asm__("int3");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ TRACE("read\n");
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_COMPLETE:
+ FIXME("unsupported - IRP_MN_COMPLETE\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL_DPC:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
+ break;
+
+ case IRP_MN_COMPRESSED:
+ FIXME("unsupported - IRP_MN_COMPRESSED\n");
+ break;
+
+ case IRP_MN_DPC:
+ FIXME("unsupported - IRP_MN_DPC\n");
+ break;
+
+ case IRP_MN_MDL:
+ FIXME("unsupported - IRP_MN_MDL\n");
+ break;
+
+ case IRP_MN_MDL_DPC:
+ FIXME("unsupported - IRP_MN_MDL_DPC\n");
+ break;
+
+ case IRP_MN_NORMAL:
+ TRACE("IRP_MN_NORMAL\n");
+ break;
+
+ default:
+ WARN("unknown minor %u\n", IrpSp->MinorFunction);
+ }
+
+ data = map_user_buffer(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ start = IrpSp->Parameters.Read.ByteOffset.QuadPart;
+ length = IrpSp->Parameters.Read.Length;
+ bytes_read = 0;
+
+ if (!fcb || !fcb->Vcb || !fcb->subvol) {
+ Status = STATUS_INTERNAL_ERROR; // FIXME - invalid param error?
+ goto exit;
+ }
+
+ TRACE("file = %.*S (fcb = %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+ TRACE("offset = %llx, 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");
+
+ // FIXME - shouldn't be able to read from a directory
+
+ if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) {
+ WARN("tried to read locked region\n");
+ Status = STATUS_FILE_LOCK_CONFLICT;
+ goto exit;
+ }
+
+ if (length == 0) {
+ WARN("tried to read zero bytes\n");
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (start >= fcb->Header.ValidDataLength.QuadPart) {
+ WARN("tried to read with offset after file end (%llx >= %llx)\n", start, fcb->Header.ValidDataLength.QuadPart);
+ Status = STATUS_END_OF_FILE;
+ goto exit;
+ }
+
+ fcb->Header.FileSize.QuadPart = fcb->ads ? fcb->adssize : fcb->inode_item.st_size;
+
+ TRACE("FileObject %p fcb %p FileSize = %llx st_size = %llx (%p)\n", FileObject, fcb, fcb->Header.FileSize.QuadPart, fcb->inode_item.st_size, &fcb->inode_item.st_size);
+// int3;
+
+ if (length + start > fcb->Header.ValidDataLength.QuadPart)
+ length = fcb->Header.ValidDataLength.QuadPart - start;
+
+ if (!(Irp->Flags & IRP_NOCACHE)) {
+ BOOL wait;
+
+ Status = STATUS_SUCCESS;
+
+// try {
+ if (!FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ ERR("calling CcInitializeCacheMap (%llx, %llx, %llx)\n",
+ ccfs.AllocationSize.QuadPart, ccfs.FileSize.QuadPart, ccfs.ValidDataLength.QuadPart);
+ CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+
+ CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+ }
+
+ // FIXME - uncomment this when async is working
+ // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
+ wait = TRUE;
+
+ ERR("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus);
+ if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) {
+ TRACE("CcCopyRead failed\n");
+
+ IoMarkIrpPending(Irp);
+ Status = STATUS_PENDING;
+ goto exit;
+ }
+ ERR("CcCopyRead finished\n");
+// } except (EXCEPTION_EXECUTE_HANDLER) {
+// Status = GetExceptionCode();
+// }
+
+ if (NT_SUCCESS(Status)) {
+ Status = Irp->IoStatus.Status;
+ bytes_read = Irp->IoStatus.Information;
+ } else
+ ERR("EXCEPTION - %08x\n", Status);
+ } else {
+ if (fcb->ads)
+ Status = read_stream(fcb, data, start, length, &bytes_read);
+ else
+ Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, start, length, &bytes_read);
+
+ TRACE("read %u bytes\n", bytes_read);
+
+ Irp->IoStatus.Information = bytes_read;
+ }
+
+ if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
+ FileObject->CurrentByteOffset.QuadPart = start + bytes_read;
+
+exit:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ Irp->IoStatus.Status = Status;
+
+ TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
+ TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
+ TRACE("returning %08x\n", Status);
+
+ if (Status != STATUS_PENDING)
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
+
+ Status = write_file(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = Status;
+
+ TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
+
+ if (Status != STATUS_PENDING)
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ FIXME("STUB: query ea\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ FIXME("STUB: set ea\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb->readonly)
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+
+ // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ BOOL top_level;
+
+ TRACE("flush buffers\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
+
+ if (fcb->Header.PagingIoResource) {
+ ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
+ ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ }
+
+ Status = Irp->IoStatus.Status;
+ }
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ ULONG BytesCopied = 0;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ WCHAR* fs_name = L"Btrfs";
+ ULONG fs_name_len = 5 * sizeof(WCHAR);
+
+ TRACE("query volume information\n");
+
+ FsRtlEnterFileSystem();
+ top_level = is_top_level(Irp);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
+ case FileFsAttributeInformation:
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+ BOOL overflow = FALSE;
+ ULONG orig_fs_name_len = fs_name_len;
+
+ TRACE("FileFsAttributeInformation\n");
+
+ if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
+ if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
+ fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
+ else
+ fs_name_len = 0;
+
+ overflow = TRUE;
+ }
+
+ data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
+ FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
+ FILE_SUPPORTS_REPARSE_POINTS;
+ if (Vcb->readonly)
+ data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
+
+ // should also be FILE_FILE_COMPRESSION when supported
+ data->MaximumComponentNameLength = 255; // FIXME - check
+ data->FileSystemNameLength = orig_fs_name_len * sizeof(WCHAR);
+ RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
+
+ BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
+ Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+ break;
+ }
+
+ case FileFsControlInformation:
+ FIXME("STUB: FileFsControlInformation\n");
+ break;
+
+ case FileFsDeviceInformation:
+ FIXME("STUB: FileFsDeviceInformation\n");
+ break;
+
+ case FileFsDriverPathInformation:
+ FIXME("STUB: FileFsDriverPathInformation\n");
+ break;
+
+ case FileFsFullSizeInformation:
+ {
+ FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
+ UINT64 totalsize, freespace;
+
+ TRACE("FileFsFullSizeInformation\n");
+
+ // FIXME - calculate correctly for RAID
+ totalsize = Vcb->superblock.total_bytes / Vcb->superblock.sector_size;
+ freespace = (Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size;
+
+ ffsi->TotalAllocationUnits.QuadPart = totalsize;
+ ffsi->ActualAvailableAllocationUnits.QuadPart = freespace;
+ ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
+ ffsi->SectorsPerAllocationUnit = 1;
+ ffsi->BytesPerSector = Vcb->superblock.sector_size;
+
+ BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileFsObjectIdInformation:
+ FIXME("STUB: FileFsObjectIdInformation\n");
+ break;
+
+ case FileFsSizeInformation:
+ {
+ FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
+ UINT64 totalsize, freespace;
+
+ TRACE("FileFsSizeInformation\n");
+
+ // FIXME - calculate correctly for RAID
+ // FIXME - is this returning the right free space?
+ totalsize = Vcb->superblock.dev_item.num_bytes / Vcb->superblock.sector_size;
+ freespace = (Vcb->superblock.dev_item.num_bytes - Vcb->superblock.dev_item.bytes_used) / Vcb->superblock.sector_size;
+
+ ffsi->TotalAllocationUnits.QuadPart = totalsize;
+ ffsi->AvailableAllocationUnits.QuadPart = freespace;
+ ffsi->SectorsPerAllocationUnit = 1;
+ ffsi->BytesPerSector = Vcb->superblock.sector_size;
+
+ BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileFsVolumeInformation:
+ {
+ FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+ FILE_FS_VOLUME_INFORMATION ffvi;
+ BOOL overflow = FALSE;
+ ULONG label_len, orig_label_len;
+
+ TRACE("FileFsVolumeInformation\n");
+ TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
+
+ acquire_tree_lock(Vcb, FALSE);
+
+// orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
+ RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ orig_label_len = label_len;
+
+ if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
+ if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
+ label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
+ else
+ label_len = 0;
+
+ overflow = TRUE;
+ }
+
+ TRACE("label_len = %u\n", label_len);
+
+ ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
+ ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
+ ffvi.VolumeLabelLength = orig_label_len;
+ ffvi.SupportsObjects = FALSE;
+
+ RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
+
+ if (label_len > 0) {
+ ULONG bytecount;
+
+// RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
+ RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
+ }
+
+ release_tree_lock(Vcb, FALSE);
+
+ BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
+ Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+ break;
+ }
+
+ default:
+ Status = STATUS_INVALID_PARAMETER;
+ WARN("unknown FsInformatClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
+ break;
+ }
+
+// if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
+// WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
+// BytesCopied = IrpSp->Parameters.QueryVolume.Length;
+// Status = STATUS_BUFFER_OVERFLOW;
+// }
+
+ Irp->IoStatus.Status = Status;
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+ Irp->IoStatus.Information = 0;
+ else
+ Irp->IoStatus.Information = BytesCopied;
+
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("query volume information returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_context* context = conptr;
+
+// DbgPrint("read_completion\n");
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+// static void test_tree_deletion(device_extension* Vcb) {
+// KEY searchkey/*, endkey*/;
+// traverse_ptr tp, next_tp;
+// root* r;
+//
+// searchkey.obj_id = 0x100;
+// searchkey.obj_type = 0x54;
+// searchkey.offset = 0xca4ab2f5;
+//
+// // endkey.obj_id = 0x100;
+// // endkey.obj_type = 0x60;
+// // endkey.offset = 0x15a;
+//
+// r = Vcb->roots;
+// while (r && r->id != 0x102)
+// r = r->next;
+//
+// if (!r) {
+// ERR("error - could not find root\n");
+// return;
+// }
+//
+// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
+// ERR("error - could not find key\n");
+// return;
+// }
+//
+// while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
+// tp.item->ignore = TRUE;
+// add_to_tree_cache(tc, tp.tree);
+//
+// if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// } else
+// break;
+// }
+//
+// free_traverse_ptr(&tp);
+// }
+
+// static void test_tree_splitting(device_extension* Vcb) {
+// int i;
+//
+// for (i = 0; i < 1000; i++) {
+// char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
+//
+// insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
+// }
+// }
+
+static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
+ ULONG utf8len;
+ NTSTATUS Status;
+
+ TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (utf8len > MAX_LABEL_SIZE) {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
+ }
+
+ // FIXME - check for '/' and '\\' and reject
+
+ acquire_tree_lock(Vcb, TRUE);
+
+// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
+ if (!NT_SUCCESS(Status))
+ goto release;
+
+ if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
+ RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
+
+// test_tree_deletion(Vcb); // TESTING
+// test_tree_splitting(Vcb);
+
+ Status = consider_write(Vcb);
+
+release:
+ release_tree_lock(Vcb, TRUE);
+
+end:
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ BOOL top_level;
+
+ TRACE("set volume information\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
+ }
+
+ switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
+ case FileFsControlInformation:
+ FIXME("STUB: FileFsControlInformation\n");
+ break;
+
+ case FileFsLabelInformation:
+ TRACE("FileFsLabelInformation\n");
+
+ Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
+ break;
+
+ case FileFsObjectIdInformation:
+ FIXME("STUB: FileFsObjectIdInformation\n");
+ break;
+
+ default:
+ WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
+ break;
+ }
+
+end:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ DIR_ITEM* di;
+ LONG len;
+
+ di = (DIR_ITEM*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
+ ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)di > tp.item->data) {
+ RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
+ dioff = newdi + ((UINT8*)di - tp.item->data);
+ } else {
+ dioff = newdi;
+ }
+
+ if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
+ RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL);
+ }
+
+ break;
+ }
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
+ di = (DIR_ITEM*)&di->name[di->n + di->m];
+ } while (len > 0);
+ } else {
+ WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index) {
+ KEY searchkey;
+ traverse_ptr tp;
+ BOOL changed = FALSE;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = parinode;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ INODE_REF* ir;
+ ULONG len;
+
+ ir = (INODE_REF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
+
+ if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+ changed = TRUE;
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)ir > tp.item->data) {
+ RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
+ iroff = newir + ((UINT8*)ir - tp.item->data);
+ } else {
+ iroff = newir;
+ }
+
+ if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL);
+ }
+
+ if (index)
+ *index = ir->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ ir = (INODE_REF*)&ir->name[ir->n];
+ } else
+ break;
+ } while (len > 0);
+
+ if (!changed) {
+ WARN("found INODE_REF entry, but couldn't find filename\n");
+ }
+ } else {
+ WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (changed)
+ return STATUS_SUCCESS;
+
+ if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
+ return STATUS_INTERNAL_ERROR;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ INODE_EXTREF* ier;
+ ULONG len;
+
+ ier = (INODE_EXTREF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
+
+ if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+ changed = TRUE;
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)ier > tp.item->data) {
+ RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
+ ieroff = newier + ((UINT8*)ier - tp.item->data);
+ } else {
+ ieroff = newier;
+ }
+
+ if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL);
+ }
+
+ if (index)
+ *index = ier->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ } else
+ break;
+ } while (len > 0);
+ } else {
+ WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
+}
+
+void delete_fcb(fcb* fcb, PFILE_OBJECT FileObject) {
+ ULONG bytecount;
+ NTSTATUS Status;
+ char* utf8 = NULL;
+ UINT32 crc32;
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ UINT64 parinode, index;
+ INODE_ITEM *ii, *dirii;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ LIST_ENTRY changed_sector_list;
+ LARGE_INTEGER freq, time1, time2;
+
+ // FIXME - throw error if try to delete subvol root(?)
+
+ // FIXME - delete all children if deleting directory
+
+ if (fcb->deleted) {
+ ERR("trying to delete already-deleted file\n");
+ return;
+ }
+
+ if (!fcb->par) {
+ ERR("error - trying to delete root FCB\n");
+ return;
+ }
+
+ time1 = KeQueryPerformanceCounter(&freq);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ if (fcb->ads) {
+ char* s;
+ TRACE("deleting ADS\n");
+
+ s = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.Length + 1, ALLOC_TAG);
+ RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+ s[fcb->adsxattr.Length] = 0;
+
+ if (!delete_xattr(fcb->Vcb, fcb->par->subvol, fcb->par->inode, s, fcb->adshash)) {
+ ERR("failed to delete xattr %s\n", s);
+ }
+
+ ExFreePool(s);
+
+ fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(fcb->Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ goto exit;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->par->inode, fcb->par->subvol->id);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(fcb->Vcb, &tp);
+
+ insert_tree_item(fcb->Vcb, fcb->par->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->par->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = now;
+
+ goto success;
+ }
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ return;
+ }
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, bytecount + 1, ALLOC_TAG);
+
+ RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ utf8[bytecount] = 0;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount);
+
+ ERR("deleting %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ if (fcb->par->subvol == fcb->subvol)
+ parinode = fcb->par->inode;
+ else
+ parinode = SUBVOL_ROOT_INODE;
+
+ // delete DIR_ITEM (0x54)
+
+ Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return;
+ }
+
+ // delete INODE_REF (0xc)
+
+ index = 0;
+
+ Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fcb->utf8, &index);
+
+ // delete DIR_INDEX (0x60)
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = index;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+
+ // delete INODE_ITEM (0x1)
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - INODE_ITEM not found\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ ii = (INODE_ITEM*)tp.item->data;
+ TRACE("nlink = %u\n", ii->st_nlink);
+
+ if (ii->st_nlink > 1) {
+ INODE_ITEM* newii;
+
+ newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
+ newii->st_nlink--;
+ newii->transid = fcb->Vcb->superblock.generation;
+ newii->sequence++;
+ newii->st_ctime = now;
+
+ TRACE("replacing (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ delete_tree_item(fcb->Vcb, &tp);
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newii, sizeof(INODE_ITEM), NULL))
+ ERR("error - failed to insert item\n");
+
+ free_traverse_ptr(&tp);
+
+ goto success2;
+ }
+
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ // delete XATTR_ITEM (0x18)
+
+ while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ if (tp.item->key.obj_id == fcb->inode) {
+ // FIXME - do metadata thing here too?
+ if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ } else
+ break;
+ }
+
+ free_traverse_ptr(&tp);
+
+ // excise extents
+
+ InitializeListHead(&changed_sector_list);
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ goto exit;
+ }
+
+ if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
+ update_checksum_tree(fcb->Vcb, &changed_sector_list);
+ }
+
+success2:
+ // update INODE_ITEM of parent
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto exit;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, fcb->subvol->id);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
+ fcb->par->inode_item.st_size -= bytecount * 2;
+ TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
+ fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+ fcb->par->inode_item.st_mtime = now;
+
+ dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(dirii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(fcb->Vcb, &tp);
+
+ insert_tree_item(fcb->Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+success:
+ consider_write(fcb->Vcb);
+
+ fcb->deleted = TRUE;
+
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+
+ if (FileObject && FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ }
+
+ // FIXME - set deleted flag of any open FCBs for ADS
+
+ TRACE("sending notification for deletion of %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ FILE_ACTION_REMOVED, NULL);
+
+ time2 = KeQueryPerformanceCounter(NULL);
+
+ ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
+
+exit:
+ if (utf8)
+ ExFreePool(utf8);
+}
+
+void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
+ ULONG rc;
+
+ rc = InterlockedDecrement(&fcb->refcount);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+// WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#else
+ _debug_message(func, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#endif
+#endif
+
+ if (rc > 0)
+ return;
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+
+ if (fcb->filepart.Buffer)
+ RtlFreeUnicodeString(&fcb->filepart);
+
+ ExDeleteResourceLite(&fcb->nonpaged->resource);
+ ExFreePool(fcb->nonpaged);
+
+ if (fcb->par/* && fcb->par != fcb->par->Vcb->root_fcb*/) {
+ RemoveEntryList(&fcb->list_entry);
+ _free_fcb(fcb->par, func, file, line);
+ }
+
+ if (fcb->prev)
+ fcb->prev->next = fcb->next;
+
+ if (fcb->next)
+ fcb->next->prev = fcb->prev;
+
+ if (fcb->Vcb->fcbs == fcb)
+ fcb->Vcb->fcbs = fcb->next;
+
+ if (fcb->full_filename.Buffer)
+ ExFreePool(fcb->full_filename.Buffer);
+
+ if (fcb->sd)
+ ExFreePool(fcb->sd);
+
+ if (fcb->adsxattr.Buffer)
+ ExFreePool(fcb->adsxattr.Buffer);
+
+ if (fcb->utf8.Buffer)
+ ExFreePool(fcb->utf8.Buffer);
+
+ FsRtlUninitializeFileLock(&fcb->lock);
+
+ ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+
+ ExFreePool(fcb);
+#ifdef DEBUG_FCB_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "freeing fcb %p\n", fcb);
+#else
+ _debug_message(func, "freeing fcb %p\n", fcb);
+#endif
+#endif
+}
+
+static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
+ fcb* fcb;
+ ccb* ccb;
+
+ TRACE("FileObject = %p\n", FileObject);
+
+ fcb = FileObject->FsContext;
+ if (!fcb) {
+ TRACE("FCB was NULL, returning success\n");
+ return STATUS_SUCCESS;
+ }
+
+ ccb = FileObject->FsContext2;
+
+ TRACE("close called for %.*S (fcb == %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+
+ FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
+
+ // FIXME - make sure notification gets sent if file is being deleted
+
+ if (ccb) {
+ if (ccb->query_string.Buffer)
+ RtlFreeUnicodeString(&ccb->query_string);
+
+ ExFreePool(ccb);
+ }
+
+ if (fcb->refcount == 1)
+ CcUninitializeCacheMap(FileObject, NULL, NULL);
+
+ free_fcb(fcb);
+
+ return STATUS_SUCCESS;
+}
+
+static void STDCALL uninit(device_extension* Vcb) {
+ LIST_ENTRY *le, *le2;
+ chunk* c;
+ space* s;
+
+ while (Vcb->roots) {
+ root* r = Vcb->roots->next;
+
+ // FIXME - also free root trees
+ ExDeleteResourceLite(&Vcb->roots->nonpaged->load_tree_lock);
+ ExFreePool(Vcb->roots->nonpaged);
+ ExFreePool(Vcb->roots);
+
+ Vcb->roots = r;
+ }
+
+ while ((le = RemoveHeadList(&Vcb->chunks)) != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ while ((le2 = RemoveHeadList(&c->space)) != &c->space) {
+ s = CONTAINING_RECORD(le2, space, list_entry);
+
+ ExFreePool(s);
+ }
+
+ if (c->devices)
+ ExFreePool(c->devices);
+
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
+ }
+
+ free_fcb(Vcb->volume_fcb);
+ free_fcb(Vcb->root_fcb);
+
+ ExDeleteResourceLite(&Vcb->fcb_lock);
+ ExDeleteResourceLite(&Vcb->load_lock);
+ ExDeleteResourceLite(&Vcb->tree_lock);
+
+ // FIXME - flush trees
+
+ ZwClose(Vcb->flush_thread_handle);
+
+ ExFreePool(Vcb);
+}
+
+static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb;
+ BOOL top_level;
+
+ TRACE("cleanup\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (DeviceObject == devobj) {
+ TRACE("closing file system\n");
+
+ if (DeviceObject->DeviceExtension)
+ uninit(DeviceObject->DeviceExtension);
+
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (FileObject) {
+ fcb = FileObject->FsContext;
+
+ TRACE("cleanup called for FileObject %p\n", FileObject);
+ TRACE("fcb %p (%.*S), refcount = %u, open_count = %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount, fcb->open_count);
+
+ FileObject->Flags |= FO_CLEANUP_COMPLETE;
+
+ if (fcb->open_count == 1 && fcb->delete_on_close && fcb != fcb->Vcb->root_fcb && fcb != fcb->Vcb->volume_fcb) {
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ delete_fcb(fcb, FileObject);
+ // FIXME - change allocation size etc. to zero
+
+ release_tree_lock(fcb->Vcb, TRUE);
+ }
+
+ fcb->open_count--;
+ }
+
+ Status = STATUS_SUCCESS;
+
+exit:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+ ULONG att;
+ char* eaval;
+ UINT16 ealen;
+
+ if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
+ if (ealen > 2) {
+ if (eaval[0] == '0' && eaval[1] == 'x') {
+ int i;
+ ULONG dosnum = 0;
+
+ for (i = 2; i < ealen; i++) {
+ dosnum *= 0x10;
+
+ if (eaval[i] >= '0' && eaval[i] <= '9')
+ dosnum |= eaval[i] - '0';
+ else if (eaval[i] >= 'a' && eaval[i] <= 'f')
+ dosnum |= eaval[i] + 10 - 'a';
+ else if (eaval[i] >= 'A' && eaval[i] <= 'F')
+ dosnum |= eaval[i] + 10 - 'a';
+ }
+
+ TRACE("DOSATTRIB: %08x\n", dosnum);
+
+ ExFreePool(eaval);
+
+ return dosnum;
+ }
+ }
+
+ ExFreePool(eaval);
+ }
+
+ switch (type) {
+ case BTRFS_TYPE_DIRECTORY:
+ att = FILE_ATTRIBUTE_DIRECTORY;
+ break;
+
+ case BTRFS_TYPE_SYMLINK:
+ att = FILE_ATTRIBUTE_REPARSE_POINT;
+ break;
+
+ default:
+ att = 0;
+ break;
+ }
+
+ if (dotfile) {
+ att |= FILE_ATTRIBUTE_HIDDEN;
+ }
+
+ att |= FILE_ATTRIBUTE_ARCHIVE;
+
+ // FIXME - get READONLY from ii->st_mode
+ // FIXME - return SYSTEM for block/char devices?
+
+ if (att == 0)
+ att = FILE_ATTRIBUTE_NORMAL;
+
+ return att;
+}
+
+// static int STDCALL utf8icmp(char* a, char* b) {
+// return strcmp(a, b); // FIXME - don't treat as ASCII
+// }
+
+NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
+ IO_STATUS_BLOCK* IoStatus;
+ LARGE_INTEGER Offset;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ read_context* context;
+
+ num_reads++;
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
+
+ Offset.QuadPart = StartingOffset;
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
+ Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+ if (!Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (DeviceObject->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (DeviceObject->Flags & DO_DIRECT_IO) {
+// TRACE("direct IO\n");
+
+ Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ // IoFreeIrp(Irp);
+ goto exit;
+// } else {
+// TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+// TRACE("neither buffered nor direct IO\n");
+ Irp->UserBuffer = Buffer;
+ }
+
+ IrpSp->Parameters.Read.Length = Length;
+ IrpSp->Parameters.Read.ByteOffset = Offset;
+
+ Irp->UserIosb = IoStatus;
+// Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ Irp->UserEvent = &context->Event;
+
+// IoQueueThreadIrp(Irp);
+
+ IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
+
+// if (Override)
+// {
+// Stack = IoGetNextIrpStackLocation(Irp);
+// Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+// }
+
+// TRACE("Calling IO Driver... with irp %p\n", Irp);
+ Status = IoCallDriver(DeviceObject, Irp);
+
+// TRACE("Waiting for IO Operation for %p\n", Irp);
+ if (Status == STATUS_PENDING) {
+// TRACE("Operation pending\n");
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+// TRACE("Getting IO Status... for %p\n", Irp);
+ Status = context->iosb.Status;
+ }
+
+ if (DeviceObject->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+ ExFreePool(IoStatus);
+ ExFreePool(context);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) {
+ NTSTATUS Status;
+ superblock* sb;
+ unsigned int i, to_read;
+ UINT32 crc32;
+
+ to_read = sector_align(sizeof(superblock), device->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+
+ i = 0;
+
+ while (superblock_addrs[i] > 0) {
+ if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length)
+ break;
+
+ Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("Failed to read superblock %u: %08x\n", i, Status);
+ ExFreePool(sb);
+ return Status;
+ }
+
+ TRACE("got superblock %u!\n", i);
+
+ if (i == 0 || sb->generation > Vcb->superblock.generation)
+ RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
+
+ i++;
+ }
+
+ ExFreePool(sb);
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
+ crc32 = ~crc32;
+ TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
+
+ if (crc32 != *((UINT32*)Vcb->superblock.checksum))
+ return STATUS_INTERNAL_ERROR; // FIXME - correct error?
+
+ TRACE("label is %s\n", Vcb->superblock.label);
+// utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
+ ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override)
+{
+ PIRP Irp;
+ KEVENT Event;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION Stack;
+ IO_STATUS_BLOCK IoStatus;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Irp = IoBuildDeviceIoControlRequest(ControlCode,
+ DeviceObject,
+ InputBuffer,
+ InputBufferSize,
+ OutputBuffer,
+ OutputBufferSize,
+ FALSE,
+ &Event,
+ &IoStatus);
+
+ if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
+
+ if (Override) {
+ Stack = IoGetNextIrpStackLocation(Irp);
+ Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+ }
+
+ Status = IoCallDriver(DeviceObject, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ }
+
+ return Status;
+}
+
+// static void STDCALL find_chunk_root(device_extension* Vcb) {
+// UINT32 i;
+// KEY* key;
+//
+// i = 0;
+// while (i < Vcb->superblock.n) {
+// key = &Vcb->superblock.sys_chunk_array[i];
+// i += sizeof(KEY);
+// }
+//
+// // FIXME
+// }
+
+// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
+// if (!Vcb->log_to_phys) {
+// Vcb->log_to_phys = ltp;
+// ltp->next = NULL;
+// return;
+// }
+//
+// // FIXME - these should be ordered
+// ltp->next = Vcb->log_to_phys;
+// Vcb->log_to_phys = ltp;
+// }
+
+static void STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
+ root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
+
+ r->id = id;
+ r->treeholder.address = addr;
+ r->treeholder.tree = NULL;
+ init_tree_holder(&r->treeholder);
+ r->prev = NULL;
+ r->next = Vcb->roots;
+
+ r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
+ ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
+
+ r->lastinode = 0;
+
+ if (tp) {
+ RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
+ if (tp->item->size < sizeof(ROOT_ITEM))
+ RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
+ }
+
+ if (Vcb->roots)
+ Vcb->roots->prev = r;
+
+ Vcb->roots = r;
+
+ switch (r->id) {
+ case BTRFS_ROOT_ROOT:
+ Vcb->root_root = r;
+ break;
+
+ case BTRFS_ROOT_EXTENT:
+ Vcb->extent_root = r;
+ break;
+
+ case BTRFS_ROOT_CHUNK:
+ Vcb->chunk_root = r;
+ break;
+
+ case BTRFS_ROOT_DEVTREE:
+ Vcb->dev_root = r;
+ break;
+
+ case BTRFS_ROOT_CHECKSUM:
+ Vcb->checksum_root = r;
+ break;
+ }
+}
+
+static void STDCALL look_for_roots(device_extension* Vcb) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ BOOL b;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any root tree entries\n");
+ return;
+ }
+
+ do {
+ TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;;
+
+ TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
+
+ add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+}
+
+static void add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) {
+ disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
+
+ dh->address = address;
+ dh->size = size;
+ dh->provisional = FALSE;
+
+ InsertTailList(disk_holes, &dh->listentry);
+}
+
+static void find_disk_holes(device_extension* Vcb, device* dev) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ UINT64 lastaddr;
+
+ InitializeListHead(&dev->disk_holes);
+
+ searchkey.obj_id = dev->devitem.dev_id;
+ searchkey.obj_type = TYPE_DEV_EXTENT;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any dev tree entries\n");
+ return;
+ }
+
+ lastaddr = 0;
+
+ do {
+ if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
+ DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
+
+ if (tp.item->key.offset > lastaddr)
+ add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr);
+
+ lastaddr = tp.item->key.offset + de->length;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (lastaddr < dev->devitem.num_bytes)
+ add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
+
+ // FIXME - free disk_holes when unmounting
+}
+
+device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
+ UINT64 i;
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
+ Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7],
+ Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]);
+
+ if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ TRACE("returning device %llx\n", i);
+ return &Vcb->devices[i];
+ }
+ }
+
+ WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
+ uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
+
+ return NULL;
+}
+
+static void STDCALL load_chunk_root(device_extension* Vcb) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ BOOL b;
+ chunk* c;
+ UINT64 i;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any chunk tree entries\n");
+ return;
+ }
+
+ do {
+ TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) {
+ // FIXME - this is a hack; make this work with multiple devices!
+ RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+ } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
+ c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+
+ c->size = tp.item->size;
+ c->offset = tp.item->key.offset;
+ c->used = c->oldused = 0;
+ c->space_changed = FALSE;
+
+ c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
+
+ if (c->chunk_item->num_stripes > 0) {
+ CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+
+ c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < c->chunk_item->num_stripes; i++) {
+ c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
+ TRACE("device %llu = %p\n", i, c->devices[i]);
+ }
+ } else
+ c->devices = NULL;
+
+ InitializeListHead(&c->space);
+
+ InsertTailList(&Vcb->chunks, &c->list_entry);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ Vcb->log_to_phys_loaded = TRUE;
+}
+
+static BOOL load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ FREE_SPACE_ITEM* fsi;
+ UINT64 inode, num_sectors, i, generation;
+ INODE_ITEM* ii;
+ UINT8* data;
+ NTSTATUS Status;
+ UINT32 *checksums, crc32;
+#if DEBUG_LEVEL > 2
+ FREE_SPACE_ENTRY* fse;
+ UINT64 num_entries;
+#endif
+
+ TRACE("(%p, %llx)\n", Vcb, c->offset);
+
+ if (Vcb->superblock.generation != Vcb->superblock.cache_generation)
+ return FALSE;
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ fsi = (FREE_SPACE_ITEM*)tp.item->data;
+
+ if (fsi->generation != Vcb->superblock.cache_generation) {
+ WARN("cache had generation %llx, expecting %llx\n", fsi->generation, Vcb->superblock.cache_generation);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ if (fsi->key.obj_type != TYPE_INODE_ITEM) {
+ WARN("cache pointed to something other than an INODE_ITEM\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ if (fsi->num_bitmaps > 0) {
+ WARN("cache had bitmaps, unsure of how to deal with these\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ inode = fsi->key.obj_id;
+
+ searchkey = fsi->key;
+#if DEBUG_LEVEL > 2
+ num_entries = fsi->num_entries;
+#endif
+
+ if (!find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ ii = (INODE_ITEM*)tp2.item->data;
+
+ data = ExAllocatePoolWithTag(PagedPool, ii->st_size, ALLOC_TAG);
+
+ Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ num_sectors = ii->st_size / Vcb->superblock.sector_size;
+
+ generation = *(data + (num_sectors * sizeof(UINT32)));
+
+ if (generation != Vcb->superblock.cache_generation) {
+ ERR("generation was %llx, expected %llx\n", generation, Vcb->superblock.cache_generation);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * num_sectors, ALLOC_TAG); // FIXME - get rid of this
+ RtlCopyMemory(checksums, data, sizeof(UINT32) * num_sectors);
+
+ for (i = 0; i < num_sectors; i++) {
+ if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
+ crc32 = ~calc_crc32c(0xffffffff, &data[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+ else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
+ crc32 = 0; // FIXME - test this
+ else
+ crc32 = ~calc_crc32c(0xffffffff, &data[sizeof(UINT32) * num_sectors], ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
+
+ if (crc32 != checksums[i]) {
+ WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]);
+ ExFreePool(checksums);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+ }
+
+ ExFreePool(checksums);
+
+#if DEBUG_LEVEL > 2
+ fse = (FREE_SPACE_ENTRY*)&data[(sizeof(UINT32) * num_sectors) + sizeof(UINT64)];
+
+ for (i = 0; i < num_entries; i++) {
+ TRACE("(%llx,%llx,%x)\n", fse[i].offset, fse[i].size, fse[i].type);
+ }
+#endif
+
+ FIXME("FIXME - read cache\n");
+
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+
+ return FALSE;
+}
+
+static void load_free_space_cache(device_extension* Vcb, chunk* c) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT64 lastaddr;
+ BOOL b;
+ space *s, *s2;
+ LIST_ENTRY* le;
+
+ load_stored_free_space_cache(Vcb, c);
+
+ TRACE("generating free space cache for chunk %llx\n", c->offset);
+
+ searchkey.obj_id = c->offset;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return;
+ }
+
+ lastaddr = c->offset;
+
+ do {
+ if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
+ break;
+
+ if (tp.item->key.obj_id >= c->offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
+ if (tp.item->key.obj_id > lastaddr) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = tp.item->key.obj_id - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+
+ if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
+ lastaddr = tp.item->key.obj_id + Vcb->superblock.node_size;
+ else
+ lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+
+ free_traverse_ptr(&tp);
+
+ // add allocated space
+
+ lastaddr = c->offset;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset > lastaddr) {
+ s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s2->offset = lastaddr;
+ s2->size = s->offset - lastaddr;
+ s2->type = SPACE_TYPE_USED;
+
+ InsertTailList(&s->list_entry, &s2->list_entry);
+ }
+
+ lastaddr = s->offset + s->size;
+
+ le = le->Flink;
+ }
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_USED;
+ InsertTailList(&c->space, &s->list_entry);
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+}
+
+void protect_superblocks(device_extension* Vcb, chunk* c) {
+ int i = 0, j;
+ UINT64 addr;
+
+ // FIXME - this will need modifying for RAID
+
+ while (superblock_addrs[i] != 0) {
+ CHUNK_ITEM* ci = c->chunk_item;
+ CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ for (j = 0; j < ci->num_stripes; j++) {
+ if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ addr = (superblock_addrs[i] - cis[j].offset) + c->offset;
+ TRACE("addr %llx\n", addr);
+
+ add_to_space_list(c, addr, sizeof(superblock), SPACE_TYPE_USED);
+ }
+ }
+
+ i++;
+ }
+}
+
+static void STDCALL find_chunk_usage(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ traverse_ptr tp;
+ BLOCK_GROUP_ITEM* bgi;
+
+// c00000,c0,800000
+// block_group_item size=7f0000 chunktreeid=100 flags=1
+
+ searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ searchkey.obj_id = c->offset;
+ searchkey.offset = c->chunk_item->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
+
+ c->used = c->oldused = bgi->used;
+
+ TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
+ }
+
+ free_traverse_ptr(&tp);
+// if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
+// cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+//
+// return (addr - c->offset) + cis->offset;
+// }
+
+ // FIXME - make sure we free occasionally after doing one of these, or we
+ // might use up a lot of memory with a big disk.
+
+ load_free_space_cache(Vcb, c);
+ protect_superblocks(Vcb, c);
+
+ le = le->Flink;
+ }
+}
+
+// static void STDCALL root_test(device_extension* Vcb) {
+// root* r;
+// KEY searchkey;
+// traverse_ptr tp, next_tp;
+// BOOL b;
+//
+// r = Vcb->roots;
+// while (r) {
+// if (r->id == 0x102)
+// break;
+// r = r->next;
+// }
+//
+// if (!r) {
+// ERR("Could not find root tree.\n");
+// return;
+// }
+//
+// searchkey.obj_id = 0x1b6;
+// searchkey.obj_type = 0xb;
+// searchkey.offset = 0;
+//
+// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
+// ERR("Could not find first item.\n");
+// return;
+// }
+//
+// b = TRUE;
+// do {
+// TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+// }
+
+static void load_sys_chunks(device_extension* Vcb) {
+ KEY key;
+ ULONG n = Vcb->superblock.n;
+
+ while (n > 0) {
+ if (n > sizeof(KEY)) {
+ RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
+ n -= sizeof(KEY);
+ } else
+ return;
+
+ TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
+
+ if (key.obj_type == TYPE_CHUNK_ITEM) {
+ CHUNK_ITEM* ci;
+ ULONG cisize;
+ sys_chunk* sc;
+
+ if (n < sizeof(CHUNK_ITEM))
+ return;
+
+ ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
+ cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
+
+ if (n < cisize)
+ return;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
+ sc->key = key;
+ sc->size = cisize;
+ sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
+ RtlCopyMemory(sc->data, ci, sc->size);
+ InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
+
+ n -= cisize;
+ } else {
+ ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
+ return;
+ }
+ }
+}
+
+static root* find_default_subvol(device_extension* Vcb) {
+ root* subvol;
+ UINT64 inode;
+ UINT8 type;
+ UNICODE_STRING filename;
+
+ static WCHAR fn[] = L"default";
+ static UINT32 crc32 = 0x8dbfc2d2;
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
+ filename.Buffer = fn;
+ filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR);
+
+ if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL))
+ WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
+ else
+ return subvol;
+ }
+
+ subvol = Vcb->roots;
+ while (subvol && subvol->id != BTRFS_ROOT_FSTREE)
+ subvol = subvol->next;
+
+ return subvol;
+}
+
+static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION Stack;
+ PDEVICE_OBJECT NewDeviceObject = NULL;
+ PDEVICE_OBJECT DeviceToMount;
+ NTSTATUS Status;
+ device_extension* Vcb = NULL;
+ PARTITION_INFORMATION_EX piex;
+ UINT64 i;
+ LIST_ENTRY* le;
+
+ TRACE("mount_vol called\n");
+
+ if (DeviceObject != devobj)
+ {
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ goto exit;
+ }
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+ DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
+
+// Status = NtfsHasFileSystem(DeviceToMount);
+// if (!NT_SUCCESS(Status))
+// {
+// goto ByeBye;
+// }
+
+ Status = IoCreateDevice(drvobj,
+ sizeof(device_extension),
+ NULL,
+ FILE_DEVICE_DISK_FILE_SYSTEM,
+ 0,
+ FALSE,
+ &NewDeviceObject);
+ if (!NT_SUCCESS(Status))
+ goto exit;
+
+// TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
+
+ NewDeviceObject->Flags |= DO_DIRECT_IO;
+ Vcb = (PVOID)NewDeviceObject->DeviceExtension;
+ RtlZeroMemory(Vcb, sizeof(device_extension));
+
+ InitializeListHead(&Vcb->tree_cache);
+
+ ExInitializeResourceLite(&Vcb->tree_lock);
+ Vcb->tree_lock_counter = 0;
+ Vcb->open_trees = 0;
+ Vcb->write_trees =0;
+
+ ExInitializeResourceLite(&Vcb->load_lock);
+ FsRtlEnterFileSystem();
+ ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
+
+// Vcb->Identifier.Type = NTFS_TYPE_VCB;
+// Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
+//
+// Status = NtfsGetVolumeData(DeviceToMount,
+// Vcb);
+// if (!NT_SUCCESS(Status))
+// goto ByeBye;
+
+// Vcb->device = DeviceToMount;
+ DeviceToMount->Flags |= DO_DIRECT_IO;
+
+// Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+// &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error reading disk geometry: %08x\n", Status);
+// goto exit;
+// } else {
+// TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
+// Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
+// Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
+// }
+
+ Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
+ &piex, sizeof(piex), TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error reading partition information: %08x\n", Status);
+ goto exit;
+ }
+
+ Vcb->length = piex.PartitionLength.QuadPart;
+ TRACE("partition length = %u\n", piex.PartitionLength);
+
+ Status = read_superblock(Vcb, DeviceToMount);
+ if (!NT_SUCCESS(Status))
+ goto exit;
+
+ if (Vcb->superblock.magic != BTRFS_MAGIC) {
+ ERR("not a BTRFS volume\n");
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ } else {
+ TRACE("btrfs magic found\n");
+ }
+
+ if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
+ WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ le = volumes.Flink;
+ while (le != &volumes) {
+ volume* v = CONTAINING_RECORD(le, volume, list_entry);
+
+ if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
+ // skipping over device in RAID which isn't the first one
+ // FIXME - hide this in My Computer
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ le = le->Flink;
+ }
+
+ // FIXME - remove this when we can
+ if (Vcb->superblock.num_devices > 1) {
+ WARN("not mounting - multiple devices not yet implemented\n");
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ Vcb->readonly = FALSE;
+ if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
+ WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
+ Vcb->readonly = TRUE;
+ }
+
+ Vcb->superblock.generation++;
+ Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
+
+ Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
+ Vcb->devices[0].devobj = DeviceToMount;
+ RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
+
+ if (Vcb->superblock.num_devices > 1)
+ RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
+
+ // FIXME - find other devices, if there are any
+
+ TRACE("DeviceToMount = %p\n", DeviceToMount);
+ TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
+
+ NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
+ Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
+ Stack->Parameters.MountVolume.Vpb->RealDevice = DeviceToMount;
+ Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
+ NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
+ NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+
+// find_chunk_root(Vcb);
+// Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
+
+// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
+
+ Vcb->roots = NULL;
+ Vcb->log_to_phys_loaded = FALSE;
+
+ Vcb->max_inline = Vcb->superblock.node_size / 2;
+
+// Vcb->write_trees = NULL;
+
+ add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
+
+ if (!Vcb->chunk_root) {
+ ERR("Could not load chunk root.\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ InitializeListHead(&Vcb->sys_chunks);
+ load_sys_chunks(Vcb);
+
+ InitializeListHead(&Vcb->chunks);
+ InitializeListHead(&Vcb->trees);
+
+ InitializeListHead(&Vcb->DirNotifyList);
+
+ FsRtlNotifyInitializeSync(&Vcb->NotifySync);
+
+ load_chunk_root(Vcb);
+
+ add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
+
+ if (!Vcb->root_root) {
+ ERR("Could not load root of roots.\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ look_for_roots(Vcb);
+
+ find_chunk_usage(Vcb);
+
+ Vcb->volume_fcb = create_fcb();
+ Vcb->volume_fcb->Vcb = Vcb;
+ Vcb->volume_fcb->sd = NULL;
+
+ Vcb->root_fcb = create_fcb();
+ Vcb->root_fcb->Vcb = Vcb;
+ Vcb->root_fcb->inode = SUBVOL_ROOT_INODE;
+ Vcb->root_fcb->type = BTRFS_TYPE_DIRECTORY;
+
+ Vcb->root_fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
+ Vcb->root_fcb->full_filename.Buffer[0] = '\\';
+ Vcb->root_fcb->full_filename.Length = Vcb->root_fcb->full_filename.MaximumLength = sizeof(WCHAR);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("volume FCB = %p\n", Vcb->volume_fcb);
+ WARN("root FCB = %p\n", Vcb->root_fcb);
+#endif
+
+ Vcb->root_fcb->subvol = find_default_subvol(Vcb);
+
+ if (!Vcb->root_fcb->subvol) {
+ ERR("could not find top subvol\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ Vcb->fcbs = Vcb->root_fcb;
+
+ if (!get_item(Vcb, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, TYPE_INODE_ITEM, 0, &Vcb->root_fcb->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find INODE_ITEM for root directory\n");
+ }
+
+ fcb_get_sd(Vcb->root_fcb);
+
+ Vcb->root_fcb->atts = get_file_attributes(Vcb, &Vcb->root_fcb->inode_item, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, Vcb->root_fcb->type,
+ FALSE, FALSE);
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ find_disk_holes(Vcb, &Vcb->devices[i]);
+ }
+
+ ExInitializeResourceLite(&Vcb->fcb_lock);
+
+// root_test(Vcb);
+
+// Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
+// Vcb->StorageDevice);
+//
+// InitializeListHead(&Vcb->FcbListHead);
+//
+// Fcb = NtfsCreateFCB(NULL, Vcb);
+// if (Fcb == NULL)
+// {
+// Status = STATUS_INSUFFICIENT_RESOURCES;
+// goto ByeBye;
+// }
+//
+// Ccb = ExAllocatePoolWithTag(NonPagedPool,
+// sizeof(NTFS_CCB),
+// TAG_CCB);
+// if (Ccb == NULL)
+// {
+// Status = STATUS_INSUFFICIENT_RESOURCES;
+// goto ByeBye;
+// }
+//
+// RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
+//
+// Ccb->Identifier.Type = NTFS_TYPE_CCB;
+// Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
+//
+// Vcb->StreamFileObject->FsContext = Fcb;
+// Vcb->StreamFileObject->FsContext2 = Ccb;
+// Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+// Vcb->StreamFileObject->PrivateCacheMap = NULL;
+// Vcb->StreamFileObject->Vpb = Vcb->Vpb;
+// Ccb->PtrFileObject = Vcb->StreamFileObject;
+// Fcb->FileObject = Vcb->StreamFileObject;
+// Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
+//
+// Fcb->Flags = FCB_IS_VOLUME_STREAM;
+//
+// Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
+// Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
+// Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
+//
+// CcInitializeCacheMap(Vcb->StreamFileObject,
+// (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+// FALSE,
+// &(NtfsGlobalData->CacheMgrCallbacks),
+// Fcb);
+//
+ ExInitializeResourceLite(&Vcb->DirResource);
+// ExInitializeResourceLite(&Vcb->LogToPhysLock);
+
+ KeInitializeSpinLock(&Vcb->FcbListLock);
+//
+// /* Get serial number */
+// NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
+//
+// /* Get volume label */
+// NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
+// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
+// Vcb->NtfsInfo.VolumeLabel,
+// Vcb->NtfsInfo.VolumeLabelLength);
+ NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
+ NewDeviceObject->Vpb->VolumeLabel[0] = '?';
+ NewDeviceObject->Vpb->VolumeLabel[1] = 0;
+
+ Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = STATUS_SUCCESS;
+
+exit:
+// if (!NT_SUCCESS(Status))
+// {
+// /* Cleanup */
+// if (Vcb && Vcb->StreamFileObject)
+// ObDereferenceObject(Vcb->StreamFileObject);
+//
+// if (Fcb)
+// ExFreePool(Fcb);
+//
+// if (Ccb)
+// ExFreePool(Ccb);
+//
+// if (NewDeviceObject)
+// IoDeleteDevice(NewDeviceObject);
+// }
+
+ if (Vcb) {
+ ExReleaseResourceLite(&Vcb->load_lock);
+ FsRtlExitFileSystem();
+ }
+
+ TRACE("mount_vol done (status: %lx)\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS status;
+ BOOL top_level;
+
+ TRACE("file system control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ status = STATUS_NOT_IMPLEMENTED;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Irp->IoStatus.Information = 0;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_MOUNT_VOLUME:
+ TRACE("IRP_MN_MOUNT_VOLUME\n");
+
+// Irp->IoStatus.Status = STATUS_SUCCESS;
+ status = mount_vol(DeviceObject, Irp);
+// IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
+// IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
+
+// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+// return Irp->IoStatus.Status;
+ break;
+
+ case IRP_MN_KERNEL_CALL:
+ TRACE("IRP_MN_KERNEL_CALL\n");
+ break;
+
+ case IRP_MN_LOAD_FILE_SYSTEM:
+ TRACE("IRP_MN_LOAD_FILE_SYSTEM\n");
+ break;
+
+ case IRP_MN_USER_FS_REQUEST:
+ TRACE("IRP_MN_USER_FS_REQUEST\n");
+
+ status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
+ break;
+
+ case IRP_MN_VERIFY_VOLUME:
+ TRACE("IRP_MN_VERIFY_VOLUME\n");
+ break;
+
+ default:
+ WARN("unknown minor %u\n", IrpSp->MinorFunction);
+ break;
+
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return status;
+}
+
+static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ fcb* fcb = IrpSp->FileObject->FsContext;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ TRACE("lock control\n");
+
+ Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb;
+ BOOL top_level;
+
+ FIXME("STUB: device control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ WARN("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+
+ }
+
+ fcb = FileObject->FsContext;
+
+ if (!fcb) {
+ ERR("FCB was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (fcb == Vcb->volume_fcb) {
+ FIXME("FIXME - pass through\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ } else {
+ TRACE("filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+ TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ default:
+ WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xff0000) >> 16,
+ (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xc000) >> 14, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3ffc) >> 2,
+ IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3);
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+end:
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ uid_map* um;
+ BOOL top_level;
+
+ TRACE("shutdown\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ // FIXME - free Vcb->devices
+
+ // FIXME - only do this when last device is unmounted?
+ while ((le = RemoveHeadList(&uid_map_list)) != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ ExFreePool(um->sid);
+
+ ExFreePool(um);
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FIXME("STUB: pnp\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ TRACE(" IRP_MN_CANCEL_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ TRACE(" IRP_MN_QUERY_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ TRACE(" IRP_MN_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_START_DEVICE:
+ TRACE(" IRP_MN_START_DEVICE\n");
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ TRACE(" IRP_MN_SURPRISE_REMOVAL\n");
+ break;
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ TRACE(" IRP_MN_QUERY_DEVICE_RELATIONS\n");
+ break;
+
+ default:
+ WARN("Unrecognized minor function 0x%x\n", IrpSp->MinorFunction);
+ break;
+ }
+
+// Irp->IoStatus.Status = Status;
+// Irp->IoStatus.Information = 0;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+
+ Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+
+// IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+#ifdef DEBUG
+static void STDCALL init_serial() {
+ UNICODE_STRING us;
+ NTSTATUS Status;
+
+ static const WCHAR com1[] = {'\\','D','e','v','i','c','e','\\','S','e','r','i','a','l','0',0};
+
+ RtlInitUnicodeString(&us, com1);
+
+ Status = IoGetDeviceObjectPointer(&us, FILE_WRITE_DATA, &comfo, &comdo);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ }
+}
+#endif
+
+#ifndef __REACTOS__
+static void STDCALL check_cpu() {
+ unsigned int cpuInfo[4];
+#ifndef _MSC_VER
+ __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
+ have_sse42 = cpuInfo[2] & bit_SSE4_2;
+#else
+ __cpuid(cpuInfo, 1);
+ have_sse42 = cpuInfo[2] & (1 << 20);
+#endif
+
+ if (have_sse42)
+ TRACE("SSE4.2 is supported\n");
+ else
+ TRACE("SSE4.2 not supported\n");
+}
+#endif
+
+static void STDCALL read_registry(PUNICODE_STRING regpath) {
+ HANDLE h;
+ UNICODE_STRING us;
+ OBJECT_ATTRIBUTES oa;
+ ULONG dispos;
+ NTSTATUS Status;
+ WCHAR* path;
+ ULONG kvfilen, retlen, i;
+ KEY_VALUE_FULL_INFORMATION* kvfi;
+
+ const WCHAR mappings[] = L"\\Mappings";
+
+ path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG);
+ RtlCopyMemory(path, regpath->Buffer, regpath->Length);
+ RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR));
+
+ us.Buffer = path;
+ us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR));
+
+ InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ // FIXME - keep open and do notify for changes
+ Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwCreateKey returned %08x\n", Status);
+ goto exit;
+ }
+
+// TRACE("dispos = %u\n", dispos);
+
+ if (dispos == REG_OPENED_EXISTING_KEY) {
+ kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
+ kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
+
+ i = 0;
+ do {
+ Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
+
+ if (NT_SUCCESS(Status) && kvfi->DataLength > 0) {
+ UINT32 val = 0;
+
+ RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32)));
+
+ TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val);
+
+ add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
+ }
+
+ i = i + 1;
+ } while (Status != STATUS_NO_MORE_ENTRIES);
+ }
+
+// InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+//
+// Status = ZwOpenKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("ZwOpenKey returned %08x\n", Status);
+// return;
+// }
+
+ ZwClose(h);
+
+exit:
+ ExFreePool(path);
+}
+
+NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
+ NTSTATUS Status;
+ PDEVICE_OBJECT DeviceObject;
+ UNICODE_STRING device_nameW;
+ UNICODE_STRING dosdevice_nameW;
+
+#ifdef DEBUG
+ init_serial();
+#endif
+
+ TRACE("DriverEntry\n");
+
+#ifndef __REACTOS__
+ check_cpu();
+#endif
+
+// TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
+
+ drvobj = DriverObject;
+
+ DriverObject->DriverUnload = DriverUnload;
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close;
+ DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)drv_write;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)drv_query_information;
+ DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)drv_set_information;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)drv_query_ea;
+ DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)drv_set_ea;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)drv_flush_buffers;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
+ DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_set_volume_information;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
+ DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)drv_directory_control;
+ DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_file_system_control;
+ DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)drv_query_security;
+ DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)drv_set_security;
+
+ device_nameW.Buffer = device_name;
+ device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) * sizeof(WCHAR);
+ dosdevice_nameW.Buffer = dosdevice_name;
+ dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+
+ Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &DeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateDevice returned %08x\n", Status);
+ return Status;
+ }
+
+ devobj = DeviceObject;
+
+ InitializeListHead(&uid_map_list);
+ read_registry(RegistryPath);
+
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateSymbolicLink returned %08x\n", Status);
+ return Status;
+ }
+
+ init_cache();
+
+ InitializeListHead(&volumes);
+ look_for_vols(&volumes);
+
+ IoRegisterFileSystem(DeviceObject);
+
+ return STATUS_SUCCESS;
+}
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+/******************************************************************************
+ * RtlUnicodeToUTF8N [NTDLL.@]
+ */
+NTSTATUS WINAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+ ULONG *utf8_bytes_written,
+ const WCHAR *uni_src, ULONG uni_bytes)
+{
+ NTSTATUS status;
+ ULONG i;
+ ULONG written;
+ ULONG ch;
+ BYTE utf8_ch[4];
+ ULONG utf8_ch_len;
+
+ if (!uni_src)
+ return STATUS_INVALID_PARAMETER_4;
+ if (!utf8_bytes_written)
+ return STATUS_INVALID_PARAMETER;
+ if (utf8_dest && uni_bytes % sizeof(WCHAR))
+ return STATUS_INVALID_PARAMETER_5;
+
+ written = 0;
+ status = STATUS_SUCCESS;
+
+ for (i = 0; i < uni_bytes / sizeof(WCHAR); i++)
+ {
+ /* decode UTF-16 into ch */
+ ch = uni_src[i];
+ if (ch >= 0xdc00 && ch <= 0xdfff)
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ if (i + 1 < uni_bytes / sizeof(WCHAR))
+ {
+ ch -= 0xd800;
+ ch <<= 10;
+ if (uni_src[i + 1] >= 0xdc00 && uni_src[i + 1] <= 0xdfff)
+ {
+ ch |= uni_src[i + 1] - 0xdc00;
+ ch += 0x010000;
+ i++;
+ }
+ else
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ }
+ else
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ }
+
+ /* encode ch as UTF-8 */
+#ifndef __REACTOS__
+ assert(ch <= 0x10ffff);
+#endif
+ if (ch < 0x80)
+ {
+ utf8_ch[0] = ch & 0x7f;
+ utf8_ch_len = 1;
+ }
+ else if (ch < 0x800)
+ {
+ utf8_ch[0] = 0xc0 | (ch >> 6 & 0x1f);
+ utf8_ch[1] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 2;
+ }
+ else if (ch < 0x10000)
+ {
+ utf8_ch[0] = 0xe0 | (ch >> 12 & 0x0f);
+ utf8_ch[1] = 0x80 | (ch >> 6 & 0x3f);
+ utf8_ch[2] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 3;
+ }
+ else if (ch < 0x200000)
+ {
+ utf8_ch[0] = 0xf0 | (ch >> 18 & 0x07);
+ utf8_ch[1] = 0x80 | (ch >> 12 & 0x3f);
+ utf8_ch[2] = 0x80 | (ch >> 6 & 0x3f);
+ utf8_ch[3] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 4;
+ }
+
+ if (!utf8_dest)
+ {
+ written += utf8_ch_len;
+ continue;
+ }
+
+ if (utf8_bytes_max >= utf8_ch_len)
+ {
+ memcpy(utf8_dest, utf8_ch, utf8_ch_len);
+ utf8_dest += utf8_ch_len;
+ utf8_bytes_max -= utf8_ch_len;
+ written += utf8_ch_len;
+ }
+ else
+ {
+ utf8_bytes_max = 0;
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ *utf8_bytes_written = written;
+ return status;
+}
+
+
+/******************************************************************************
+ * RtlUTF8ToUnicodeN [NTDLL.@]
+ */
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+ ULONG *uni_bytes_written,
+ const CHAR *utf8_src, ULONG utf8_bytes)
+{
+ NTSTATUS status;
+ ULONG i, j;
+ ULONG written;
+ ULONG ch;
+ ULONG utf8_trail_bytes;
+ WCHAR utf16_ch[3];
+ ULONG utf16_ch_len;
+
+ if (!utf8_src)
+ return STATUS_INVALID_PARAMETER_4;
+ if (!uni_bytes_written)
+ return STATUS_INVALID_PARAMETER;
+
+ written = 0;
+ status = STATUS_SUCCESS;
+
+ for (i = 0; i < utf8_bytes; i++)
+ {
+ /* read UTF-8 lead byte */
+ ch = (BYTE)utf8_src[i];
+ utf8_trail_bytes = 0;
+ if (ch >= 0xf5)
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0xf0)
+ {
+ ch &= 0x07;
+ utf8_trail_bytes = 3;
+ }
+ else if (ch >= 0xe0)
+ {
+ ch &= 0x0f;
+ utf8_trail_bytes = 2;
+ }
+ else if (ch >= 0xc2)
+ {
+ ch &= 0x1f;
+ utf8_trail_bytes = 1;
+ }
+ else if (ch >= 0x80)
+ {
+ /* overlong or trail byte */
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+
+ /* read UTF-8 trail bytes */
+ if (i + utf8_trail_bytes < utf8_bytes)
+ {
+ for (j = 0; j < utf8_trail_bytes; j++)
+ {
+ if ((utf8_src[i + 1] & 0xc0) == 0x80)
+ {
+ ch <<= 6;
+ ch |= utf8_src[i + 1] & 0x3f;
+ i++;
+ }
+ else
+ {
+ ch = 0xfffd;
+ utf8_trail_bytes = 0;
+ status = STATUS_SOME_NOT_MAPPED;
+ break;
+ }
+ }
+ }
+ else
+ {
+ ch = 0xfffd;
+ utf8_trail_bytes = 0;
+ status = STATUS_SOME_NOT_MAPPED;
+ i = utf8_bytes;
+ }
+
+ /* encode ch as UTF-16 */
+ if ((ch > 0x10ffff) ||
+ (ch >= 0xd800 && ch <= 0xdfff) ||
+ (utf8_trail_bytes == 2 && ch < 0x00800) ||
+ (utf8_trail_bytes == 3 && ch < 0x10000))
+ {
+ /* invalid codepoint or overlong encoding */
+ utf16_ch[0] = 0xfffd;
+ utf16_ch[1] = 0xfffd;
+ utf16_ch[2] = 0xfffd;
+ utf16_ch_len = utf8_trail_bytes;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0x10000)
+ {
+ /* surrogate pair */
+ ch -= 0x010000;
+ utf16_ch[0] = 0xd800 + (ch >> 10 & 0x3ff);
+ utf16_ch[1] = 0xdc00 + (ch >> 0 & 0x3ff);
+ utf16_ch_len = 2;
+ }
+ else
+ {
+ /* single unit */
+ utf16_ch[0] = ch;
+ utf16_ch_len = 1;
+ }
+
+ if (!uni_dest)
+ {
+ written += utf16_ch_len;
+ continue;
+ }
+
+ for (j = 0; j < utf16_ch_len; j++)
+ {
+ if (uni_bytes_max >= sizeof(WCHAR))
+ {
+ *uni_dest++ = utf16_ch[j];
+ uni_bytes_max -= sizeof(WCHAR);
+ written++;
+ }
+ else
+ {
+ uni_bytes_max = 0;
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ }
+
+ *uni_bytes_written = written * sizeof(WCHAR);
+ return status;
+}
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
Index: reactos/drivers/filesystems/btrfs/btrfs.h
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.h (working copy)
@@ -0,0 +1,403 @@
+/* btrfs.h
+ * Generic btrfs header file. Thanks to whoever it was who wrote
+ * https://btrfs.wiki.kernel.org/index.php/On-disk_Format - you saved me a lot of time!
+ *
+ * I release this file, and this file only, into the public domain - do whatever
+ * you want with it. You don't have to, but I'd appreciate if you let me know if you
+ * use it anything cool - mark@harmstone.com. */
+
+#ifndef BTRFS_H_DEFINED
+#define BTRFS_H_DEFINED
+
+static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4000000000000, 0 };
+
+#define BTRFS_MAGIC 0x4d5f53665248425f
+#define MAX_LABEL_SIZE 0x100
+#define SUBVOL_ROOT_INODE 0x100
+
+#define TYPE_INODE_ITEM 0x01
+#define TYPE_INODE_REF 0x0C
+#define TYPE_INODE_EXTREF 0x0D
+#define TYPE_XATTR_ITEM 0x18
+#define TYPE_DIR_ITEM 0x54
+#define TYPE_DIR_INDEX 0x60
+#define TYPE_EXTENT_DATA 0x6C
+#define TYPE_EXTENT_CSUM 0x80
+#define TYPE_ROOT_ITEM 0x84
+#define TYPE_ROOT_BACKREF 0x90
+#define TYPE_ROOT_REF 0x9C
+#define TYPE_EXTENT_ITEM 0xA8
+#define TYPE_METADATA_ITEM 0xA9
+#define TYPE_TREE_BLOCK_REF 0xB0
+#define TYPE_EXTENT_DATA_REF 0xB2
+#define TYPE_EXTENT_REF_V0 0xB4
+#define TYPE_SHARED_BLOCK_REF 0xB6
+#define TYPE_SHARED_DATA_REF 0xB8
+#define TYPE_BLOCK_GROUP_ITEM 0xC0
+#define TYPE_DEV_EXTENT 0xCC
+#define TYPE_DEV_ITEM 0xD8
+#define TYPE_CHUNK_ITEM 0xE4
+
+#define BTRFS_ROOT_ROOT 1
+#define BTRFS_ROOT_EXTENT 2
+#define BTRFS_ROOT_CHUNK 3
+#define BTRFS_ROOT_DEVTREE 4
+#define BTRFS_ROOT_FSTREE 5
+#define BTRFS_ROOT_CHECKSUM 7
+
+#define BTRFS_COMPRESSION_NONE 0
+#define BTRFS_COMPRESSION_ZLIB 1
+#define BTRFS_COMPRESSION_LZO 2
+
+#define BTRFS_ENCRYPTION_NONE 0
+
+#define BTRFS_ENCODING_NONE 0
+
+#define EXTENT_TYPE_INLINE 0
+#define EXTENT_TYPE_REGULAR 1
+#define EXTENT_TYPE_PREALLOC 2
+
+#define BLOCK_FLAG_DATA 0x001
+#define BLOCK_FLAG_SYSTEM 0x002
+#define BLOCK_FLAG_METADATA 0x004
+#define BLOCK_FLAG_RAID0 0x008
+#define BLOCK_FLAG_RAID1 0x010
+#define BLOCK_FLAG_DUPLICATE 0x020
+#define BLOCK_FLAG_RAID10 0x040
+#define BLOCK_FLAG_RAID5 0x080
+#define BLOCK_FLAG_RAID6 0x100
+
+#define FREE_SPACE_CACHE_ID 0xFFFFFFFFFFFFFFF5
+#define EXTENT_CSUM_ID 0xFFFFFFFFFFFFFFF6
+
+#define BTRFS_INODE_NODATASUM 0x001
+#define BTRFS_INODE_NODATACOW 0x002
+#define BTRFS_INODE_READONLY 0x004
+#define BTRFS_INODE_NOCOMPRESS 0x008
+#define BTRFS_INODE_PREALLOC 0x010
+#define BTRFS_INODE_SYNC 0x020
+#define BTRFS_INODE_IMMUTABLE 0x040
+#define BTRFS_INODE_APPEND 0x080
+#define BTRFS_INODE_NODUMP 0x100
+#define BTRFS_INODE_NOATIME 0x200
+#define BTRFS_INODE_DIRSYNC 0x400
+#define BTRFS_INODE_COMPRESS 0x800
+
+#define BTRFS_SUBVOL_READONLY 0x1
+
+#define BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE 0x1
+
+#define BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF 0x0001
+#define BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL 0x0002
+#define BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS 0x0004
+#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO 0x0008
+#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZOV2 0x0010
+#define BTRFS_INCOMPAT_FLAGS_BIG_METADATA 0x0020
+#define BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF 0x0040
+#define BTRFS_INCOMPAT_FLAGS_RAID56 0x0080
+#define BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA 0x0100
+#define BTRFS_INCOMPAT_FLAGS_NO_HOLES 0x0200
+
+#pragma pack(push, 1)
+
+typedef struct {
+ UINT8 uuid[16];
+} BTRFS_UUID;
+
+typedef struct {
+ UINT64 obj_id;
+ UINT8 obj_type;
+ UINT64 offset;
+} KEY;
+
+#define HEADER_FLAG_MIXED_BACKREF 0x100000000000000
+#define HEADER_FLAG_SHARED_BACKREF 0x000000000000002
+
+typedef struct {
+ UINT8 csum[32];
+ BTRFS_UUID fs_uuid;
+ UINT64 address;
+ UINT64 flags;
+ BTRFS_UUID chunk_tree_uuid;
+ UINT64 generation;
+ UINT64 tree_id;
+ UINT32 num_items;
+ UINT8 level;
+} tree_header;
+
+typedef struct {
+ KEY key;
+ UINT32 offset;
+ UINT32 size;
+} leaf_node;
+
+typedef struct {
+ KEY key;
+ UINT64 address;
+ UINT64 generation;
+} internal_node;
+
+typedef struct {
+ UINT64 dev_id;
+ UINT64 num_bytes;
+ UINT64 bytes_used;
+ UINT32 optimal_io_align;
+ UINT32 optimal_io_width;
+ UINT32 minimal_io_size;
+ UINT64 type;
+ UINT64 generation;
+ UINT64 start_offset;
+ UINT32 dev_group;
+ UINT8 seek_speed;
+ UINT8 bandwidth;
+ BTRFS_UUID device_uuid;
+ BTRFS_UUID fs_uuid;
+} DEV_ITEM;
+
+#define SYS_CHUNK_ARRAY_SIZE 0x800
+
+typedef struct {
+ UINT8 checksum[32];
+ BTRFS_UUID uuid;
+ UINT64 sb_phys_addr;
+ UINT64 flags;
+ UINT64 magic;
+ UINT64 generation;
+ UINT64 root_tree_addr;
+ UINT64 chunk_tree_addr;
+ UINT64 log_tree_addr;
+ UINT64 log_root_transid;
+ UINT64 total_bytes;
+ UINT64 bytes_used;
+ UINT64 root_dir_objectid;
+ UINT64 num_devices;
+ UINT32 sector_size;
+ UINT32 node_size;
+ UINT32 leaf_size;
+ UINT32 stripe_size;
+ UINT32 n;
+ UINT64 chunk_root_generation;
+ UINT64 compat_flags;
+ UINT64 compat_ro_flags;
+ UINT64 incompat_flags;
+ UINT16 csum_type;
+ UINT8 root_level;
+ UINT8 chunk_root_level;
+ UINT8 log_root_level;
+ DEV_ITEM dev_item;
+ char label[MAX_LABEL_SIZE];
+ UINT64 cache_generation;
+ UINT64 uuid_tree_generation;
+ UINT64 reserved[30];
+ UINT8 sys_chunk_array[SYS_CHUNK_ARRAY_SIZE];
+// struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
+ UINT8 reserved2[1237];
+} superblock;
+
+#define BTRFS_TYPE_UNKNOWN 0
+#define BTRFS_TYPE_FILE 1
+#define BTRFS_TYPE_DIRECTORY 2
+#define BTRFS_TYPE_CHARDEV 3
+#define BTRFS_TYPE_BLOCKDEV 4
+#define BTRFS_TYPE_FIFO 5
+#define BTRFS_TYPE_SOCKET 6
+#define BTRFS_TYPE_SYMLINK 7
+#define BTRFS_TYPE_EA 8
+
+typedef struct {
+ KEY key;
+ UINT64 transid;
+ UINT16 m;
+ UINT16 n;
+ UINT8 type;
+ char name[1];
+} DIR_ITEM;
+
+typedef struct {
+ UINT64 seconds;
+ UINT32 nanoseconds;
+} BTRFS_TIME;
+
+typedef struct {
+ UINT64 generation;
+ UINT64 transid;
+ UINT64 st_size;
+ UINT64 st_blocks;
+ UINT64 block_group;
+ UINT32 st_nlink;
+ UINT32 st_uid;
+ UINT32 st_gid;
+ UINT32 st_mode;
+ UINT64 st_rdev;
+ UINT64 flags;
+ UINT64 sequence;
+ UINT8 reserved[32];
+ BTRFS_TIME st_atime;
+ BTRFS_TIME st_ctime;
+ BTRFS_TIME st_mtime;
+ BTRFS_TIME otime;
+} INODE_ITEM;
+
+typedef struct {
+ INODE_ITEM inode;
+ UINT64 generation;
+ UINT64 objid;
+ UINT64 block_number;
+ UINT64 byte_limit;
+ UINT64 bytes_used;
+ UINT64 last_snapshot_generation;
+ UINT64 flags;
+ UINT32 num_references;
+ KEY drop_progress;
+ UINT8 drop_level;
+ UINT8 root_level;
+ UINT64 generation2;
+ BTRFS_UUID uuid;
+ BTRFS_UUID parent_uuid;
+ BTRFS_UUID received_uuid;
+ UINT64 ctransid;
+ UINT64 otransid;
+ UINT64 stransid;
+ UINT64 rtransid;
+ BTRFS_TIME ctime;
+ BTRFS_TIME otime;
+ BTRFS_TIME stime;
+ BTRFS_TIME rtime;
+ UINT64 reserved[8];
+} ROOT_ITEM;
+
+typedef struct {
+ UINT64 size;
+ UINT64 root_id;
+ UINT64 stripe_length;
+ UINT64 type;
+ UINT32 opt_io_alignment;
+ UINT32 opt_io_width;
+ UINT32 sector_size;
+ UINT16 num_stripes;
+ UINT16 sub_stripes;
+} CHUNK_ITEM;
+
+typedef struct {
+ UINT64 dev_id;
+ UINT64 offset;
+ BTRFS_UUID dev_uuid;
+} CHUNK_ITEM_STRIPE;
+
+typedef struct {
+ UINT64 generation;
+ UINT64 decoded_size;
+ UINT8 compression;
+ UINT8 encryption;
+ UINT16 encoding;
+ UINT8 type;
+ UINT8 data[1];
+} EXTENT_DATA;
+
+typedef struct {
+ UINT64 address;
+ UINT64 size;
+ UINT64 offset;
+ UINT64 num_bytes;
+} EXTENT_DATA2;
+
+typedef struct {
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} INODE_REF;
+
+typedef struct {
+ UINT64 dir;
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} INODE_EXTREF;
+
+#define EXTENT_ITEM_DATA 0x001
+#define EXTENT_ITEM_TREE_BLOCK 0x002
+#define EXTENT_ITEM_SHARED_BACKREFS 0x100
+
+typedef struct {
+ UINT64 refcount;
+ UINT64 generation;
+ UINT64 flags;
+} EXTENT_ITEM;
+
+typedef struct {
+ UINT32 refcount;
+} EXTENT_ITEM_V0;
+
+typedef struct {
+ EXTENT_ITEM extent_item;
+ KEY firstitem;
+ UINT8 level;
+} EXTENT_ITEM_TREE;
+
+typedef struct {
+ UINT64 offset;
+} TREE_BLOCK_REF;
+
+typedef struct {
+ UINT64 root;
+ UINT64 objid;
+ UINT64 offset;
+ UINT32 count;
+} EXTENT_DATA_REF;
+
+typedef struct {
+ UINT64 used;
+ UINT64 chunk_tree;
+ UINT64 flags;
+} BLOCK_GROUP_ITEM;
+
+typedef struct {
+ UINT64 root;
+ UINT64 gen;
+ UINT64 objid;
+ UINT64 count;
+} EXTENT_REF_V0;
+
+typedef struct {
+ UINT64 offset;
+} SHARED_BLOCK_REF;
+
+typedef struct {
+ UINT64 offset;
+ UINT32 count;
+} SHARED_DATA_REF;
+
+#define FREE_SPACE_EXTENT 1
+#define FREE_SPACE_BITMAP 2
+
+typedef struct {
+ UINT64 offset;
+ UINT64 size;
+ UINT8 type;
+} FREE_SPACE_ENTRY;
+
+typedef struct {
+ KEY key;
+ UINT64 generation;
+ UINT64 num_entries;
+ UINT64 num_bitmaps;
+} FREE_SPACE_ITEM;
+
+typedef struct {
+ UINT64 dir;
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} ROOT_REF;
+
+typedef struct {
+ UINT64 chunktree;
+ UINT64 objid;
+ UINT64 address;
+ UINT64 length;
+ BTRFS_UUID chunktree_uuid;
+} DEV_EXTENT;
+
+#pragma pack(pop)
+
+#endif
Index: reactos/drivers/filesystems/btrfs/btrfs.rc
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.rc (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.rc (working copy)
@@ -0,0 +1,101 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904b0"
+ BEGIN
+ VALUE "FileDescription", "WinBtrfs"
+ VALUE "FileVersion", "0.1"
+ VALUE "InternalName", "btrfs"
+ VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
+ VALUE "OriginalFilename", "btrfs.sys"
+ VALUE "ProductName", "WinBtrfs"
+ VALUE "ProductVersion", "0.1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
Index: reactos/drivers/filesystems/btrfs/btrfs_drv.h
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs_drv.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs_drv.h (working copy)
@@ -0,0 +1,644 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifndef BTRFS_DRV_H_DEFINED
+#define BTRFS_DRV_H_DEFINED
+
+#ifndef __REACTOS__
+#undef _WIN32_WINNT
+#undef NTDDI_VERSION
+
+#define _WIN32_WINNT 0x0600
+#define NTDDI_VERSION 0x06010000 // Win 7
+#define _CRT_SECURE_NO_WARNINGS
+#endif /* __REACTOS__ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+#include
+#include
+#include
+#include
+#include
+#include "btrfs.h"
+
+#ifdef _DEBUG
+// #define DEBUG_TREE_REFCOUNTS
+// #define DEBUG_FCB_REFCOUNTS
+// #define DEBUG_LONG_MESSAGES
+#define DEBUG_PARANOID
+#define DEBUG_LEVEL 2 // 0 = nothing, 1 = ERR and FIXME, 2 = WARN, 3 = TRACE
+#else
+#define DEBUG_LEVEL 1
+#endif
+
+#define BTRFS_NODE_TYPE_CCB 0x2295
+#define BTRFS_NODE_TYPE_FCB 0x2296
+
+#define ALLOC_TAG 0x4D484274 //'MHBt'
+
+#define STDCALL __stdcall
+
+#define UID_NOBODY 65534
+#define GID_NOBODY 65534
+
+#define EA_NTACL "security.NTACL"
+#define EA_NTACL_HASH 0x45922146
+
+#define EA_DOSATTRIB "user.DOSATTRIB"
+#define EA_DOSATTRIB_HASH 0x914f9939
+
+#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
+
+#ifdef _MSC_VER
+#define try __try
+#define except __except
+#define finally __finally
+#else
+#define try if (1)
+#define except(x) if (0 && (x))
+#define finally if (1)
+#endif
+
+// #pragma pack(push, 1)
+
+struct device_extension;
+
+typedef struct {
+ PDEVICE_OBJECT devobj;
+ BTRFS_UUID fsuuid;
+ BTRFS_UUID devuuid;
+ UINT64 devnum;
+ LIST_ENTRY list_entry;
+} volume;
+
+typedef struct _fcb_nonpaged {
+ FAST_MUTEX HeaderMutex;
+ SECTION_OBJECT_POINTERS segment_object;
+ ERESOURCE resource;
+} fcb_nonpaged;
+
+struct _root;
+
+typedef struct _fcb {
+ FSRTL_ADVANCED_FCB_HEADER Header;
+ struct _fcb_nonpaged* nonpaged;
+ LONG refcount;
+ ULONG open_count;
+ UNICODE_STRING filepart;
+ ANSI_STRING utf8;
+ struct _device_extension* Vcb;
+ struct _fcb* par;
+ struct _fcb* prev;
+ struct _fcb* next;
+ struct _root* subvol;
+ LIST_ENTRY children;
+ UINT64 inode;
+ UINT8 type;
+ BOOL delete_on_close;
+ INODE_ITEM inode_item;
+ UNICODE_STRING full_filename;
+ ULONG name_offset;
+ SECURITY_DESCRIPTOR* sd;
+ FILE_LOCK lock;
+ BOOL deleted;
+ PKTHREAD lazy_writer_thread;
+ ULONG atts;
+
+ BOOL ads;
+ UINT32 adssize;
+ UINT32 adshash;
+ ANSI_STRING adsxattr;
+
+ LIST_ENTRY list_entry;
+} fcb;
+
+typedef struct _ccb {
+ USHORT NodeType;
+ CSHORT NodeSize;
+ ULONG disposition;
+ ULONG options;
+ UINT64 query_dir_offset;
+// char* query_string;
+ UNICODE_STRING query_string;
+ BOOL has_wildcard;
+ BOOL specific_file;
+} ccb;
+
+// typedef struct _log_to_phys {
+// UINT64 address;
+// UINT64 size;
+// UINT64 physaddr;
+// UINT32 sector_size;
+// struct _log_to_phys* next;
+// } log_to_phys;
+
+struct _device_extension;
+
+// enum tree_holder_status {
+// tree_holder_unloaded,
+// tree_holder_loading,
+// tree_holder_loaded,
+// tree_holder_unloading
+// };
+
+// typedef struct {
+// enum tree_holder_status status;
+// KSPIN_LOCK spin_lock;
+// ERESOURCE lock;
+// } tree_holder_nonpaged;
+
+typedef struct {
+ UINT64 address;
+ UINT64 generation;
+ struct _tree* tree;
+// tree_holder_nonpaged* nonpaged;
+} tree_holder;
+
+typedef struct _tree_data {
+ KEY key;
+ LIST_ENTRY list_entry;
+ BOOL ignore;
+ BOOL inserted;
+
+ union {
+ tree_holder treeholder;
+
+ struct {
+ UINT32 size;
+ UINT8* data;
+ };
+ };
+} tree_data;
+
+// typedef struct _tree_nonpaged {
+// ERESOURCE load_tree_lock;
+// } tree_nonpaged;
+
+typedef struct _tree {
+// UINT64 address;
+// UINT8 level;
+ tree_header header;
+ LONG refcount;
+ UINT32 size;
+ struct _device_extension* Vcb;
+ struct _tree* parent;
+ tree_data* paritem;
+ struct _root* root;
+// tree_nonpaged* nonpaged;
+ LIST_ENTRY itemlist;
+ LIST_ENTRY list_entry;
+ UINT64 new_address;
+ UINT64 flags;
+} tree;
+
+typedef struct {
+// KSPIN_LOCK load_tree_lock;
+ ERESOURCE load_tree_lock;
+} root_nonpaged;
+
+typedef struct _root {
+ UINT64 id;
+ tree_holder treeholder;
+ root_nonpaged* nonpaged;
+ UINT64 lastinode;
+ ROOT_ITEM root_item;
+
+ struct _root* prev;
+ struct _root* next;
+} root;
+
+typedef struct {
+ tree* tree;
+ tree_data* item;
+} traverse_ptr;
+
+typedef struct _tree_cache {
+ tree* tree;
+ BOOL write;
+ LIST_ENTRY list_entry;
+} tree_cache;
+
+typedef struct _root_cache {
+ root* root;
+ struct _root_cache* next;
+} root_cache;
+
+#define SPACE_TYPE_FREE 0
+#define SPACE_TYPE_USED 1
+#define SPACE_TYPE_DELETING 2
+#define SPACE_TYPE_WRITING 3
+
+typedef struct {
+ UINT64 offset;
+ UINT64 size;
+ UINT8 type;
+ LIST_ENTRY list_entry;
+} space;
+
+typedef struct {
+ UINT64 address;
+ UINT64 size;
+ BOOL provisional;
+ LIST_ENTRY listentry;
+} disk_hole;
+
+typedef struct {
+ PDEVICE_OBJECT devobj;
+ DEV_ITEM devitem;
+ LIST_ENTRY disk_holes;
+} device;
+
+typedef struct {
+ CHUNK_ITEM* chunk_item;
+ UINT32 size;
+ UINT64 offset;
+ UINT64 used;
+ UINT32 oldused;
+ BOOL space_changed;
+ device** devices;
+ LIST_ENTRY space;
+ LIST_ENTRY list_entry;
+} chunk;
+
+typedef struct {
+ KEY key;
+ void* data;
+ USHORT size;
+ LIST_ENTRY list_entry;
+} sys_chunk;
+
+typedef struct _device_extension {
+ device* devices;
+// DISK_GEOMETRY geometry;
+ UINT64 length;
+ superblock superblock;
+// WCHAR label[MAX_LABEL_SIZE];
+ BOOL readonly;
+ fcb* fcbs;
+ fcb* volume_fcb;
+ fcb* root_fcb;
+ ERESOURCE DirResource;
+ KSPIN_LOCK FcbListLock;
+ ERESOURCE fcb_lock;
+ ERESOURCE load_lock;
+ ERESOURCE tree_lock;
+ PNOTIFY_SYNC NotifySync;
+ LIST_ENTRY DirNotifyList;
+ LONG tree_lock_counter;
+ LONG open_trees;
+ ULONG write_trees;
+// ERESOURCE LogToPhysLock;
+// UINT64 chunk_root_phys_addr;
+ UINT64 root_tree_phys_addr;
+// log_to_phys* log_to_phys;
+ root* roots;
+ root* chunk_root;
+ root* root_root;
+ root* extent_root;
+ root* checksum_root;
+ root* dev_root;
+ BOOL log_to_phys_loaded;
+ UINT32 max_inline;
+ LIST_ENTRY sys_chunks;
+ LIST_ENTRY chunks;
+ LIST_ENTRY trees;
+ LIST_ENTRY tree_cache;
+ HANDLE flush_thread_handle;
+ KTIMER flush_thread_timer;
+} device_extension;
+
+typedef struct {
+ LIST_ENTRY listentry;
+ PSID sid;
+ UINT32 uid;
+} uid_map;
+
+// #pragma pack(pop)
+
+static __inline void init_tree_holder(tree_holder* th) {
+// th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG);
+// KeInitializeSpinLock(&th->nonpaged->spin_lock);
+// ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later
+}
+
+static __inline void* map_user_buffer(PIRP Irp) {
+ if (!Irp->MdlAddress) {
+ return Irp->UserBuffer;
+ } else {
+ return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+ }
+}
+
+static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
+ return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
+}
+
+static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) {
+ ULONGLONG l = t.QuadPart - 116444736000000000;
+
+ out->seconds = l / 10000000;
+ out->nanoseconds = (l % 10000000) * 100;
+}
+
+// in btrfs.c
+device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
+ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
+int keycmp(const KEY* key1, const KEY* key2);
+NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
+void STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen);
+BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32);
+void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize);
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8);
+UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index);
+void delete_fcb(fcb* fcb, PFILE_OBJECT FileObject);
+fcb* create_fcb();
+void protect_superblocks(device_extension* Vcb, chunk* c);
+BOOL is_top_level(PIRP Irp);
+
+#ifdef _MSC_VER
+#define funcname __FUNCTION__
+#else
+#define funcname __func__
+#endif
+
+// FIXME - we probably shouldn't be moving funcname etc. around if we're not printing debug messages
+#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
+
+#ifdef DEBUG_LONG_MESSAGES
+
+#if DEBUG_LEVEL >= 3
+#define TRACE(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define TRACE(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 2
+#define WARN(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define WARN(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 1
+#define FIXME(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#define ERR(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define FIXME(s, ...)
+#define ERR(s, ...)
+#endif
+
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...);
+
+#else
+
+#if DEBUG_LEVEL >= 3
+#define TRACE(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define TRACE(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 2
+#define WARN(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define WARN(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 1
+#define FIXME(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#define ERR(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define FIXME(s, ...)
+#define ERR(s, ...)
+#endif
+
+void STDCALL _debug_message(const char* func, char* s, ...);
+
+#endif
+
+// in fastio.c
+void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
+
+// in crc32c.c
+UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
+
+// in treefuncs.c
+BOOL STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _get_item(device_extension* Vcb, root* r, UINT64 objid, UINT8 objtype, UINT64 offset, void* ptr, UINT32 size, const char* func, const char* file, unsigned int line);
+void STDCALL free_tree_cache(LIST_ENTRY* tc);
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp);
+void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp);
+void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write);
+tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
+tree* STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, const char* func, const char* file, unsigned int line);
+BOOL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, const char* func, const char* file, unsigned int line);
+
+#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
+#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
+#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
+#define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
+#define load_tree(t, addr, r) _load_tree(t, addr, r, funcname, __FILE__, __LINE__)
+#define free_traverse_ptr(tp) _free_traverse_ptr(tp, funcname, __FILE__, __LINE__)
+#define get_item(Vcb, r, objid, objtype, offset, ptr, size) _get_item(Vcb, r, objid, objtype, offset, ptr, size, funcname, __FILE__, __LINE__)
+#define do_load_tree(Vcb, th, r, t, td) _do_load_tree(Vcb, th, r, t, td, funcname, __FILE__, __LINE__)
+
+// in search.c
+void STDCALL look_for_vols(LIST_ENTRY* volumes);
+
+// in cache.c
+void STDCALL init_cache();
+void STDCALL free_cache();
+extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
+
+// in write.c
+NTSTATUS STDCALL do_write(device_extension* Vcb);
+NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache);
+NTSTATUS truncate_file(fcb* fcb, UINT64 end);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list);
+void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
+NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length);
+NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset);
+NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list);
+void print_trees(LIST_ENTRY* tc);
+chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
+void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type);
+NTSTATUS consider_write(device_extension* Vcb);
+
+// in dirctrl.c
+NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+
+// in security.c
+NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+void fcb_get_sd(fcb* fcb);
+// UINT32 STDCALL get_uid();
+void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
+NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as);
+
+// in fileinfo.c
+NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8);
+
+// in reparse.c
+BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject);
+NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+
+// in create.c
+NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent);
+BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol,
+ UINT64* inode, UINT8* type, PANSI_STRING utf8);
+
+// in fsctl.c
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
+
+// in flushthread.c
+void STDCALL flush_thread(void* context);
+
+static __inline void print_open_trees(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
+ tree_data* td = CONTAINING_RECORD(t->itemlist.Flink, tree_data, list_entry);
+ ERR("tree %p: root %llx, level %u, refcount %u, first key (%llx,%x,%llx)\n",
+ t, t->root->id, t->header.level, t->refcount, td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ le = le->Flink;
+ }
+}
+
+static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* before) {
+ item->Flink = before->Flink;
+ before->Flink = item;
+ item->Blink = before;
+
+ if (item->Flink != head)
+ item->Flink->Blink = item;
+ else
+ head->Blink = item;
+}
+
+#ifdef _MSC_VER
+// #define int3 __asm { int 3 }
+#define int3 __debugbreak()
+#else
+#define int3 asm("int3;")
+#endif
+
+#define acquire_tree_lock(Vcb, exclusive) {\
+ LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \
+ ref = ref; \
+ if (exclusive) { \
+ TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \
+ TRACE("open tree count = %i\n", Vcb->open_trees); \
+ } else { \
+ TRACE("getting tree_lock %u->%u\n", ref-1, ref); \
+ ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \
+ } \
+}
+
+// if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; }
+// else TRACE("open tree count = %i\n", Vcb->open_trees);
+
+// FIXME - find a way to catch unfreed trees again
+
+#define release_tree_lock(Vcb, exclusive) {\
+ LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \
+ ref = ref; \
+ TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \
+ if (exclusive) {\
+ TRACE("open tree count = %i\n", Vcb->open_trees); \
+ } \
+ ExReleaseResourceLite(&Vcb->tree_lock); \
+}
+
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+#define _increase_tree_rc(t, func, file, line) { \
+ LONG rc = InterlockedIncrement(&t->refcount); \
+ _debug_message(func, file, line, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
+}
+#else
+#define _increase_tree_rc(t, func, file, line) { \
+ LONG rc = InterlockedIncrement(&t->refcount); \
+ _debug_message(func, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
+}
+#endif
+#define increase_tree_rc(t) _increase_tree_rc(t, funcname, __FILE__, __LINE__)
+#else
+#define increase_tree_rc(t) InterlockedIncrement(&t->refcount);
+#define _increase_tree_rc(t, func, file, line) increase_tree_rc(t)
+#endif
+
+// from sys/stat.h
+#define __S_IFMT 0170000 /* These bits determine file type. */
+#define __S_IFDIR 0040000 /* Directory. */
+#define __S_IFCHR 0020000 /* Character device. */
+#define __S_IFBLK 0060000 /* Block device. */
+#define __S_IFREG 0100000 /* Regular file. */
+#define __S_IFIFO 0010000 /* FIFO. */
+#define __S_IFLNK 0120000 /* Symbolic link. */
+#define __S_IFSOCK 0140000 /* Socket. */
+#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 0000100
+#endif
+
+#ifdef __REACTOS__
+#define S_IFDIR __S_IFDIR
+#define S_IFREG __S_IFREG
+#endif /* __REACTOS__ */
+
+#ifndef S_IXGRP
+#define S_IXGRP (S_IXUSR >> 3)
+#endif
+
+#ifndef S_IXOTH
+#define S_IXOTH (S_IXGRP >> 3)
+#endif
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+NTSTATUS WINAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+ ULONG *utf8_bytes_written,
+ const WCHAR *uni_src, ULONG uni_bytes);
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+ ULONG *uni_bytes_written,
+ const CHAR *utf8_src, ULONG utf8_bytes);
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
+
+#endif
Index: reactos/drivers/filesystems/btrfs/cache.c
===================================================================
--- reactos/drivers/filesystems/btrfs/cache.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/cache.c (working copy)
@@ -0,0 +1,62 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+#include
+
+CACHE_MANAGER_CALLBACKS* cache_callbacks;
+
+static BOOLEAN STDCALL acquire_for_lazy_write(PVOID Context, BOOLEAN Wait) {
+ fcb* fcb = Context;
+
+ TRACE("(%p, %u)\n", Context, Wait);
+
+ fcb->lazy_writer_thread = KeGetCurrentThread();
+
+ return TRUE;
+}
+
+static void STDCALL release_from_lazy_write(PVOID Context) {
+ fcb* fcb = Context;
+
+ TRACE("(%p)\n", Context);
+
+ fcb->lazy_writer_thread = NULL;
+}
+
+static BOOLEAN STDCALL acquire_for_read_ahead(PVOID Context, BOOLEAN Wait) {
+ TRACE("(%p, %u)\n", Context, Wait);
+
+ return TRUE;
+}
+
+static void STDCALL release_from_read_ahead(PVOID Context) {
+ TRACE("(%p)\n", Context);
+}
+
+void STDCALL init_cache() {
+ cache_callbacks = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_MANAGER_CALLBACKS), ALLOC_TAG);
+
+ 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;
+}
+
+void STDCALL free_cache() {
+ ExFreePool(cache_callbacks);
+}
Index: reactos/drivers/filesystems/btrfs/crc32c.c
===================================================================
--- reactos/drivers/filesystems/btrfs/crc32c.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/crc32c.c (working copy)
@@ -0,0 +1,105 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include
+#ifndef __REACTOS__
+#include
+
+extern BOOL have_sse42;
+#endif
+static const UINT32 crctable[] = {
+ 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
+ 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
+ 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
+ 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
+ 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
+ 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
+ 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
+ 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
+ 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
+ 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
+ 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
+ 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
+ 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
+ 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
+ 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
+ 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
+ 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
+ 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
+ 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
+ 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
+ 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
+ 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
+ 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
+ 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
+ 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
+ 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
+ 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
+ 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
+ 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
+ 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
+ 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
+ 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
+};
+
+// HW code taken from https://github.com/rurban/smhasher/blob/master/crc32_hw.c
+#ifndef __REACTOS__
+#define ALIGN_SIZE 0x08UL
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+#define CALC_CRC(op, crc, type, buf, len) \
+ do { \
+ for (; (len) >= sizeof (type); (len) -= sizeof(type), buf += sizeof (type)) { \
+ (crc) = op((crc), *(type *) (buf)); \
+ } \
+ } while(0)
+
+static UINT32 crc32c_hw(const void *input, int len, UINT32 crc) {
+ const char* buf = (const char*)input;
+
+ for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) {
+ crc = _mm_crc32_u8(crc, *buf);
+ }
+
+#ifdef __x86_64__
+ CALC_CRC(_mm_crc32_u64, crc, UINT64, buf, len);
+#endif
+ CALC_CRC(_mm_crc32_u32, crc, UINT32, buf, len);
+ CALC_CRC(_mm_crc32_u16, crc, UINT16, buf, len);
+ CALC_CRC(_mm_crc32_u8, crc, UINT8, buf, len);
+
+ return crc;
+}
+#endif
+UINT32 __stdcall calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen) {
+ UINT32 rem;
+ ULONG i;
+
+#ifndef __REACTOS__
+ if (have_sse42) {
+ return crc32c_hw(msg, msglen, seed);
+ } else {
+#endif
+ rem = seed;
+
+ for (i = 0; i < msglen; i++) {
+ rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
+ }
+#ifndef __REACTOS__
+ }
+#endif
+ return rem;
+}
Index: reactos/drivers/filesystems/btrfs/create.c
===================================================================
--- reactos/drivers/filesystems/btrfs/create.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/create.c (working copy)
@@ -0,0 +1,2007 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifndef __REACTOS__
+#include
+#endif
+#include "btrfs_drv.h"
+
+extern PDEVICE_OBJECT devobj;
+
+BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r,
+ UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
+ DIR_ITEM* di;
+ KEY searchkey;
+ traverse_ptr tp, tp2, next_tp;
+ BOOL b;
+ NTSTATUS Status;
+ ULONG stringlen;
+
+ TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ return FALSE;
+ }
+
+ TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ UINT32 size = tp.item->size;
+
+ // found by hash
+
+ di = (DIR_ITEM*)tp.item->data;
+
+ while (size > 0) {
+ size -= sizeof(DIR_ITEM) - sizeof(char);
+ size -= di->n;
+ size -= di->m;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+ UNICODE_STRING us;
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)stringlen;
+
+ if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
+ if (di->key.obj_type == TYPE_ROOT_ITEM) {
+ root* fcbroot = Vcb->roots;
+ while (fcbroot && fcbroot->id != di->key.obj_id)
+ fcbroot = fcbroot->next;
+
+ *subvol = fcbroot;
+ *inode = SUBVOL_ROOT_INODE;
+ *type = BTRFS_TYPE_DIRECTORY;
+ } else {
+ *subvol = r;
+ *inode = di->key.obj_id;
+ *type = di->type;
+ }
+
+ if (utf8) {
+ utf8->MaximumLength = di->n;
+ utf8->Length = utf8->MaximumLength;
+ utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(utf8->Buffer, di->name, di->n);
+ }
+
+ free_traverse_ptr(&tp);
+ ExFreePool(utf16);
+
+ TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+
+ return TRUE;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ di = (DIR_ITEM*)&di->name[di->n + di->m];
+ }
+ }
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 2;
+
+ if (!find_item(Vcb, r, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (keycmp(&tp.item->key, &searchkey) == -1) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ b = TRUE;
+ do {
+ TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ di = (DIR_ITEM*)tp.item->data;
+
+ TRACE("%.*s\n", di->n, di->name);
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+ UNICODE_STRING us;
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)stringlen;
+
+ if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
+ if (di->key.obj_type == TYPE_ROOT_ITEM) {
+ root* fcbroot = Vcb->roots;
+ while (fcbroot && fcbroot->id != di->key.obj_id)
+ fcbroot = fcbroot->next;
+
+ *subvol = fcbroot;
+ *inode = SUBVOL_ROOT_INODE;
+ *type = BTRFS_TYPE_DIRECTORY;
+ } else {
+ *subvol = r;
+ *inode = di->key.obj_id;
+ *type = di->type;
+ }
+ TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+
+ if (utf8) {
+ utf8->MaximumLength = di->n;
+ utf8->Length = utf8->MaximumLength;
+ utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(utf8->Buffer, di->name, di->n);
+ }
+
+ free_traverse_ptr(&tp);
+ ExFreePool(utf16);
+
+ return TRUE;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+}
+
+fcb* create_fcb() {
+ fcb* fcb;
+
+ fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("allocating fcb %p\n", fcb);
+#endif
+ RtlZeroMemory(fcb, sizeof(struct _fcb));
+
+ fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
+ fcb->Header.NodeByteSize = sizeof(struct _fcb);
+
+ fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
+ RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
+
+ ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
+ FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
+
+ fcb->refcount = 1;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
+#endif
+
+ ExInitializeResourceLite(&fcb->nonpaged->resource);
+ fcb->Header.Resource = &fcb->nonpaged->resource;
+
+ FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
+
+ InitializeListHead(&fcb->children);
+
+ return fcb;
+}
+
+static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
+ UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
+ char* fn;
+ UINT32 crc32;
+ BOOL ret;
+ ULONG utf8len;
+ NTSTATUS Status;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
+ if (!NT_SUCCESS(Status)) {
+ ExFreePool(fn);
+ ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ TRACE("%.*s\n", utf8len, fn);
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
+ TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
+
+ ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8);
+
+ return ret;
+}
+
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, UINT32* hash, PANSI_STRING xattr) {
+ NTSTATUS Status;
+ ULONG utf8len;
+ char* utf8;
+ UINT32 crc32;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL success = FALSE, b;
+
+ static char xapref[] = "user.";
+ ULONG xapreflen = strlen(xapref);
+
+ TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ TRACE("utf8len = %u\n", utf8len);
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
+ RtlCopyMemory(utf8, xapref, xapreflen);
+
+ Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+ goto end;
+ }
+
+ utf8len += xapreflen;
+ utf8[utf8len] = 0;
+
+ TRACE("utf8 = %s\n", utf8);
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
+ TRACE("crc32 = %08x\n", crc32);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto end;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ ULONG len = tp.item->size, xasize;
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+
+ TRACE("found match on hash\n");
+
+ while (len > 0) {
+ if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
+ TRACE("found exact match for %s\n", utf8);
+
+ *size = di->m;
+ *hash = tp.item->key.offset;
+
+ xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
+ xattr->Length = xattr->MaximumLength = di->n;
+ RtlCopyMemory(xattr->Buffer, di->name, di->n);
+ xattr->Buffer[di->n] = 0;
+
+ free_traverse_ptr(&tp);
+
+ success = TRUE;
+ goto end;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
+
+ if (len > xasize) {
+ len -= xasize;
+ di = (DIR_ITEM*)&di->name[di->m + di->n];
+ } else
+ break;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.offset = 0;
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
+ ULONG len = tp.item->size, xasize;
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ ULONG utf16len;
+
+ TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
+
+ while (len > 0) {
+ if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
+ TRACE("found potential xattr %.*s\n", di->n, di->name);
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ UNICODE_STRING us;
+
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)utf16len;
+
+ if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
+ TRACE("found case-insensitive match for %s\n", utf8);
+
+ *newstreamname = us;
+ *size = di->m;
+ *hash = tp.item->key.offset;
+
+ xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
+ xattr->Length = xattr->MaximumLength = di->n;
+ RtlCopyMemory(xattr->Buffer, di->name, di->n);
+ xattr->Buffer[di->n] = 0;
+
+ free_traverse_ptr(&tp);
+
+ success = TRUE;
+ goto end;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
+
+ if (len > xasize) {
+ len -= xasize;
+ di = (DIR_ITEM*)&di->name[di->m + di->n];
+ } else
+ break;
+ }
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+end:
+ ExFreePool(utf8);
+
+ return success;
+}
+
+static void split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
+ ULONG len, i, j, np;
+ BOOL has_stream;
+ UNICODE_STRING* ps;
+ WCHAR* buf;
+
+ np = 1;
+
+ len = path->Length / sizeof(WCHAR);
+ if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
+ len--;
+
+ has_stream = FALSE;
+ for (i = 0; i < len; i++) {
+ if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+ np++;
+ has_stream = FALSE;
+ } else if (path->Buffer[i] == ':') {
+ has_stream = TRUE;
+ }
+ }
+
+ if (has_stream)
+ np++;
+
+ ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
+ RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
+
+ buf = path->Buffer;
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+ ps[j].Buffer = buf;
+ ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ buf = &path->Buffer[i+1];
+ j++;
+ }
+ }
+
+ ps[j].Buffer = buf;
+ ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ if (has_stream) {
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ UNICODE_STRING dsus;
+
+ dsus.Buffer = datasuf;
+ dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+
+ for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
+ if (ps[j].Buffer[i] == ':') {
+ ps[j+1].Buffer = &ps[j].Buffer[i+1];
+ ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
+
+ ps[j].Length = i * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ j++;
+
+ break;
+ }
+ }
+
+ // FIXME - should comparison be case-insensitive?
+ // remove :$DATA suffix
+ if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
+ ps[j].Length -= dsus.Length;
+
+ if (ps[j].Length == 0) {
+ np--;
+ has_stream = FALSE;
+ }
+ }
+
+ // if path is just stream name, remove first empty item
+ if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
+ ps[0] = ps[1];
+ np--;
+ }
+
+// for (i = 0; i < np; i++) {
+// ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
+// }
+
+ *num_parts = np;
+ *parts = ps;
+ *stream = has_stream;
+}
+
+static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
+ LIST_ENTRY* le;
+ fcb *c, *deleted = NULL;
+
+ le = dir->children.Flink;
+ while (le != &dir->children) {
+ c = CONTAINING_RECORD(le, fcb, list_entry);
+
+ if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
+ if (c->deleted) {
+ deleted = c;
+ } else {
+ c->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+#endif
+ return c;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return deleted;
+}
+
+#ifdef DEBUG_FCB_REFCOUNTS
+static void print_fcbs(device_extension* Vcb) {
+ fcb* fcb = Vcb->fcbs;
+
+ while (fcb) {
+ ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
+
+ fcb = fcb->next;
+ }
+}
+#endif
+
+NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
+ fcb *dir, *sf, *sf2;
+ ULONG i, num_parts;
+ UNICODE_STRING fnus2;
+ UNICODE_STRING* parts = NULL;
+ BOOL has_stream;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ print_fcbs(Vcb);
+#endif
+
+ fnus2 = *fnus;
+
+ if (fnus2.Length < sizeof(WCHAR) && !relatedfcb) {
+ ERR("error - fnus was too short\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (relatedfcb) {
+ dir = relatedfcb;
+ } else {
+ if (fnus2.Buffer[0] != '\\') {
+ ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (fnus2.Length == sizeof(WCHAR)) {
+ *pfcb = Vcb->root_fcb;
+ Vcb->root_fcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, Vcb->root_fcb->refcount);
+#endif
+ return STATUS_SUCCESS;
+ }
+
+ dir = Vcb->root_fcb;
+
+ fnus2.Buffer++;
+ fnus2.Length -= sizeof(WCHAR);
+ fnus2.MaximumLength -= sizeof(WCHAR);
+ }
+
+ if (dir->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
+ WARN("passed relatedfcb which isn't a directory (%.*S) (fnus = %.*S)\n",
+ relatedfcb->full_filename.Length / sizeof(WCHAR), relatedfcb->full_filename.Buffer, fnus->Length / sizeof(WCHAR), fnus->Buffer);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (fnus->Length == 0) {
+ num_parts = 0;
+ } else {
+ split_path(&fnus2, &parts, &num_parts, &has_stream);
+ }
+
+ // FIXME - handle refcounts(?)
+ sf = dir;
+ dir->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", dir, dir->refcount, dir->full_filename.Length / sizeof(WCHAR), dir->full_filename.Buffer);
+#endif
+
+ if (parent) {
+ num_parts--;
+
+ if (has_stream) {
+ num_parts--;
+ has_stream = FALSE;
+ }
+ }
+
+ if (num_parts == 0) {
+ Status = STATUS_SUCCESS;
+ *pfcb = dir;
+ goto end2;
+ }
+
+ for (i = 0; i < num_parts; i++) {
+ BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
+
+ sf2 = search_fcb_children(sf, &parts[i]);
+
+ if (sf2 && sf2->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
+ WARN("passed path including file as subdirectory\n");
+
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ }
+
+ if (!sf2) {
+ if (has_stream && i == num_parts - 1) {
+ UNICODE_STRING streamname;
+ ANSI_STRING xattr;
+ UINT32 streamsize, streamhash;
+
+ streamname.Buffer = NULL;
+ streamname.Length = streamname.MaximumLength = 0;
+ xattr.Buffer = NULL;
+ xattr.Length = xattr.MaximumLength = 0;
+
+ if (!find_stream(Vcb, sf, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
+ WARN("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ } else {
+ ULONG fnlen;
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ if (streamname.Buffer) // case has changed
+ sf2->filepart = streamname;
+ else {
+ sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
+ }
+
+ sf2->par = sf;
+
+ sf->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
+#endif
+
+ sf2->subvol = sf->subvol;
+ sf2->inode = sf->inode;
+ sf2->type = sf->type;
+ sf2->ads = TRUE;
+ sf2->adssize = streamsize;
+ sf2->adshash = streamhash;
+ sf2->adsxattr = xattr;
+
+ TRACE("stream found: size = %x, hash = %08x\n", sf2->adssize, sf2->adshash);
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
+
+ if (sf != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
+
+ sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
+
+ sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':';
+
+ RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
+
+ // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+
+ TRACE("found stream %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, sf->subvol);
+
+ InsertTailList(&sf->children, &sf2->list_entry);
+ }
+ } else {
+ root* subvol;
+ UINT64 inode;
+ UINT8 type;
+ ANSI_STRING utf8;
+
+ if (!find_file_in_dir(Vcb, &parts[i], sf->subvol, sf->inode, &subvol, &inode, &type, &utf8)) {
+ WARN("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
+
+ Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ } else if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
+ WARN("passed path including file as subdirectory\n");
+
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ } else {
+ ULONG fnlen, strlen;
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ }
+ }
+
+ sf2->par = sf;
+
+ sf->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
+#endif
+
+ sf2->subvol = subvol;
+ sf2->inode = inode;
+ sf2->type = type;
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
+
+ if (sf != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
+
+ sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
+
+ if (sf != Vcb->root_fcb)
+ sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
+
+ sf2->utf8 = utf8;
+
+ if (!get_item(sf2->Vcb, sf2->subvol, sf2->inode, TYPE_INODE_ITEM, 0, &sf2->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find (%llx,%x,0) in subvol %llx\n", sf2->inode, TYPE_INODE_ITEM, sf2->subvol->id);
+ }
+
+ sf2->atts = get_file_attributes(Vcb, &sf2->inode_item, sf2->subvol, sf2->inode, sf2->type, sf2->filepart.Buffer[0] == '.', FALSE);
+
+ fcb_get_sd(sf2);
+
+ TRACE("found %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, subvol);
+
+ InsertTailList(&sf->children, &sf2->list_entry);
+ }
+ }
+ }
+
+ if (i == num_parts - 1)
+ break;
+
+ free_fcb(sf);
+ sf = sf2;
+ }
+
+ Status = STATUS_SUCCESS;
+ *pfcb = sf2;
+
+end:
+ free_fcb(sf);
+
+end2:
+ if (parts)
+ ExFreePool(parts);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ print_fcbs(Vcb);
+#endif
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL attach_fcb_to_fileobject(device_extension* Vcb, fcb* fcb, PFILE_OBJECT FileObject) {
+ FileObject->FsContext = fcb;
+// FileObject->FsContext2 = 0x0badc0de;//NULL;
+
+ // FIXME - cache stuff
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, fcb* parfcb, ULONG options, fcb** pfcb) {
+ NTSTATUS Status;
+ fcb* fcb;
+ ULONG utf8len;
+ char* utf8 = NULL;
+ UINT32 crc32;
+ UINT64 dirpos, inode;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM *dirii, *ii;
+ UINT8 type;
+ ULONG disize;
+ DIR_ITEM *di, *di2;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ANSI_STRING utf8as;
+ ULONG defda;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
+ if (!NT_SUCCESS(Status)) {
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ utf8[utf8len] = 0;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
+
+ dirpos = find_next_dir_index(Vcb, parfcb->subvol, parfcb->inode);
+ if (dirpos == 0) {
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfcb->inode_item.st_size += utf8len * 2;
+ TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfcb->inode_item.transid = Vcb->superblock.generation;
+ parfcb->inode_item.sequence++;
+ parfcb->inode_item.st_ctime = now;
+ parfcb->inode_item.st_mtime = now;
+
+ searchkey.obj_id = parfcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parfcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfcb->inode, parfcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ free_traverse_ptr(&tp);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(dirii, &parfcb->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(Vcb, &tp);
+
+ insert_tree_item(Vcb, parfcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ if (parfcb->subvol->lastinode == 0)
+ get_last_inode(Vcb, parfcb->subvol);
+
+ inode = parfcb->subvol->lastinode + 1;
+
+ type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
+
+ disize = sizeof(DIR_ITEM) - 1 + utf8len;
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = (UINT16)utf8len;
+ di->type = type;
+ RtlCopyMemory(di->name, utf8, utf8len);
+
+ insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL);
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(Vcb, parfcb->subvol, parfcb->inode, crc32, di2, disize);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ // FIXME - handle Irp->Overlay.AllocationSize
+
+ utf8as.Buffer = utf8;
+ utf8as.Length = utf8as.MaximumLength = utf8len;
+
+ Status = add_inode_ref(Vcb, parfcb->subvol, inode, parfcb->inode, dirpos, &utf8as);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
+
+ TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
+
+ IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
+
+ defda = 0;
+
+ if (utf8[0] == '.')
+ defda |= FILE_ATTRIBUTE_HIDDEN;
+
+ if (options & FILE_DIRECTORY_FILE) {
+ defda |= FILE_ATTRIBUTE_DIRECTORY;
+ IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ TRACE("defda = %x\n", defda);
+
+ if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
+ IrpSp->Parameters.Create.FileAttributes = defda;
+
+ if (IrpSp->Parameters.Create.FileAttributes != defda) {
+ char val[64];
+
+ sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
+
+ set_xattr(Vcb, parfcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ }
+
+ parfcb->subvol->lastinode++;
+
+ fcb = create_fcb();
+ fcb->Vcb = Vcb;
+
+ RtlZeroMemory(&fcb->inode_item, sizeof(INODE_ITEM));
+ fcb->inode_item.generation = Vcb->superblock.generation;
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_size = 0;
+ fcb->inode_item.st_blocks = 0;
+ fcb->inode_item.block_group = 0;
+ fcb->inode_item.st_nlink = 1;
+// fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
+ fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
+ fcb->inode_item.st_mode = parfcb ? (parfcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
+ fcb->inode_item.st_rdev = 0;
+ fcb->inode_item.flags = 0;
+ fcb->inode_item.sequence = 1;
+ fcb->inode_item.st_atime = now;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+ fcb->inode_item.otime = now;
+
+ if (type == BTRFS_TYPE_DIRECTORY)
+ fcb->inode_item.st_mode |= S_IFDIR;
+ else {
+ fcb->inode_item.st_mode |= S_IFREG;
+ fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
+ }
+
+ // inherit nodatacow flag from parent directory
+ if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+ fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+
+ if (type != BTRFS_TYPE_DIRECTORY)
+ fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+ }
+
+// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+
+ fcb->atts = IrpSp->Parameters.Create.FileAttributes;
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ fcb->par = parfcb;
+ parfcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+#endif
+ fcb->subvol = parfcb->subvol;
+ fcb->inode = inode;
+ fcb->type = type;
+
+ fcb->utf8.MaximumLength = fcb->utf8.Length = utf8len;
+ fcb->utf8.Buffer = utf8;
+
+ Status = fcb_get_new_sd(fcb, IrpSp->Parameters.Create.SecurityContext->AccessState);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("fcb_get_new_sd returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ fcb->filepart = *fpus;
+
+ set_xattr(Vcb, parfcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd));
+
+ fcb->full_filename.Length = parfcb->full_filename.Length + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fcb->filepart.Length;
+ fcb->full_filename.MaximumLength = fcb->full_filename.Length;
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.Length, ALLOC_TAG);
+ RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
+
+ if (parfcb->full_filename.Length > sizeof(WCHAR))
+ fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[(parfcb->full_filename.Length / sizeof(WCHAR)) + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], fcb->filepart.Buffer, fcb->filepart.Length);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, parfcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ *pfcb = fcb;
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+ TRACE("created new file %.*S in subvol %llx, inode %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->subvol->id, fcb->inode);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options) {
+ NTSTATUS Status;
+ fcb *fcb, *parfcb = NULL;
+ ULONG i, j;
+ ULONG utf8len;
+ ccb* ccb;
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ UNICODE_STRING dsus, fpus, stream;
+
+ TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ dsus.Buffer = datasuf;
+ dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+ fpus.Buffer = NULL;
+
+ // FIXME - apparently you can open streams using RelatedFileObject. How can we test this?
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &parfcb, fnus, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, TRUE);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ }
+
+ if (parfcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ i = (fnus->Length / sizeof(WCHAR))-1;
+ while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
+
+ j = i;
+
+ while (fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/' && i > 0) { i--; }
+
+ fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
+ fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
+ fpus.Length = (j - i + 1) * sizeof(WCHAR);
+
+ RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
+ fpus.Buffer[j - i + 1] = 0;
+
+ if (fpus.Length > dsus.Length) { // check for :$DATA suffix
+ UNICODE_STRING lb;
+
+ lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
+ lb.Length = lb.MaximumLength = dsus.Length;
+
+ TRACE("lb = %.*S\n", lb.Length/sizeof(WCHAR), lb.Buffer);
+
+ if (FsRtlAreNamesEqual(&dsus, &lb, TRUE, NULL)) {
+ TRACE("ignoring :$DATA suffix\n");
+
+ fpus.Length -= lb.Length;
+
+ if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
+ fpus.Length -= sizeof(WCHAR);
+
+ TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
+ }
+ }
+
+ stream.Length = 0;
+
+ for (i = 0; i < fpus.Length/sizeof(WCHAR); i++) {
+ if (fpus.Buffer[i] == ':') {
+ stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
+ stream.Buffer = &fpus.Buffer[i+1];
+ fpus.Buffer[i] = 0;
+ fpus.Length = i * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if (stream.Length > 0) {
+ struct _fcb* newpar;
+ static char xapref[] = "user.";
+ ULONG xapreflen = strlen(xapref), fnlen;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
+ TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &newpar, &fpus, parfcb, FALSE);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &newpar);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("file_create2 returned %08x\n", Status);
+ goto end;
+ }
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("get_fcb returned %08x\n", Status);
+ goto end;
+ }
+
+ free_fcb(parfcb);
+ parfcb = newpar;
+
+ if (newpar->type != BTRFS_TYPE_FILE && newpar->type != BTRFS_TYPE_SYMLINK) {
+ WARN("parent not file or symlink\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (options & FILE_DIRECTORY_FILE) {
+ WARN("tried to create directory as stream\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ fcb = create_fcb();
+ fcb->Vcb = Vcb;
+
+// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ fcb->par = parfcb;
+ parfcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+#endif
+ fcb->subvol = parfcb->subvol;
+ fcb->inode = parfcb->inode;
+ fcb->type = parfcb->type;
+
+ fcb->ads = TRUE;
+ fcb->adssize = 0;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ fcb->adsxattr.Length = utf8len + xapreflen;
+ fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
+ fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+
+ Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
+ if (!NT_SUCCESS(Status)) {
+ free_fcb(fcb);
+ goto end;
+ }
+
+ fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
+
+ TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
+
+ fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+ TRACE("adshash = %08x\n", fcb->adshash);
+
+ fcb->name_offset = parfcb->full_filename.Length / sizeof(WCHAR);
+ if (parfcb != Vcb->root_fcb)
+ fcb->name_offset++;
+
+ fcb->filepart.MaximumLength = fcb->filepart.Length = stream.Length;
+ fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->filepart.Buffer, stream.Buffer, stream.Length);
+
+ fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
+
+ fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = ':';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
+ TRACE("full_filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+ set_xattr(Vcb, parfcb->subvol, parfcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ parfcb->inode_item.transid = Vcb->superblock.generation;
+ parfcb->inode_item.sequence++;
+ parfcb->inode_item.st_ctime = now;
+
+ searchkey.obj_id = parfcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parfcb->subvol->id);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfcb->subvol->id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &parfcb->inode_item, sizeof(INODE_ITEM));
+
+ insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ parfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ parfcb->subvol->root_item.ctime = now;
+
+ ExFreePool(fpus.Buffer);
+ fpus.Buffer = NULL;
+ } else {
+ Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &fcb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("file_create2 returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = fcb;
+
+ fcb->next = Vcb->fcbs;
+ Vcb->fcbs = fcb;
+
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
+
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ RtlZeroMemory(ccb, sizeof(*ccb));
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(ccb);
+ ccb->disposition = disposition;
+ ccb->options = options;
+ ccb->query_dir_offset = 0;
+ RtlInitUnicodeString(&ccb->query_string, NULL);
+ ccb->has_wildcard = FALSE;
+ ccb->specific_file = FALSE;
+
+ fcb->open_count++;
+
+ FileObject->FsContext2 = ccb;
+
+ FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+
+ TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
+
+ Status = consider_write(Vcb);
+
+ if (NT_SUCCESS(Status)) {
+ ULONG fnlen;
+
+ fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
+
+ if (fcb->par != Vcb->root_fcb)
+ fcb->name_offset++;
+
+ fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+
+ if (fcb->par != Vcb->root_fcb)
+ fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
+
+ FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ FILE_ACTION_ADDED, NULL);
+
+ goto end2;
+ }
+
+end:
+ if (fpus.Buffer)
+ ExFreePool(fpus.Buffer);
+
+end2:
+ if (parfcb)
+ free_fcb(parfcb);
+
+ return Status;
+}
+
+static __inline void debug_create_options(ULONG RequestedOptions) {
+ if (RequestedOptions != 0) {
+ ULONG options = RequestedOptions;
+
+ TRACE("requested options:\n");
+
+ if (options & FILE_DIRECTORY_FILE) {
+ TRACE(" FILE_DIRECTORY_FILE\n");
+ options &= ~FILE_DIRECTORY_FILE;
+ }
+
+ if (options & FILE_WRITE_THROUGH) {
+ TRACE(" FILE_WRITE_THROUGH\n");
+ options &= ~FILE_WRITE_THROUGH;
+ }
+
+ if (options & FILE_SEQUENTIAL_ONLY) {
+ TRACE(" FILE_SEQUENTIAL_ONLY\n");
+ options &= ~FILE_SEQUENTIAL_ONLY;
+ }
+
+ if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
+ TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
+ options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
+ }
+
+ if (options & FILE_SYNCHRONOUS_IO_ALERT) {
+ TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
+ options &= ~FILE_SYNCHRONOUS_IO_ALERT;
+ }
+
+ if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
+ TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
+ options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
+ }
+
+ if (options & FILE_NON_DIRECTORY_FILE) {
+ TRACE(" FILE_NON_DIRECTORY_FILE\n");
+ options &= ~FILE_NON_DIRECTORY_FILE;
+ }
+
+ if (options & FILE_CREATE_TREE_CONNECTION) {
+ TRACE(" FILE_CREATE_TREE_CONNECTION\n");
+ options &= ~FILE_CREATE_TREE_CONNECTION;
+ }
+
+ if (options & FILE_COMPLETE_IF_OPLOCKED) {
+ TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
+ options &= ~FILE_COMPLETE_IF_OPLOCKED;
+ }
+
+ if (options & FILE_NO_EA_KNOWLEDGE) {
+ TRACE(" FILE_NO_EA_KNOWLEDGE\n");
+ options &= ~FILE_NO_EA_KNOWLEDGE;
+ }
+
+ if (options & FILE_OPEN_REMOTE_INSTANCE) {
+ TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
+ options &= ~FILE_OPEN_REMOTE_INSTANCE;
+ }
+
+ if (options & FILE_RANDOM_ACCESS) {
+ TRACE(" FILE_RANDOM_ACCESS\n");
+ options &= ~FILE_RANDOM_ACCESS;
+ }
+
+ if (options & FILE_DELETE_ON_CLOSE) {
+ TRACE(" FILE_DELETE_ON_CLOSE\n");
+ options &= ~FILE_DELETE_ON_CLOSE;
+ }
+
+ if (options & FILE_OPEN_BY_FILE_ID) {
+ TRACE(" FILE_OPEN_BY_FILE_ID\n");
+ options &= ~FILE_OPEN_BY_FILE_ID;
+ }
+
+ if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
+ TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
+ options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ if (options & FILE_NO_COMPRESSION) {
+ TRACE(" FILE_NO_COMPRESSION\n");
+ options &= ~FILE_NO_COMPRESSION;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+ if (options & FILE_OPEN_REQUIRING_OPLOCK) {
+ TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
+ options &= ~FILE_OPEN_REQUIRING_OPLOCK;
+ }
+
+ if (options & FILE_DISALLOW_EXCLUSIVE) {
+ TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
+ options &= ~FILE_DISALLOW_EXCLUSIVE;
+ }
+#endif
+ if (options & FILE_RESERVE_OPFILTER) {
+ TRACE(" FILE_RESERVE_OPFILTER\n");
+ options &= ~FILE_RESERVE_OPFILTER;
+ }
+
+ if (options & FILE_OPEN_REPARSE_POINT) {
+ TRACE(" FILE_OPEN_REPARSE_POINT\n");
+ options &= ~FILE_OPEN_REPARSE_POINT;
+ }
+
+ if (options & FILE_OPEN_NO_RECALL) {
+ TRACE(" FILE_OPEN_NO_RECALL\n");
+ options &= ~FILE_OPEN_NO_RECALL;
+ }
+
+ if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
+ TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
+ options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
+ }
+
+ if (options)
+ TRACE(" unknown options: %x\n", options);
+ } else {
+ TRACE("requested options: (none)\n");
+ }
+}
+
+static NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* newii;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find %llx,%x,%llx in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, subvol->id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, 0, newii, sizeof(INODE_ITEM), NULL);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION Stack;
+ PFILE_OBJECT FileObject;
+ ULONG RequestedDisposition;
+ ULONG options;
+ NTSTATUS Status;
+ fcb* fcb;
+ ccb* ccb;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
+ options = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+ if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
+ WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ FileObject = Stack->FileObject;
+
+ debug_create_options(options);
+
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ TRACE("requested disposition: FILE_SUPERSEDE\n");
+ break;
+
+ case FILE_CREATE:
+ TRACE("requested disposition: FILE_CREATE\n");
+ break;
+
+ case FILE_OPEN:
+ TRACE("requested disposition: FILE_OPEN\n");
+ break;
+
+ case FILE_OPEN_IF:
+ TRACE("requested disposition: FILE_OPEN_IF\n");
+ break;
+
+ case FILE_OVERWRITE:
+ TRACE("requested disposition: FILE_OVERWRITE\n");
+ break;
+
+ case FILE_OVERWRITE_IF:
+ TRACE("requested disposition: FILE_OVERWRITE_IF\n");
+ break;
+
+ default:
+ ERR("unknown disposition: %x\n", RequestedDisposition);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+ TRACE("FileObject = %p\n", FileObject);
+
+ if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto exit;
+ }
+
+ // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &fcb, &FileObject->FileName, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, Stack->Flags & SL_OPEN_TARGET_DIRECTORY);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ if (NT_SUCCESS(Status) && fcb->deleted) {
+ free_fcb(fcb);
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (NT_SUCCESS(Status)) {
+ if (RequestedDisposition == FILE_CREATE) {
+ WARN("file %.*S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ free_fcb(fcb);
+ goto exit;
+ }
+ } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
+ WARN("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
+ goto exit;
+ }
+ } else {
+ TRACE("get_fcb returned %08x\n", Status);
+ goto exit;
+ }
+
+ if (NT_SUCCESS(Status)) { // file already exists
+ struct _fcb* sf;
+
+ if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
+ RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
+ Status = STATUS_ACCESS_DENIED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ TRACE("deleted = %s\n", fcb->deleted ? "TRUE" : "FALSE");
+
+// if (!fcb->ads && !get_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, &fcb->inode_item, sizeof(INODE_ITEM), NULL)) { // FIXME - don't do this if already loaded
+// ERR("couldn't find (%llx,%x,0) in subvol %llx\n", fcb->inode, TYPE_INODE_ITEM, fcb->subvol->id);
+// Status = STATUS_INTERNAL_ERROR;
+// free_fcb(fcb);
+// goto exit;
+// }
+
+ sf = fcb;
+ while (sf) {
+ if (sf->delete_on_close) {
+ WARN("could not open as deletion pending\n");
+ Status = STATUS_DELETE_PENDING;
+ free_fcb(fcb);
+ goto exit;
+ }
+ sf = sf->par;
+ }
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK && !(options & FILE_OPEN_REPARSE_POINT)) {
+ if (!follow_symlink(fcb, FileObject)) {
+ ERR("follow_symlink failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ Status = STATUS_REPARSE;
+ Irp->IoStatus.Information = IO_REPARSE;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ // FIXME - supersede on FILE_SUPERSEDE
+
+ if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
+ ULONG defda;
+ LIST_ENTRY changed_sector_list;
+
+ if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fcb->atts & FILE_ATTRIBUTE_READONLY) {
+ WARN("cannot overwrite readonly file\n");
+ Status = STATUS_ACCESS_DENIED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ // FIXME - where did we get this from?
+// if (fcb->refcount > 1) {
+// WARN("cannot overwrite open file (fcb = %p, refcount = %u)\n", fcb, fcb->refcount);
+// Status = STATUS_ACCESS_DENIED;
+// free_fcb(fcb);
+// goto exit;
+// }
+ InitializeListHead(&changed_sector_list);
+
+ // FIXME - make sure not ADS!
+ Status = truncate_file(fcb, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ Status = update_inode_item(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_inode_item returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+
+ if (RequestedDisposition == FILE_SUPERSEDE)
+ fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ else
+ fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+
+ if (Stack->Parameters.Create.FileAttributes != defda) {
+ char val[64];
+
+ sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ } else
+ delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH);
+
+ // FIXME - truncate streams
+ // FIXME - do we need to alter parent directory's times?
+ // FIXME - send notifications
+
+ Status = consider_write(fcb->Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("consider_write returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+ }
+
+// fcb->Header.IsFastIoPossible = TRUE;
+
+ if (fcb->ads) {
+ fcb->Header.AllocationSize.QuadPart = fcb->adssize;
+ fcb->Header.FileSize.QuadPart = fcb->adssize;
+ fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
+ } else {
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ }
+
+ if (options & FILE_NON_DIRECTORY_FILE && fcb->type == BTRFS_TYPE_DIRECTORY) {
+ free_fcb(fcb);
+ Status = STATUS_FILE_IS_A_DIRECTORY;
+ goto exit;
+ } else if (options & FILE_DIRECTORY_FILE && fcb->type != BTRFS_TYPE_DIRECTORY) {
+ TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, path = %.*S)\n", fcb->type, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ free_fcb(fcb);
+ Status = STATUS_NOT_A_DIRECTORY;
+ goto exit;
+ }
+
+ Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ RtlZeroMemory(ccb, sizeof(*ccb));
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(ccb);
+ ccb->disposition = RequestedDisposition;
+ ccb->options = options;
+ ccb->query_dir_offset = 0;
+ RtlInitUnicodeString(&ccb->query_string, NULL);
+ ccb->has_wildcard = FALSE;
+ ccb->specific_file = FALSE;
+
+ FileObject->FsContext2 = ccb;
+
+ FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+
+ if (NT_SUCCESS(Status)) {
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ Irp->IoStatus.Information = FILE_SUPERSEDED;
+ break;
+
+ case FILE_OPEN:
+ case FILE_OPEN_IF:
+ Irp->IoStatus.Information = FILE_OPENED;
+ break;
+
+ case FILE_OVERWRITE:
+ case FILE_OVERWRITE_IF:
+ Irp->IoStatus.Information = FILE_OVERWRITTEN;
+ break;
+ }
+ }
+
+ fcb->open_count++;
+ } else {
+ Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options);
+ Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
+
+// if (!NT_SUCCESS(Status))
+// free_fcb(fcb);
+ }
+
+exit:
+ if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ device_extension* Vcb = NULL;
+ BOOL top_level;
+
+ TRACE("create (flags = %x)\n", Irp->Flags);
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ /* return success if just called for FS device object */
+ if (DeviceObject == devobj) {
+ TRACE("create called for FS device object\n");
+
+ Irp->IoStatus.Information = FILE_OPENED;
+ Status = STATUS_SUCCESS;
+
+ goto exit;
+ }
+
+ Vcb = DeviceObject->DeviceExtension;
+ ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (IrpSp->Flags != 0) {
+ UINT32 flags = IrpSp->Flags;
+
+ TRACE("flags:\n");
+
+ if (flags & SL_CASE_SENSITIVE) {
+ TRACE("SL_CASE_SENSITIVE\n");
+ flags &= ~SL_CASE_SENSITIVE;
+ }
+
+ if (flags & SL_FORCE_ACCESS_CHECK) {
+ TRACE("SL_FORCE_ACCESS_CHECK\n");
+ flags &= ~SL_FORCE_ACCESS_CHECK;
+ }
+
+ if (flags & SL_OPEN_PAGING_FILE) {
+ TRACE("SL_OPEN_PAGING_FILE\n");
+ flags &= ~SL_OPEN_PAGING_FILE;
+ }
+
+ if (flags & SL_OPEN_TARGET_DIRECTORY) {
+ TRACE("SL_OPEN_TARGET_DIRECTORY\n");
+ flags &= ~SL_OPEN_TARGET_DIRECTORY;
+ }
+
+ if (flags & SL_STOP_ON_SYMLINK) {
+ TRACE("SL_STOP_ON_SYMLINK\n");
+ flags &= ~SL_STOP_ON_SYMLINK;
+ }
+
+ if (flags)
+ WARN("unknown flags: %x\n", flags);
+ } else {
+ TRACE("flags: (none)\n");
+ }
+
+// Vpb = DeviceObject->DeviceExtension;
+
+// TRACE("create called for something other than FS device object\n");
+
+ // opening volume
+ // FIXME - also check if RelatedFileObject is Vcb
+ if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
+ ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+ ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+ TRACE("open operation for volume\n");
+
+ if (RequestedDisposition != FILE_OPEN &&
+ RequestedDisposition != FILE_OPEN_IF)
+ {
+ Status = STATUS_ACCESS_DENIED;
+ goto exit;
+ }
+
+ if (RequestedOptions & FILE_DIRECTORY_FILE)
+ {
+ Status = STATUS_NOT_A_DIRECTORY;
+ goto exit;
+ }
+
+ Vcb->volume_fcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, Vcb->volume_fcb->refcount);
+#endif
+ attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
+// // NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
+// // DeviceExt->VolumeFcb->RefCount++;
+//
+ Irp->IoStatus.Information = FILE_OPENED;
+ Status = STATUS_SUCCESS;
+ } else {
+ BOOL exclusive;
+ ULONG disposition;
+
+ TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
+
+ if (IrpSp->FileObject->RelatedFileObject) {
+ fcb* relfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
+
+ if (relfcb)
+ TRACE("related file name = %.*S\n", relfcb->full_filename.Length / sizeof(WCHAR), relfcb->full_filename.Buffer);
+ }
+
+ disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+
+ // We acquire the lock exclusively if there's the possibility we might be writing
+ exclusive = disposition != FILE_OPEN;
+
+ acquire_tree_lock(Vcb, exclusive);
+
+// ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
+ // Status = NtfsCreateFile(DeviceObject,
+ // Irp);
+ Status = create_file(DeviceObject, Irp);
+// ExReleaseResourceLite(&Vpb->DirResource);
+
+ release_tree_lock(Vcb, exclusive);
+
+// Status = STATUS_ACCESS_DENIED;
+ }
+
+exit:
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest( Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT );
+// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ TRACE("create returning %08x\n", Status);
+
+ ExReleaseResourceLite(&Vcb->load_lock);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/dirctrl.c
===================================================================
--- reactos/drivers/filesystems/btrfs/dirctrl.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/dirctrl.c (working copy)
@@ -0,0 +1,791 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+enum DirEntryType {
+ DirEntryType_File,
+ DirEntryType_Self,
+ DirEntryType_Parent
+};
+
+typedef struct {
+ KEY key;
+ char* name;
+ ULONG namelen;
+ UINT8 type;
+ enum DirEntryType dir_entry_type;
+} dir_entry;
+
+static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
+ PIO_STACK_LOCATION IrpSp;
+ UINT32 needed;
+ UINT64 inode;
+ INODE_ITEM ii;
+ NTSTATUS Status;
+ ULONG stringlen;
+ BOOL dotfile;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol
+ r = fcb->Vcb->roots;
+ while (r && r->id != de->key.obj_id)
+ r = r->next;
+
+ if (!r) {
+ ERR("could not find root %llx\n", de->key.obj_id);
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ inode = SUBVOL_ROOT_INODE;
+ } else {
+ inode = de->key.obj_id;
+ }
+
+ if (IrpSp->Parameters.QueryDirectory.FileInformationClass != FileNamesInformation) { // FIXME - object ID and reparse point classes too?
+ switch (de->dir_entry_type) {
+ case DirEntryType_File:
+ if (!get_item(fcb->Vcb, r, inode, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM))) { // FIXME - used cached version
+ ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+ break;
+
+ case DirEntryType_Self:
+ ii = fcb->inode_item;
+ r = fcb->subvol;
+ inode = fcb->inode;
+ break;
+
+ case DirEntryType_Parent:
+ ii = fcb->par->inode_item;
+ r = fcb->par->subvol;
+ inode = fcb->par->inode;
+ break;
+ }
+ }
+
+ // FICs which return the filename
+ if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileNamesInformation) {
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de->name, de->namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0);
+
+ switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
+ case FileBothDirectoryInformation:
+ {
+ FILE_BOTH_DIR_INFORMATION* fbdi = buf;
+
+ TRACE("FileBothDirectoryInformation\n");
+
+ needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fbdi->NextEntryOffset = 0;
+ fbdi->FileIndex = 0;
+ fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fbdi->ChangeTime.QuadPart = 0;
+ fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fbdi->FileNameLength = stringlen;
+ fbdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fbdi->ShortNameLength = 0;
+// fibdi->ShortName[12];
+
+ Status = RtlUTF8ToUnicodeN(fbdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileDirectoryInformation:
+ {
+ FILE_DIRECTORY_INFORMATION* fdi = buf;
+
+ TRACE("FileDirectoryInformation\n");
+
+ needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fdi->NextEntryOffset = 0;
+ fdi->FileIndex = 0;
+ fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fdi->ChangeTime.QuadPart = 0;
+ fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fdi->FileNameLength = stringlen;
+
+ Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileFullDirectoryInformation:
+ {
+ FILE_FULL_DIR_INFORMATION* ffdi = buf;
+
+ TRACE("FileFullDirectoryInformation\n");
+
+ needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ ffdi->NextEntryOffset = 0;
+ ffdi->FileIndex = 0;
+ ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ ffdi->ChangeTime.QuadPart = 0;
+ ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ ffdi->FileNameLength = stringlen;
+ ffdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+
+ Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileIdBothDirectoryInformation:
+ {
+ FILE_ID_BOTH_DIR_INFORMATION* fibdi = buf;
+
+ TRACE("FileIdBothDirectoryInformation\n");
+
+ needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+// if (!buf)
+// return STATUS_INVALID_POINTER;
+
+ fibdi->NextEntryOffset = 0;
+ fibdi->FileIndex = 0;
+ fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fibdi->ChangeTime.QuadPart = 0;
+ fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fibdi->FileNameLength = stringlen;
+ fibdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fibdi->ShortNameLength = 0;
+// fibdi->ShortName[12];
+ fibdi->FileId.QuadPart = inode;
+
+ Status = RtlUTF8ToUnicodeN(fibdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileIdFullDirectoryInformation:
+ FIXME("STUB: FileIdFullDirectoryInformation\n");
+ break;
+
+ case FileNamesInformation:
+ {
+ FILE_NAMES_INFORMATION* fni = buf;
+
+ TRACE("FileNamesInformation\n");
+
+ needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fni->NextEntryOffset = 0;
+ fni->FileIndex = 0;
+ fni->FileNameLength = stringlen;
+
+ Status = RtlUTF8ToUnicodeN(fni->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileObjectIdInformation:
+ FIXME("STUB: FileObjectIdInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ case FileQuotaInformation:
+ FIXME("STUB: FileQuotaInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ case FileReparsePointInformation:
+ FIXME("STUB: FileReparsePointInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ default:
+ WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ return STATUS_NO_MORE_FILES;
+}
+
+static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
+ KEY searchkey;
+ traverse_ptr next_tp;
+ DIR_ITEM* di;
+
+ if (fcb->par) { // don't return . and .. if root directory
+ if (*offset == 0) {
+ de->key.obj_id = fcb->inode;
+ de->key.obj_type = TYPE_INODE_ITEM;
+ de->key.offset = 0;
+ de->dir_entry_type = DirEntryType_Self;
+ de->name = ".";
+ de->namelen = 1;
+ de->type = BTRFS_TYPE_DIRECTORY;
+
+ *offset = 1;
+
+ return STATUS_SUCCESS;
+ } else if (*offset == 1) {
+ de->key.obj_id = fcb->par->inode;
+ de->key.obj_type = TYPE_INODE_ITEM;
+ de->key.offset = 0;
+ de->dir_entry_type = DirEntryType_Parent;
+ de->name = "..";
+ de->namelen = 2;
+ de->type = BTRFS_TYPE_DIRECTORY;
+
+ *offset = 2;
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ if (!tp->tree) {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = *offset;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ free_traverse_ptr(tp);
+ tp->tree = NULL;
+ return STATUS_NO_MORE_FILES;
+ }
+
+ TRACE("found item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+
+ if (keycmp(&tp->item->key, &searchkey) == -1) {
+ if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
+ free_traverse_ptr(tp);
+ *tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+ }
+ }
+
+ if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) {
+ free_traverse_ptr(tp);
+ tp->tree = NULL;
+ return STATUS_NO_MORE_FILES;
+ }
+
+ *offset = tp->item->key.offset + 1;
+
+ di = (DIR_ITEM*)tp->item->data;
+ de->key = di->key;
+ de->name = di->name;
+ de->namelen = di->n;
+ de->type = di->type;
+ de->dir_entry_type = DirEntryType_File;
+
+ return STATUS_SUCCESS;
+ } else {
+ if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
+ if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) {
+ free_traverse_ptr(tp);
+ *tp = next_tp;
+
+ *offset = tp->item->key.offset + 1;
+
+ di = (DIR_ITEM*)tp->item->data;
+ de->key = di->key;
+ de->name = di->name;
+ de->namelen = di->n;
+ de->type = di->type;
+ de->dir_entry_type = DirEntryType_File;
+
+ return STATUS_SUCCESS;
+ } else {
+ free_traverse_ptr(&next_tp);
+ return STATUS_NO_MORE_FILES;
+ }
+ } else
+ return STATUS_NO_MORE_FILES;
+ }
+}
+
+static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status, status2;
+ fcb* fcb;
+ ccb* ccb;
+ void* buf;
+ UINT8 *curitem, *lastitem;
+ LONG length;
+ ULONG count;
+ BOOL has_wildcard = FALSE, specific_file = FALSE;
+// UINT64 num_reads_orig;
+ traverse_ptr tp;
+ dir_entry de;
+
+ TRACE("query directory\n");
+
+// get_uid(); // TESTING
+
+// num_reads_orig = num_reads;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ fcb = IrpSp->FileObject->FsContext;
+ ccb = IrpSp->FileObject->FsContext2;
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ if (IrpSp->Flags == 0) {
+ TRACE("QD flags: (none)\n");
+ } else {
+ ULONG flags = IrpSp->Flags;
+
+ TRACE("QD flags:\n");
+
+ if (flags & SL_INDEX_SPECIFIED) {
+ TRACE(" SL_INDEX_SPECIFIED\n");
+ flags &= ~SL_INDEX_SPECIFIED;
+ }
+
+ if (flags & SL_RESTART_SCAN) {
+ TRACE(" SL_RESTART_SCAN\n");
+ flags &= ~SL_RESTART_SCAN;
+ }
+
+ if (flags & SL_RETURN_SINGLE_ENTRY) {
+ TRACE(" SL_RETURN_SINGLE_ENTRY\n");
+ flags &= ~SL_RETURN_SINGLE_ENTRY;
+ }
+
+ if (flags != 0)
+ TRACE(" unknown flags: %u\n", flags);
+ }
+
+ if (IrpSp->Flags & SL_RESTART_SCAN) {
+ ccb->query_dir_offset = 0;
+
+ if (ccb->query_string.Buffer) {
+ RtlFreeUnicodeString(&ccb->query_string);
+ ccb->query_string.Buffer = NULL;
+ }
+ }
+
+ if (IrpSp->Parameters.QueryDirectory.FileName) {
+// int i;
+// WCHAR* us;
+
+ TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
+
+// if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
+// specific_file = TRUE;
+// for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) {
+// if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') {
+// has_wildcard = TRUE;
+// specific_file = FALSE;
+// }
+// }
+// }
+ has_wildcard = TRUE;
+
+ if (ccb->query_string.Buffer)
+ RtlFreeUnicodeString(&ccb->query_string);
+
+// us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
+// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
+// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
+
+// ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG);
+// utf16_to_utf8(us, ccb->query_string);
+
+// ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
+// RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer,
+// IrpSp->Parameters.QueryDirectory.FileName->Length);
+// ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length;
+// ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
+ RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
+
+ ccb->has_wildcard = has_wildcard;
+ ccb->specific_file = specific_file;
+
+// ExFreePool(us);
+ } else {
+ has_wildcard = ccb->has_wildcard;
+ specific_file = ccb->specific_file;
+ }
+
+ if (ccb->query_string.Buffer) {
+ TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
+ }
+
+ tp.tree = NULL;
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ Status = STATUS_NO_SUCH_FILE;
+ goto end;
+ }
+
+ // FIXME - make this work
+// if (specific_file) {
+// UINT64 filesubvol, fileinode;
+// WCHAR* us;
+//
+// us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
+// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
+// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
+//
+// if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) {
+// ExFreePool(us);
+// return STATUS_NO_MORE_FILES;
+// }
+//
+// ExFreePool(us);
+// }
+
+ buf = map_user_buffer(Irp);
+ length = IrpSp->Parameters.QueryDirectory.Length;
+
+// if (specific_file) {
+ if (has_wildcard) {
+ WCHAR* uni_fn;
+ ULONG stringlen;
+ UNICODE_STRING di_uni_fn;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+
+ while (!FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+
+ ExFreePool(uni_fn);
+ if (NT_SUCCESS(Status)) {
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExFreePool(uni_fn);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+ } else {
+ if (tp.tree) free_traverse_ptr(&tp);
+
+ if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ Status = STATUS_NO_SUCH_FILE;
+
+ goto end;
+ }
+ }
+
+ ExFreePool(uni_fn);
+ }
+
+ TRACE("file(0) = %.*s\n", de.namelen, de.name);
+ TRACE("offset = %u\n", ccb->query_dir_offset - 1);
+
+ Status = query_dir_item(fcb, buf, &length, Irp, &de, fcb->subvol);
+
+ count = 0;
+ if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
+ lastitem = (UINT8*)buf;
+
+ while (length > 0) {
+ switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
+ case FileBothDirectoryInformation:
+ case FileIdBothDirectoryInformation:
+ case FileFullDirectoryInformation:
+ length -= length % 8;
+ break;
+
+ case FileNamesInformation:
+ length -= length % 4;
+ break;
+
+ default:
+ WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
+ break;
+ }
+
+ if (length > 0) {
+ WCHAR* uni_fn = NULL;
+ UNICODE_STRING di_uni_fn;
+
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+ if (NT_SUCCESS(Status)) {
+ if (has_wildcard) {
+ ULONG stringlen;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExFreePool(uni_fn);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+ }
+
+ if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
+ curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
+ count++;
+
+ TRACE("file(%u) %u = %.*s\n", count, curitem - (UINT8*)buf, de.namelen, de.name);
+ TRACE("offset = %u\n", ccb->query_dir_offset - 1);
+
+ status2 = query_dir_item(fcb, curitem, &length, Irp, &de, fcb->subvol);
+
+ if (NT_SUCCESS(status2)) {
+ ULONG* lastoffset = (ULONG*)lastitem;
+
+ *lastoffset = (ULONG)(curitem - lastitem);
+
+ lastitem = curitem;
+ } else {
+ if (uni_fn) ExFreePool(uni_fn);
+
+ break;
+ }
+ }
+
+ if (uni_fn) {
+ ExFreePool(uni_fn);
+ uni_fn = NULL;
+ }
+ } else {
+ if (Status == STATUS_NO_MORE_FILES)
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+ } else
+ break;
+ }
+ }
+
+ Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
+
+ if (tp.tree) free_traverse_ptr(&tp);
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+// TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig));
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ NTSTATUS Status;
+// WCHAR fn[MAX_PATH];
+
+ TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ // FIXME - raise exception if FCB marked for deletion?
+
+ TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fcb->full_filename,
+ IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
+
+ Status = STATUS_PENDING;
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ ULONG func;
+ BOOL top_level;
+
+ TRACE("directory control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ func = IrpSp->MinorFunction;
+
+ switch (func) {
+ case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
+ Status = notify_change_directory(DeviceObject->DeviceExtension, Irp);
+ break;
+
+ case IRP_MN_QUERY_DIRECTORY:
+ Status = query_directory(DeviceObject, Irp);
+ break;
+
+ default:
+ WARN("unknown minor %u\n", func);
+ Status = STATUS_NOT_IMPLEMENTED;
+ Irp->IoStatus.Status = Status;
+ break;
+ }
+
+ if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) {
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+ }
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/fastio.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fastio.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fastio.c (working copy)
@@ -0,0 +1,182 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include
+#include "btrfs_drv.h"
+
+FAST_IO_DISPATCH FastIoDispatch;
+
+static void STDCALL acquire_file_for_create_section(PFILE_OBJECT FileObject) {
+ TRACE("STUB: acquire_file_for_create_section\n");
+}
+
+static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) {
+ TRACE("STUB: release_file_for_create_section\n");
+}
+
+static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION buf,
+ PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+
+ TRACE("STUB: fast_query_basic_info\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION buf,
+ PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+
+ TRACE("STUB: fast_query_standard_info\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, PDEVICE_OBJECT DeviceObject) {
+ TRACE("STUB: fast_io_query_open\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_check_if_possible(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
+ ULONG LockKey, BOOLEAN CheckForReadOperation, PIO_STATUS_BLOCK IoStatus,
+ PDEVICE_OBJECT DeviceObject) {
+ fcb* fcb = FileObject->FsContext;
+ LARGE_INTEGER len2;
+
+ TRACE("(%p, %llx, %x, %x, %x, %x, %p, %p)\n", FileObject, FileOffset->QuadPart, Length, Wait, LockKey, CheckForReadOperation, IoStatus, DeviceObject);
+
+ len2.QuadPart = Length;
+
+ if (CheckForReadOperation) {
+ if (FsRtlFastCheckLockForRead(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
+ return TRUE;
+ } else {
+ if (!fcb->Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) && FsRtlFastCheckLockForWrite(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_lock(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_lock\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_single(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_single\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_all(PFILE_OBJECT FileObject, PEPROCESS ProcessId, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_all\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_all_by_key(PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_all_by_key\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_device_control(PFILE_OBJECT FileObject, BOOLEAN Wait, PVOID InputBuffer OPTIONAL, ULONG InputBufferLength, PVOID OutputBuffer OPTIONAL, ULONG OutputBufferLength, ULONG IoControlCode, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_device_control\n");
+ return FALSE;
+}
+
+static VOID STDCALL fast_io_detach_device(PDEVICE_OBJECT SourceDevice, PDEVICE_OBJECT TargetDevice){
+ TRACE("STUB: fast_io_detach_device\n");
+}
+
+static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, struct _FILE_NETWORK_OPEN_INFORMATION *Buffer, struct _IO_STATUS_BLOCK *IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_query_network_open_info\n");
+ return FALSE;
+}
+
+static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_acquire_for_mod_write\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_read_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_write_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_write_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_mdl_read_complete_compressed(PFILE_OBJECT FileObject, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_mdl_read_complete_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_mdl_write_complete_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_mdl_write_complete_compressed\n");
+ return FALSE;
+}
+
+static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_release_for_mod_write\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_acquire_for_ccflush\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_release_for_ccflush\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
+ RtlZeroMemory(&FastIoDispatch, sizeof(FastIoDispatch));
+
+ FastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
+
+ FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible;
+ FastIoDispatch.FastIoRead = FsRtlCopyRead;
+ FastIoDispatch.FastIoWrite = FsRtlCopyWrite;
+ FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info;
+ FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info;
+ FastIoDispatch.FastIoLock = fast_io_lock;
+ FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single;
+ FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all;
+ FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key;
+ FastIoDispatch.FastIoDeviceControl = fast_io_device_control;
+ FastIoDispatch.AcquireFileForNtCreateSection = acquire_file_for_create_section;
+ FastIoDispatch.ReleaseFileForNtCreateSection = release_file_for_create_section;
+ FastIoDispatch.FastIoDetachDevice = fast_io_detach_device;
+ FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info;
+ FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write;
+ FastIoDispatch.MdlRead = FsRtlMdlReadDev;
+ FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
+ FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
+ FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
+ FastIoDispatch.FastIoReadCompressed = fast_io_read_compressed;
+ FastIoDispatch.FastIoWriteCompressed = fast_io_write_compressed;
+ FastIoDispatch.MdlReadCompleteCompressed = fast_io_mdl_read_complete_compressed;
+ FastIoDispatch.MdlWriteCompleteCompressed = fast_io_mdl_write_complete_compressed;
+ FastIoDispatch.FastIoQueryOpen = fast_io_query_open;
+ FastIoDispatch.ReleaseForModWrite = fast_io_release_for_mod_write;
+ FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush;
+ FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush;
+
+ *fiod = &FastIoDispatch;
+}
\ No newline at end of file
Index: reactos/drivers/filesystems/btrfs/fileinfo.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fileinfo.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fileinfo.c (working copy)
@@ -0,0 +1,2598 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ ULONG defda;
+ BOOL inode_item_changed = FALSE;
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+ TRACE("file = %.*S, attributes = %x\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fbi->FileAttributes);
+
+ if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
+ WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // FIXME - what if FCB is volume or root?
+ // FIXME - what about subvol roots?
+
+ // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
+ // FIXME - handle times == -1
+
+ // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
+ if (fbi->FileAttributes != 0) {
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY)
+ fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (fcb->type == BTRFS_TYPE_SYMLINK)
+ fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ // create new xattr
+ if (defda != fbi->FileAttributes) {
+ char val[64];
+
+ TRACE("inserting new DOSATTRIB xattr\n");
+ sprintf(val, "0x%lx", fbi->FileAttributes);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ } else
+ delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH);
+
+ fcb->atts = fbi->FileAttributes;
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.st_ctime = now;
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ inode_item_changed = TRUE;
+ }
+
+// FIXME - CreationTime
+// FIXME - LastAccessTime
+// FIXME - LastWriteTime
+// FIXME - ChangeTime
+
+ if (inode_item_changed) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find old INODE_ITEM\n");
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ii);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ ULONG atts;
+
+ TRACE("changing delete_on_close to %s for %.*S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+
+ atts = fcb->ads ? fcb->par->atts : fcb->atts;
+ TRACE("atts = %x\n", atts);
+
+ if (atts & FILE_ATTRIBUTE_READONLY)
+ return STATUS_CANNOT_DELETE;
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
+ return STATUS_DIRECTORY_NOT_EMPTY;
+
+ if (fcb->inode == SUBVOL_ROOT_INODE) {
+ FIXME("FIXME - subvol deletion not yet supported\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ fcb->delete_on_close = fdi->DeleteFile;
+ // FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
+ FileObject->DeletePending = fdi->DeleteFile;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_EXTREF* ier;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG iersize = tp.item->size + sizeof(INODE_EXTREF) - 1 + utf8->Length;
+ UINT8* ier2;
+ UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (iersize > maxlen) {
+ ERR("item would be too long (%u > %u)\n", iersize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ier2 = ExAllocatePoolWithTag(PagedPool, iersize, ALLOC_TAG);
+ RtlCopyMemory(ier2, tp.item->data, tp.item->size);
+
+ ier = (INODE_EXTREF*)&ier2[tp.item->size];
+ ier->dir = parinode;
+ ier->index = index;
+ ier->n = utf8->Length;
+ RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ier = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_EXTREF) - 1 + utf8->Length, ALLOC_TAG);
+
+ ier->dir = parinode;
+ ier->index = index;
+ ier->n = utf8->Length;
+ RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_REF* ir;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = parinode;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG irsize = tp.item->size + sizeof(INODE_REF) - 1 + utf8->Length;
+ UINT8* ir2;
+ UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (irsize > maxlen) {
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+ TRACE("INODE_REF too long, creating INODE_EXTREF\n");
+ free_traverse_ptr(&tp);
+ return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8);
+ } else {
+ ERR("item would be too long (%u > %u)\n", irsize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ ir2 = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
+ RtlCopyMemory(ir2, tp.item->data, tp.item->size);
+
+ ir = (INODE_REF*)&ir2[tp.item->size];
+ ir->index = index;
+ ir->n = utf8->Length;
+ RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + utf8->Length, ALLOC_TAG);
+
+ ir->index = index;
+ ir->n = utf8->Length;
+ RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS get_fcb_from_dir_item(device_extension* Vcb, fcb** pfcb, fcb* parent, root* subvol, DIR_ITEM* di) {
+ LIST_ENTRY* le;
+ fcb* sf2;
+ struct _fcb* c;
+
+ le = parent->children.Flink;
+
+ while (le != &parent->children) {
+ c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (c->refcount > 0 && c->inode == di->key.obj_id && c->subvol == subvol) {
+ c->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+#endif
+ *pfcb = c;
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ sf2->utf8.Length = sf2->utf8.MaximumLength = di->n;
+ sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
+ RtlCopyMemory(sf2->utf8.Buffer, di->name, di->n);
+
+ sf2->par = parent;
+
+ parent->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parent, parent->refcount, parent->full_filename.Length / sizeof(WCHAR), parent->full_filename.Buffer);
+#endif
+
+ sf2->subvol = subvol;
+ sf2->inode = di->key.obj_id;
+ sf2->type = di->type;
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = parent->full_filename.Length / sizeof(WCHAR);
+
+ if (parent != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ InsertTailList(&parent->children, &sf2->list_entry);
+
+ if (!get_item(sf2->Vcb, sf2->subvol, sf2->inode, TYPE_INODE_ITEM, 0, &sf2->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find (%llx,%x,0) in subvol %llx\n", sf2->inode, TYPE_INODE_ITEM, sf2->subvol->id);
+ free_fcb(sf2);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // This is just a quick function for the sake of move_across_subvols. As such, we don't bother
+ // with sf2->atts, sf2->sd, or sf2->full_filename.
+
+ *pfcb = sf2;
+
+ return STATUS_SUCCESS;
+}
+
+// static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) {
+// LONG rc = 0;
+// LIST_ENTRY* le = Vcb->trees.Flink;
+//
+// while (le != &Vcb->trees) {
+// tree* t = CONTAINING_RECORD(le, tree, list_entry);
+//
+// rc += t->refcount;
+//
+// le = le->Flink;
+// }
+//
+// le = tc->Flink;
+// while (le != tc) {
+// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+// tree* t;
+//
+// rc--;
+//
+// t = tc2->tree->parent;
+// while (t) {
+// rc--;
+// t = t->parent;
+// }
+//
+// le = le->Flink;
+// }
+//
+// return rc;
+// }
+
+static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, UINT64 inode, UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now) {
+ UINT64 oldindex, index;
+ UINT32 oldcrc32;
+ INODE_ITEM* ii;
+ BOOL has_hardlink = FALSE;
+ DIR_ITEM* di;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+
+ // move INODE_ITEM
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = *now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(Vcb, &tp);
+
+ if (fcb->inode_item.st_nlink > 1) {
+ fcb->inode_item.st_nlink--;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ has_hardlink = TRUE;
+ }
+ } else {
+ WARN("couldn't find old INODE_ITEM\n");
+ }
+
+ free_traverse_ptr(&tp);
+
+ fcb->inode_item.st_nlink = 1;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+
+ // delete old DIR_ITEM
+
+ Status = delete_dir_item(Vcb, fcb->subvol, oldparinode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // create new DIR_ITEM
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, oldparinode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete DIR_INDEX
+
+ if (oldindex == 0) {
+ WARN("couldn't find old INODE_REF\n");
+ } else {
+ searchkey.obj_id = oldparinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find old DIR_INDEX\n");
+
+ free_traverse_ptr(&tp);
+ }
+
+ // get new index
+
+ searchkey.obj_id = destinode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, destsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ index = tp.item->key.offset + 1;
+ } else
+ index = 2;
+
+ free_traverse_ptr(&tp);
+
+ // create INODE_REF
+
+ Status = add_inode_ref(Vcb, destsubvol, inode, destinode, index, utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // create DIR_INDEX
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // move XATTR_ITEMs
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(di, tp.item->data, tp.item->size);
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_XATTR_ITEM, tp.item->key.offset, di, tp.item->size, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!has_hardlink)
+ delete_tree_item(Vcb, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ // do extents
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(ed, tp.item->data, tp.item->size);
+
+ // FIXME - update ed's generation
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ed, tp.item->size, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ if (!has_hardlink) {
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+ }
+ }
+
+ if (!has_hardlink)
+ delete_tree_item(Vcb, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ fcb* fcb;
+ UINT8 level;
+ UINT32 crc32;
+ UINT64 newinode;
+ UINT64 newparinode;
+ ANSI_STRING utf8;
+ LIST_ENTRY list_entry;
+} dir_list;
+
+static NTSTATUS add_to_dir_list(fcb* fcb, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ NTSTATUS Status;
+
+ *empty = TRUE;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 2;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ struct _fcb* child;
+ dir_list* dl2;
+
+ // FIXME - what about subvols?
+ if (di->key.obj_type == TYPE_INODE_ITEM) {
+ TRACE("moving inode %llx\n", di->key.obj_id);
+
+ *empty = FALSE;
+
+ Status = get_fcb_from_dir_item(fcb->Vcb, &child, fcb, fcb->subvol, di);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_fcb_from_dir_item returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+
+ dl2 = ExAllocatePoolWithTag(PagedPool, sizeof(dir_list), ALLOC_TAG);
+ dl2->fcb = child;
+ dl2->level = level;
+ dl2->newparinode = newparinode;
+
+ dl2->utf8.Length = dl2->utf8.MaximumLength = di->n;
+ dl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dl2->utf8.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(dl2->utf8.Buffer, di->name, dl2->utf8.Length);
+ dl2->crc32 = calc_crc32c(0xfffffffe, (UINT8*)dl2->utf8.Buffer, (ULONG)dl2->utf8.Length);
+
+ InsertTailList(dl, &dl2->list_entry);
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now) {
+ UINT64 inode, oldparinode;
+ NTSTATUS Status;
+ LIST_ENTRY dl;
+
+ if (destsubvol->lastinode == 0)
+ get_last_inode(Vcb, destsubvol);
+
+ inode = destsubvol->lastinode + 1;
+ destsubvol->lastinode++;
+
+ oldparinode = fcb->subvol == fcb->par->subvol ? fcb->par->inode : SUBVOL_ROOT_INODE;
+
+ Status = move_inode_across_subvols(Vcb, fcb, destsubvol, destinode, inode, oldparinode, utf8, crc32, now);
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_inode_across_subvols returned %08x\n", Status);
+ return Status;
+ }
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
+ BOOL b, empty;
+ UINT8 level, max_level;
+ LIST_ENTRY* le;
+
+ InitializeListHead(&dl);
+
+ add_to_dir_list(fcb, 0, &dl, inode, &b);
+
+ level = 0;
+ do {
+ empty = TRUE;
+
+ le = dl.Flink;
+ while (le != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ if (dl2->level == level) {
+ inode++;
+ destsubvol->lastinode++;
+
+ dl2->newinode = inode;
+
+ add_to_dir_list(dl2->fcb, level+1, &dl, dl2->newinode, &b);
+ if (!b) empty = FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!empty) level++;
+ } while (!empty);
+
+ max_level = level;
+
+ for (level = 0; level <= max_level; level++) {
+ TRACE("level %u\n", level);
+
+ le = dl.Flink;
+ while (le != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ if (dl2->level == level) {
+ TRACE("inode %llx\n", dl2->fcb->inode);
+
+ Status = move_inode_across_subvols(Vcb, dl2->fcb, destsubvol, dl2->newparinode, dl2->newinode, dl2->fcb->par->inode, &dl2->utf8, dl2->crc32, now);
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_inode_across_subvols returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ while ((le = RemoveHeadList(&dl)) != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ ExFreePool(dl2->utf8.Buffer);
+ free_fcb(dl2->fcb);
+
+ ExFreePool(dl2);
+ }
+ }
+
+ fcb->inode = inode;
+ fcb->subvol = destsubvol;
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = *now;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ROOT_REF* rr;
+ ULONG len;
+
+ rr = (ROOT_REF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n;
+
+ if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)rr > tp.item->data) {
+ RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data);
+ rroff = newrr + ((UINT8*)rr - tp.item->data);
+ } else {
+ rroff = newrr;
+ }
+
+ if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data));
+
+ insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL);
+ }
+
+ if (index)
+ *index = rr->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ rr = (ROOT_REF*)&rr->name[rr->n];
+ } else
+ break;
+ } while (len > 0);
+ } else {
+ WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n;
+ UINT8* rr2;
+
+ rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ RtlCopyMemory(rr2, tp.item->data, tp.item->size);
+ RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n);
+ ExFreePool(rr);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid) {
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT8* data;
+ ULONG datalen;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ datalen = tp.item->size;
+
+ data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
+ RtlCopyMemory(data, tp.item->data, datalen);
+ } else {
+ datalen = 0;
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = subvolid;
+ searchkey.obj_type = TYPE_ROOT_BACKREF;
+ searchkey.offset = parsubvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ if (datalen > 0) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, struct _fcb* destfcb, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists) {
+ DIR_ITEM* di;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT64 oldindex, index;
+ ROOT_REF* rr;
+
+ // delete old DIR_ITEM
+
+ Status = delete_dir_item(Vcb, fcb->par->subvol, fcb->par->inode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // create new DIR_ITEM
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = fcb->subvol->id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ Status = add_dir_item(Vcb, destfcb->subvol, destfcb->inode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete old ROOT_REF
+
+ oldindex = 0;
+
+ Status = delete_root_ref(Vcb, fcb->subvol->id, fcb->par->subvol->id, fcb->par->inode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_root_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ TRACE("root index = %llx\n", oldindex);
+
+ // delete old DIR_INDEX
+
+ if (oldindex != 0) {
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %lx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find old DIR_INDEX entry\n");
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ // create new DIR_INDEX
+
+ if (fcb->par->subvol == destfcb->subvol && fcb->par->inode == destfcb->inode) {
+ index = oldindex;
+ } else {
+ index = find_next_dir_index(Vcb, destfcb->subvol, destfcb->inode);
+ }
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = fcb->subvol->id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, destfcb->subvol, destfcb->inode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // create new ROOT_REF
+
+ rr = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_REF) - 1 + utf8->Length, ALLOC_TAG);
+ rr->dir = destfcb->inode;
+ rr->index = index;
+ rr->n = utf8->Length;
+ RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
+
+ Status = add_root_ref(Vcb, fcb->subvol->id, destfcb->subvol->id, rr);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_root_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = update_root_backref(Vcb, fcb->subvol->id, fcb->par->subvol->id);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_backref 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ if (fcb->par->subvol != destfcb->subvol) {
+ Status = update_root_backref(Vcb, fcb->subvol->id, destfcb->subvol->id);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_backref 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = *now;
+ }
+
+ destfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ destfcb->subvol->root_item.ctime = *now;
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL has_open_children(fcb* fcb) {
+ LIST_ENTRY* le = fcb->children.Flink;
+ struct _fcb* c;
+
+ while (le != &fcb->children) {
+ c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (c->refcount > 0) {
+ if (c->open_count > 0)
+ return TRUE;
+
+ if (has_open_children(c))
+ return TRUE;
+ }
+
+ le = le->Flink;
+ }
+
+ return FALSE;
+}
+
+static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) {
+ FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
+ fcb *fcb = FileObject->FsContext, *tfofcb, *oldparfcb, *oldfcb;
+ root* parsubvol;
+ UINT64 parinode, dirpos;
+ WCHAR* fn;
+ UNICODE_STRING fnus;
+ ULONG fnlen, utf8len, disize;
+ NTSTATUS Status;
+ ANSI_STRING utf8;
+ UINT32 crc32, oldcrc32;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ DIR_ITEM* di;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ BOOL across_directories;
+ INODE_ITEM* ii;
+
+ // FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
+
+ TRACE(" tfo = %p\n", tfo);
+ TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists);
+ TRACE(" RootDirectory = %p\n", fri->RootDirectory);
+ TRACE(" FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ utf8.Buffer = NULL;
+
+ if (!fcb->par) {
+ ERR("error - tried to rename file with no parent\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ fn = fri->FileName;
+ fnlen = fri->FileNameLength / sizeof(WCHAR);
+
+ if (!tfo) {
+ parsubvol = fcb->par->subvol;
+ parinode = fcb->par->inode;
+ tfofcb = NULL;
+
+ across_directories = FALSE;
+ } else {
+ LONG i;
+
+ tfofcb = tfo->FsContext;
+ parsubvol = tfofcb->subvol;
+ parinode = tfofcb->inode;
+
+ for (i = fnlen - 1; i >= 0; i--) {
+ if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
+ fn = &fri->FileName[i+1];
+ fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
+ break;
+ }
+ }
+
+ across_directories = parsubvol != fcb->par->subvol || parinode != fcb->par->inode;
+ }
+
+ fnus.Buffer = fn;
+ fnus.Length = fnus.MaximumLength = fnlen * sizeof(WCHAR);
+
+ TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ utf8.MaximumLength = utf8.Length = utf8len;
+ utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length);
+
+ // FIXME - set to crc32 if utf8 and oldutf8 are identical
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+
+// TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
+
+ oldfcb = NULL;
+
+ Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE);
+
+ if (NT_SUCCESS(Status)) {
+ WARN("destination file %.*S already exists\n", oldfcb->full_filename.Length / sizeof(WCHAR), oldfcb->full_filename.Buffer);
+
+ if (fcb != oldfcb && !(oldfcb->open_count == 0 && oldfcb->deleted)) {
+ if (!ReplaceIfExists) {
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end;
+ } else if (oldfcb->open_count >= 1 && !oldfcb->deleted) {
+ WARN("trying to overwrite open file\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (oldfcb->type == BTRFS_TYPE_DIRECTORY) {
+ WARN("trying to overwrite directory\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+ }
+ }
+
+ if (has_open_children(fcb)) {
+ WARN("trying to rename file with open children\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (oldfcb)
+ delete_fcb(oldfcb, NULL); // FIXME - make sure this doesn't cause problems if operation later fails
+
+ if (fcb->inode == SUBVOL_ROOT_INODE) {
+ UNICODE_STRING filename;
+
+ filename.Buffer = fn;
+ filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
+
+ Status = move_subvol(Vcb, fcb, tfofcb, &utf8, crc32, oldcrc32, &now, ReplaceIfExists);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_subvol returned %08x\n", Status);
+ goto end;
+ }
+ } else if (parsubvol != fcb->subvol) {
+ UNICODE_STRING filename;
+
+ filename.Buffer = fn;
+ filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
+
+ Status = move_across_subvols(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_across_subvols returned %08x\n", Status);
+ goto end;
+ }
+ } else {
+ UINT64 oldindex;
+ INODE_ITEM* ii;
+
+ // delete old DIR_ITEM entry
+
+ Status = delete_dir_item(Vcb, fcb->subvol, fcb->par->inode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // FIXME - make sure fcb's filepart matches the case on disk
+
+ // create new DIR_ITEM entry
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8.Length, ALLOC_TAG);
+ di->key.obj_id = fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8.Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
+
+ Status = add_dir_item(Vcb, parsubvol, parinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ oldindex = 0;
+
+ Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fcb->par->inode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete old DIR_INDEX entry
+
+ if (oldindex != 0) {
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else {
+ WARN("couldn't find DIR_INDEX\n");
+ }
+
+ free_traverse_ptr(&tp);
+ } else {
+ WARN("couldn't get index from INODE_REF\n");
+ }
+
+ // create new DIR_INDEX entry
+
+ if (parsubvol != fcb->par->subvol || parinode != fcb->par->inode) {
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, parsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parsubvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ dirpos = 2;
+
+ do {
+ TRACE("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ dirpos = tp.item->key.offset + 1;
+ break;
+ }
+
+ if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ } else
+ break;
+ } while (tp.item->key.obj_id >= parinode && tp.item->key.obj_type >= TYPE_DIR_INDEX);
+
+ free_traverse_ptr(&tp);
+ } else
+ dirpos = oldindex;
+
+ disize = (ULONG)(sizeof(DIR_ITEM) - 1 + utf8.Length);
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ di->key.obj_id = fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = (UINT16)utf8.Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
+
+ if (!insert_tree_item(Vcb, parsubvol, parinode, TYPE_DIR_INDEX, dirpos, di, disize, NULL))
+ ERR("error - failed to insert item\n");
+
+ // create new INODE_REF entry
+
+ Status = add_inode_ref(Vcb, parsubvol, fcb->inode, parinode, dirpos, &utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, parsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parsubvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, parsubvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ WARN("insert_tree_item failed\n");
+ }
+ }
+
+ // update directory INODE_ITEMs
+
+ fcb->par->inode_item.transid = Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+ fcb->par->inode_item.st_mtime = now;
+
+ TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
+ if (!tfofcb || (fcb->par->inode == tfofcb->inode && fcb->par->subvol == tfofcb->subvol)) {
+ fcb->par->inode_item.st_size += 2 * (utf8.Length - fcb->utf8.Length);
+ } else {
+ fcb->par->inode_item.st_size -= 2 * fcb->utf8.Length;
+ TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb->inode_item.st_size);
+ tfofcb->inode_item.st_size += 2 * utf8.Length;
+ TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb->inode_item.st_size);
+ tfofcb->inode_item.transid = Vcb->superblock.generation;
+ tfofcb->inode_item.sequence++;
+ tfofcb->inode_item.st_ctime = now;
+ tfofcb->inode_item.st_mtime = now;
+ }
+ TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
+
+ if (oldfcb && oldfcb->par != fcb->par) {
+ TRACE("oldfcb->par->inode_item.st_size was %llx\n", oldfcb->par->inode_item.st_size);
+ oldfcb->par->inode_item.st_size -= 2 * oldfcb->utf8.Length;
+ TRACE("oldfcb->par->inode_item.st_size now %llx\n", oldfcb->par->inode_item.st_size);
+ }
+
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->par->subvol, fcb->par->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL))
+ WARN("insert_tree_item failed\n");
+
+ free_traverse_ptr(&tp);
+
+ if (tfofcb && (fcb->par->inode != tfofcb->inode || fcb->par->subvol != tfofcb->subvol)) {
+ searchkey.obj_id = tfofcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, tfofcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", tfofcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &tfofcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, tfofcb->subvol, tfofcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL))
+ WARN("insert_tree_item failed\n");
+
+ free_traverse_ptr(&tp);
+ }
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ // FIXME - handle overwrite by rename here
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME, NULL);
+
+ // FIXME - change full_filename and name_offset of open children
+
+ if (fnlen != fcb->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fcb->filepart.Buffer, fcb->filepart.Length) != fcb->filepart.Length) {
+ RtlFreeUnicodeString(&fcb->filepart);
+ fcb->filepart.Length = fcb->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR));
+ fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.Length, ALLOC_TAG);
+ RtlCopyMemory(fcb->filepart.Buffer, fn, fcb->filepart.Length);
+ }
+
+ if (tfo && tfofcb != fcb->par) {
+ oldparfcb = fcb->par;
+ fcb->par = tfofcb;
+
+ fcb->par->refcount++;
+
+ RemoveEntryList(&fcb->list_entry);
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", fcb->par, fcb->par->refcount, fcb->par->full_filename.Length / sizeof(WCHAR), fcb->par->full_filename.Buffer);
+#endif
+ free_fcb(oldparfcb);
+ }
+
+ ExFreePool(fcb->utf8.Buffer);
+ fcb->utf8 = utf8;
+ utf8.Buffer = NULL;
+
+ // change fcb->full_filename
+
+ fcb->full_filename.MaximumLength = fcb->par->full_filename.Length + fcb->filepart.Length;
+ if (fcb->par->par) fcb->full_filename.MaximumLength += sizeof(WCHAR);
+ ExFreePool(fcb->full_filename.Buffer);
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+ fcb->full_filename.Length = fcb->par->full_filename.Length;
+
+ if (fcb->par->par) {
+ fcb->full_filename.Buffer[fcb->full_filename.Length / sizeof(WCHAR)] = '\\';
+ fcb->full_filename.Length += sizeof(WCHAR);
+ }
+ fcb->name_offset = fcb->full_filename.Length / sizeof(WCHAR);
+
+ RtlAppendUnicodeStringToString(&fcb->full_filename, &fcb->filepart);
+
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME, NULL);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (utf8.Buffer)
+ ExFreePool(utf8.Buffer);
+
+ if (oldfcb)
+ free_fcb(oldfcb);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only) {
+ FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+ CC_FILE_SIZES ccfs;
+ UINT8* data = NULL;
+ UINT16 datalen;
+
+ TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->adssize);
+
+ if (feofi->EndOfFile.QuadPart < fcb->adssize) {
+ if (advance_only)
+ return STATUS_SUCCESS;
+
+ TRACE("truncating stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ if (feofi->EndOfFile.QuadPart > 0) {
+ if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, feofi->EndOfFile.QuadPart);
+
+ fcb->adssize = feofi->EndOfFile.QuadPart;
+
+ if (data)
+ ExFreePool(data);
+ } else if (feofi->EndOfFile.QuadPart > fcb->adssize) {
+ UINT16 maxlen;
+ UINT8* data2;
+
+ TRACE("extending stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ // find maximum length of xattr
+ maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = fcb->adshash;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find key for xattr\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
+
+ free_traverse_ptr(&tp);
+
+ if (feofi->EndOfFile.QuadPart > maxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", feofi->EndOfFile.QuadPart, maxlen);
+ return STATUS_DISK_FULL;
+ }
+
+ if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ data2 = ExAllocatePoolWithTag(PagedPool, feofi->EndOfFile.QuadPart, ALLOC_TAG);
+ RtlCopyMemory(data2, data, datalen);
+ ExFreePool(data);
+
+ RtlZeroMemory(&data2[datalen], feofi->EndOfFile.QuadPart - datalen);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, feofi->EndOfFile.QuadPart);
+
+ fcb->adssize = feofi->EndOfFile.QuadPart;
+
+ ExFreePool(data2);
+ }
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->par->inode_item.transid = Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = now;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only) {
+ FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ NTSTATUS Status;
+ LARGE_INTEGER time;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+ CC_FILE_SIZES ccfs;
+
+ if (fcb->deleted)
+ return STATUS_FILE_CLOSED;
+
+ if (fcb->ads)
+ return stream_set_end_of_file_information(Vcb, Irp, FileObject, advance_only);
+
+ TRACE("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
+ TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
+ fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+
+// int3;
+ TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
+
+// if (feofi->EndOfFile.QuadPart==0x36c000)
+// int3;
+
+ if (feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
+ if (advance_only)
+ return STATUS_SUCCESS;
+
+ TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ Status = truncate_file(fcb, feofi->EndOfFile.QuadPart);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - truncate_file failed\n");
+ return Status;
+ }
+ } else if (feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
+ UINT64 oldalloc, newalloc;
+
+ if (Irp->Flags & IRP_PAGING_IO) {
+ WARN("paging IO tried to extend file size\n");
+ return STATUS_SUCCESS;
+ }
+
+ TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ newalloc = sector_align(feofi->EndOfFile.QuadPart, Vcb->superblock.sector_size);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ oldalloc = 0;
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ oldalloc = tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size;
+ }
+
+ // FIXME - handle inline extents here
+ if (newalloc > oldalloc) {
+ // FIXME - we should be doing prealloc rather than inserting a sparse extent here
+
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, oldalloc, newalloc - oldalloc);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+
+ fcb->inode_item.st_size = feofi->EndOfFile.QuadPart;
+ TRACE("setting st_size to %llx\n", feofi->EndOfFile.QuadPart);
+
+ TRACE("newalloc = %llx\n", newalloc);
+
+ fcb->Header.AllocationSize.QuadPart = newalloc;
+ fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+
+ free_traverse_ptr(&tp);
+ }
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ TRACE("setting FileSize for %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, ccfs.FileSize);
+
+ KeQuerySystemTime(&time);
+
+ win_time_to_unix(time, &fcb->inode_item.st_mtime);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+// static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+// FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+// fcb* fcb = FileObject->FsContext;
+//
+// FIXME("FIXME\n");
+// ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+// ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
+//
+// return STATUS_NOT_IMPLEMENTED;
+// }
+
+static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+#if DEBUG_LEVEL >= 3
+ fcb* fcb = FileObject->FsContext;
+#endif
+
+ TRACE("setting the position on %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fpi->CurrentByteOffset.QuadPart);
+
+ // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
+
+ FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb = IrpSp->FileObject->FsContext;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
+ }
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ TRACE("set information\n");
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ switch (IrpSp->Parameters.SetFile.FileInformationClass) {
+ case FileAllocationInformation:
+ TRACE("FileAllocationInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE);
+ break;
+
+ case FileBasicInformation:
+ TRACE("FileBasicInformation\n");
+ Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileDispositionInformation:
+ TRACE("FileDispositionInformation\n");
+ Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileEndOfFileInformation:
+ TRACE("FileEndOfFileInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly);
+ break;
+
+ case FileLinkInformation:
+ FIXME("STUB: FileLinkInformation\n");
+ break;
+
+ case FilePositionInformation:
+ TRACE("FilePositionInformation\n");
+ Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileRenameInformation:
+ TRACE("FileRenameInformation\n");
+ // FIXME - make this work with streams
+ Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists);
+ break;
+
+ case FileValidDataLengthInformation:
+ FIXME("STUB: FileValidDataLengthInformation\n");
+ break;
+#ifndef __REACTOS__
+ case FileNormalizedNameInformation:
+ FIXME("STUB: FileNormalizedNameInformation\n");
+ break;
+
+ case FileStandardLinkInformation:
+ FIXME("STUB: FileStandardLinkInformation\n");
+ break;
+#endif
+ default:
+ WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
+ }
+
+ if (NT_SUCCESS(Status))
+ Status = consider_write(Vcb);
+
+ release_tree_lock(Vcb, TRUE);
+
+end:
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb) {
+ RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
+
+ *length -= sizeof(FILE_BASIC_INFORMATION);
+
+ fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+ fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+ fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+ fbi->ChangeTime.QuadPart = 0;
+ fbi->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, LONG* length) {
+ INODE_ITEM* ii;
+
+ if (*length < sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
+ WARN("overflow\n");
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
+
+ *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+
+ fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+ fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+ fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+ fnoi->ChangeTime.QuadPart = 0;
+
+ if (fcb->ads) {
+ fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adssize;
+ fnoi->FileAttributes = fcb->par->atts;
+ } else {
+ fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+ fnoi->FileAttributes = fcb->atts;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, LONG* length) {
+ RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
+
+ *length -= sizeof(FILE_STANDARD_INFORMATION);
+
+ if (fcb->ads) {
+ fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adssize;
+ fsi->NumberOfLinks = fcb->par->inode_item.st_nlink;
+ fsi->Directory = S_ISDIR(fcb->par->inode_item.st_mode);
+ } else {
+ fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+ fsi->NumberOfLinks = fcb->inode_item.st_nlink;
+ fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
+ }
+
+ TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
+
+ fsi->DeletePending = fcb->delete_on_close;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, UINT64 inode, LONG* length) {
+ *length -= sizeof(FILE_INTERNAL_INFORMATION);
+
+ fii->IndexNumber.QuadPart = inode;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_ea_information(FILE_EA_INFORMATION* eai, LONG* length) {
+ *length -= sizeof(FILE_EA_INFORMATION);
+
+ eai->EaSize = 0;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_access_information(FILE_ACCESS_INFORMATION* fai, LONG* length) {
+ *length -= sizeof(FILE_ACCESS_INFORMATION);
+
+ fai->AccessFlags = GENERIC_READ;
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
+ RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
+
+ *length -= sizeof(FILE_POSITION_INFORMATION);
+
+ fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_mode_information(FILE_MODE_INFORMATION* fmi, ccb* ccb, LONG* length) {
+ RtlZeroMemory(fmi, sizeof(FILE_MODE_INFORMATION));
+
+ *length -= sizeof(FILE_MODE_INFORMATION);
+
+ if (ccb->options & FILE_WRITE_THROUGH)
+ fmi->Mode |= FILE_WRITE_THROUGH;
+
+ if (ccb->options & FILE_SEQUENTIAL_ONLY)
+ fmi->Mode |= FILE_SEQUENTIAL_ONLY;
+
+ if (ccb->options & FILE_NO_INTERMEDIATE_BUFFERING)
+ fmi->Mode |= FILE_NO_INTERMEDIATE_BUFFERING;
+
+ if (ccb->options & FILE_SYNCHRONOUS_IO_ALERT)
+ fmi->Mode |= FILE_SYNCHRONOUS_IO_ALERT;
+
+ if (ccb->options & FILE_SYNCHRONOUS_IO_NONALERT)
+ fmi->Mode |= FILE_SYNCHRONOUS_IO_NONALERT;
+
+ if (ccb->options & FILE_DELETE_ON_CLOSE)
+ fmi->Mode |= FILE_DELETE_ON_CLOSE;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_alignment_information(FILE_ALIGNMENT_INFORMATION* fai, device_extension* Vcb, LONG* length) {
+ RtlZeroMemory(fai, sizeof(FILE_ALIGNMENT_INFORMATION));
+
+ *length -= sizeof(FILE_ALIGNMENT_INFORMATION);
+
+ fai->AlignmentRequirement = Vcb->devices[0].devobj->AlignmentRequirement;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, LONG* length) {
+#if DEBUG_LEVEL > 2
+ ULONG retlen = 0;
+#endif
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ ULONG datasuflen = wcslen(datasuf) * sizeof(WCHAR);
+
+ RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION));
+
+ *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
+
+ TRACE("maximum length is %u\n", *length);
+ fni->FileNameLength = 0;
+
+ fni->FileName[0] = 0;
+
+ if (*length >= (LONG)fcb->full_filename.Length) {
+ RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, fcb->full_filename.Length);
+#if DEBUG_LEVEL > 2
+ retlen = fcb->full_filename.Length;
+#endif
+ *length -= fcb->full_filename.Length;
+ } else {
+ if (*length > 0) {
+ RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, *length);
+#if DEBUG_LEVEL > 2
+ retlen = *length;
+#endif
+ }
+ *length = -1;
+ }
+
+ fni->FileNameLength = fcb->full_filename.Length;
+
+ if (fcb->ads) {
+ if (*length >= (LONG)datasuflen) {
+ RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen);
+#if DEBUG_LEVEL > 2
+ retlen += datasuflen;
+#endif
+ *length -= datasuflen;
+ } else {
+ if (*length > 0) {
+ RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, *length);
+#if DEBUG_LEVEL > 2
+ retlen += *length;
+#endif
+ }
+ *length = -1;
+ }
+ }
+
+ TRACE("%.*S\n", retlen / sizeof(WCHAR), fni->FileName);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, LONG* length) {
+ *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
+
+ ati->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
+ ati->ReparseTag = 0;
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ UNICODE_STRING name;
+ UINT64 size;
+} stream_info;
+
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fcb* fcb, LONG* length) {
+ ULONG reqsize;
+ UINT64 i, num_streams;
+ stream_info* streams;
+ FILE_STREAM_INFORMATION* entry;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ static char xapref[] = "user.";
+ UNICODE_STRING suf;
+
+ suf.Buffer = datasuf;
+ suf.Length = suf.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+
+ num_streams = 1;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ ULONG len = tp.item->size;
+ DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
+
+ do {
+ if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref))
+ num_streams++;
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
+ xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
+ } while (len > 0);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ streams = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info) * num_streams, ALLOC_TAG);
+
+ reqsize = 0;
+
+ streams[0].name.Length = streams[0].name.MaximumLength = 0;
+ streams[0].name.Buffer = NULL;
+ streams[0].size = fcb->inode_item.st_size;
+ reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[0].name.Length;
+
+ i = 1;
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ ULONG len = tp.item->size;
+ DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
+ ULONG stringlen;
+
+ do {
+ if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref)) {
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
+ if (!NT_SUCCESS(Status)) {
+ UINT64 j;
+
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+
+ for (j = i; j < num_streams; j++)
+ streams[j].name.Buffer = NULL;
+
+ goto end;
+ }
+
+ streams[i].name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(streams[i].name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
+
+ if (!NT_SUCCESS(Status)) {
+ UINT64 j;
+
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+
+ ExFreePool(streams[i].name.Buffer);
+ for (j = i; j < num_streams; j++)
+ streams[j].name.Buffer = NULL;
+
+ goto end;
+ }
+
+ streams[i].name.Length = streams[i].name.MaximumLength = stringlen;
+
+ streams[i].size = xa->m;
+ reqsize = sector_align(reqsize, sizeof(LONGLONG));
+ reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length;
+
+ TRACE("streams[%llu].name = %.*S (length = %u)\n", i, streams[i].name.Length / sizeof(WCHAR), streams[i].name.Buffer, streams[i].name.Length / sizeof(WCHAR));
+
+ i++;
+ }
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
+ xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
+ } while (len > 0);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ TRACE("length = %i, reqsize = %u\n", *length, reqsize);
+
+ if (reqsize > *length) {
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ entry = fsi;
+ for (i = 0; i < num_streams; i++) {
+ entry->StreamNameLength = streams[i].name.Length + suf.Length + sizeof(WCHAR);
+ entry->StreamSize.QuadPart = streams[i].size;
+
+ if (i == 0)
+ entry->StreamAllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ else
+ entry->StreamAllocationSize.QuadPart = streams[i].size;
+
+ entry->StreamName[0] = ':';
+
+ if (streams[i].name.Length > 0)
+ RtlCopyMemory(&entry->StreamName[1], streams[i].name.Buffer, streams[i].name.Length);
+
+ RtlCopyMemory(&entry->StreamName[1 + (streams[i].name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
+
+ if (i == num_streams - 1)
+ entry->NextEntryOffset = 0;
+ else {
+ entry->NextEntryOffset = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length, sizeof(LONGLONG));
+
+ entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + entry->NextEntryOffset);
+ }
+ }
+
+ *length -= reqsize;
+
+ Status = STATUS_SUCCESS;
+
+end:
+ for (i = 0; i < num_streams; i++) {
+ if (streams[i].name.Buffer)
+ ExFreePool(streams[i].name.Buffer);
+ }
+
+ ExFreePool(streams);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, LONG* length) {
+ TRACE("FileStandardLinkInformation\n");
+
+ // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
+
+ fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
+ fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
+ fsli->DeletePending = fcb->delete_on_close;
+ fsli->Directory = fcb->type == BTRFS_TYPE_DIRECTORY ? TRUE : FALSE;
+
+ *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ LONG length = IrpSp->Parameters.QueryFile.Length;
+ fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
+ TRACE("fcb = %p\n", fcb);
+
+ if (fcb == Vcb->volume_fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
+ case FileAllInformation:
+ {
+ FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
+ INODE_ITEM* ii;
+
+ TRACE("FileAllInformation\n");
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+ if (length > 0)
+ fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb);
+
+ if (length > 0)
+ fill_in_file_standard_information(&fai->StandardInformation, fcb, &length);
+
+ if (length > 0)
+ fill_in_file_internal_information(&fai->InternalInformation, fcb->inode, &length);
+
+ if (length > 0)
+ fill_in_file_ea_information(&fai->EaInformation, &length);
+
+ if (length > 0)
+ fill_in_file_access_information(&fai->AccessInformation, &length);
+
+ if (length > 0)
+ fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
+
+ if (length > 0)
+ fill_in_file_mode_information(&fai->ModeInformation, ccb, &length);
+
+ if (length > 0)
+ fill_in_file_alignment_information(&fai->AlignmentInformation, Vcb, &length);
+
+ if (length > 0)
+ fill_in_file_name_information(&fai->NameInformation, fcb, &length);
+
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileAttributeTagInformation:
+ {
+ FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileAttributeTagInformation\n");
+
+ Status = fill_in_file_attribute_information(ati, fcb, &length);
+
+ break;
+ }
+
+ case FileBasicInformation:
+ {
+ FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
+ INODE_ITEM* ii;
+
+ TRACE("FileBasicInformation\n");
+
+ if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
+ WARN("overflow\n");
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+ Status = fill_in_file_basic_information(fbi, ii, &length, fcb);
+ break;
+ }
+
+ case FileCompressionInformation:
+ FIXME("STUB: FileCompressionInformation\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+
+ case FileEaInformation:
+ {
+ FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileEaInformation\n");
+
+ Status = fill_in_file_ea_information(eai, &length);
+
+ break;
+ }
+
+ case FileInternalInformation:
+ {
+ FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileInternalInformation\n");
+
+ Status = fill_in_file_internal_information(fii, fcb->inode, &length);
+
+ break;
+ }
+
+ case FileNameInformation:
+ {
+ FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNameInformation\n");
+
+ Status = fill_in_file_name_information(fni, fcb, &length);
+
+ break;
+ }
+
+ case FileNetworkOpenInformation:
+ {
+ FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNetworkOpenInformation\n");
+
+ Status = fill_in_file_network_open_information(fnoi, fcb, &length);
+
+ break;
+ }
+
+ case FilePositionInformation:
+ {
+ FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FilePositionInformation\n");
+
+ Status = fill_in_file_position_information(fpi, FileObject, &length);
+
+ break;
+ }
+
+ case FileStandardInformation:
+ {
+ FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStandardInformation\n");
+
+ if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
+ WARN("overflow\n");
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+
+ Status = fill_in_file_standard_information(fsi, fcb, &length);
+
+ break;
+ }
+
+ case FileStreamInformation:
+ {
+ FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStreamInformation\n");
+
+ Status = fill_in_file_stream_information(fsi, fcb, &length);
+
+ break;
+ }
+#ifndef __REACTOS__
+ case FileHardLinkInformation:
+ FIXME("STUB: FileHardLinkInformation\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+
+ case FileNormalizedNameInformation:
+ {
+ FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNormalizedNameInformation\n");
+
+ Status = fill_in_file_name_information(fni, fcb, &length);
+
+ break;
+ }
+
+ case FileStandardLinkInformation:
+ {
+ FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStandardLinkInformation\n");
+
+ Status = fill_in_file_standard_link_information(fsli, fcb, &length);
+
+ break;
+ }
+#endif
+ default:
+ WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ if (length < 0) {
+ length = 0;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
+
+exit:
+ TRACE("query_info returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ fcb* fcb;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ TRACE("query information\n");
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ acquire_tree_lock(Vcb, FALSE);
+
+ fcb = IrpSp->FileObject->FsContext;
+ TRACE("fcb = %p\n", fcb);
+ TRACE("fcb->subvol = %p\n", fcb->subvol);
+
+ Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
+
+ TRACE("returning %08x\n", Status);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ release_tree_lock(Vcb, FALSE);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/flushthread.c
===================================================================
--- reactos/drivers/filesystems/btrfs/flushthread.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/flushthread.c (working copy)
@@ -0,0 +1,57 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#define INTERVAL 15000 // in milliseconds
+
+static void do_flush(device_extension* Vcb) {
+ FsRtlEnterFileSystem();
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ if (Vcb->write_trees > 0)
+ do_write(Vcb);
+
+ free_tree_cache(&Vcb->tree_cache);
+
+ release_tree_lock(Vcb, TRUE);
+
+ FsRtlExitFileSystem();
+}
+
+void STDCALL flush_thread(void* context) {
+ device_extension* Vcb = context;
+ LARGE_INTEGER due_time;
+
+ KeInitializeTimer(&Vcb->flush_thread_timer);
+
+ due_time.QuadPart = -INTERVAL * 10000;
+
+ KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+
+ while (TRUE) {
+ KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
+
+ do_flush(Vcb);
+
+ KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+ }
+
+ KeCancelTimer(&Vcb->flush_thread_timer);
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
Index: reactos/drivers/filesystems/btrfs/fsctl.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fsctl.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fsctl.c (working copy)
@@ -0,0 +1,509 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#ifndef FSCTL_CSV_CONTROL
+#define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS Status;
+
+ switch (type) {
+ case FSCTL_REQUEST_OPLOCK_LEVEL_1:
+ WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_OPLOCK_LEVEL_2:
+ WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_BATCH_OPLOCK:
+ WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
+ WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_NOTIFY:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_LOCK_VOLUME:
+ WARN("STUB: FSCTL_LOCK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_UNLOCK_VOLUME:
+ WARN("STUB: FSCTL_UNLOCK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DISMOUNT_VOLUME:
+ WARN("STUB: FSCTL_DISMOUNT_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_VOLUME_MOUNTED:
+ WARN("STUB: FSCTL_IS_VOLUME_MOUNTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_PATHNAME_VALID:
+ WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MARK_VOLUME_DIRTY:
+ WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_RETRIEVAL_POINTERS:
+ WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_COMPRESSION:
+ WARN("STUB: FSCTL_GET_COMPRESSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_COMPRESSION:
+ WARN("STUB: FSCTL_SET_COMPRESSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_BOOTLOADER_ACCESSED:
+ WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_ACK_NO_2:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_INVALIDATE_VOLUMES:
+ WARN("STUB: FSCTL_INVALIDATE_VOLUMES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_FAT_BPB:
+ WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_FILTER_OPLOCK:
+ WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FILESYSTEM_GET_STATISTICS:
+ WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_NTFS_VOLUME_DATA:
+ WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_NTFS_FILE_RECORD:
+ WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_VOLUME_BITMAP:
+ WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_RETRIEVAL_POINTERS:
+ WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MOVE_FILE:
+ WARN("STUB: FSCTL_MOVE_FILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_VOLUME_DIRTY:
+ WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ALLOW_EXTENDED_DASD_IO:
+ WARN("STUB: FSCTL_ALLOW_EXTENDED_DASD_IO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FIND_FILES_BY_SID:
+ WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_OBJECT_ID:
+ WARN("STUB: FSCTL_SET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_OBJECT_ID:
+ WARN("STUB: FSCTL_GET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DELETE_OBJECT_ID:
+ WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_REPARSE_POINT:
+ Status = set_reparse_point(DeviceObject, Irp);
+ break;
+
+ case FSCTL_GET_REPARSE_POINT:
+ Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
+ IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
+ break;
+
+ case FSCTL_DELETE_REPARSE_POINT:
+ WARN("STUB: FSCTL_DELETE_REPARSE_POINT\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENUM_USN_DATA:
+ WARN("STUB: FSCTL_ENUM_USN_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SECURITY_ID_CHECK:
+ WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_USN_JOURNAL:
+ WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_OBJECT_ID_EXTENDED:
+ WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CREATE_OR_GET_OBJECT_ID:
+ WARN("STUB: FSCTL_CREATE_OR_GET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_SPARSE:
+ WARN("STUB: FSCTL_SET_SPARSE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ZERO_DATA:
+ WARN("STUB: FSCTL_SET_ZERO_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_ALLOCATED_RANGES:
+ WARN("STUB: FSCTL_QUERY_ALLOCATED_RANGES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENABLE_UPGRADE:
+ WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ENCRYPTION:
+ WARN("STUB: FSCTL_SET_ENCRYPTION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENCRYPTION_FSCTL_IO:
+ WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WRITE_RAW_ENCRYPTED:
+ WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_RAW_ENCRYPTED:
+ WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CREATE_USN_JOURNAL:
+ WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_FILE_USN_DATA:
+ WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WRITE_USN_CLOSE_RECORD:
+ WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_EXTEND_VOLUME:
+ WARN("STUB: FSCTL_EXTEND_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_USN_JOURNAL:
+ WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DELETE_USN_JOURNAL:
+ WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MARK_HANDLE:
+ WARN("STUB: FSCTL_MARK_HANDLE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SIS_COPYFILE:
+ WARN("STUB: FSCTL_SIS_COPYFILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SIS_LINK_FILES:
+ WARN("STUB: FSCTL_SIS_LINK_FILES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_RECALL_FILE:
+ WARN("STUB: FSCTL_RECALL_FILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_FROM_PLEX:
+ WARN("STUB: FSCTL_READ_FROM_PLEX\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FILE_PREFETCH:
+ WARN("STUB: FSCTL_FILE_PREFETCH\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+#if (_WIN32_WINNT >= 0x0600)
+ case FSCTL_MAKE_MEDIA_COMPATIBLE:
+ WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_DEFECT_MANAGEMENT:
+ WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_SPARING_INFO:
+ WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
+ WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_VOLUME_COMPRESSION_STATE:
+ WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_MODIFY_RM:
+ WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_QUERY_RM_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_ROLLFORWARD_REDO:
+ WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_ROLLFORWARD_UNDO:
+ WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_START_RM:
+ WARN("STUB: FSCTL_TXFS_START_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_SHUTDOWN_RM:
+ WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_READ_BACKUP_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_CREATE_SECONDARY_RM:
+ WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_GET_METADATA_INFO:
+ WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_GET_TRANSACTED_VERSION:
+ WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_SAVEPOINT_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_CREATE_MINIVERSION:
+ WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_TRANSACTION_ACTIVE:
+ WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ZERO_ON_DEALLOCATION:
+ WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_REPAIR:
+ WARN("STUB: FSCTL_SET_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_REPAIR:
+ WARN("STUB: FSCTL_GET_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WAIT_FOR_REPAIR:
+ WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_INITIATE_REPAIR:
+ WARN("STUB: FSCTL_INITIATE_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CSC_INTERNAL:
+ WARN("STUB: FSCTL_CSC_INTERNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SHRINK_VOLUME:
+ WARN("STUB: FSCTL_SHRINK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_SHORT_NAME_BEHAVIOR:
+ WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
+ WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
+ WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_LIST_TRANSACTIONS:
+ WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
+ WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
+ WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
+ WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CSV_CONTROL:
+ WARN("STUB: FSCTL_CSV_CONTROL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+#endif
+ default:
+ WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
+ IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
+ (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
+ IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/reparse.c
===================================================================
--- reactos/drivers/filesystems/btrfs/reparse.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/reparse.c (working copy)
@@ -0,0 +1,521 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject) {
+ NTSTATUS Status;
+ ULONG len, stringlen;
+#if defined(__REACTOS__) && (NTDDI_VERSION >= NTDDI_VISTA)
+ USHORT newlen;
+#endif
+ OBJECT_NAME_INFORMATION* oni;
+ UINT8* data;
+ UINT32 i;
+ BOOL success = FALSE;
+ WCHAR* utf16;
+ UNICODE_STRING abspath;
+
+ if (fcb->inode_item.st_size == 0) {
+ WARN("can't follow symlink with no data\n");
+ return FALSE;
+ }
+
+ Status = ObQueryNameString(FileObject->DeviceObject, NULL, 0, &len);
+
+ if (Status != STATUS_INFO_LENGTH_MISMATCH) {
+ ERR("ObQueryNameString 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ oni = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+
+ Status = ObQueryNameString(FileObject->DeviceObject, oni, len, &len);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ObQueryNameString 2 returned %08x\n", Status);
+ ExFreePool(oni);
+ return FALSE;
+ }
+
+ data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
+
+ Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, 0, fcb->inode_item.st_size, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ goto end;
+ }
+
+ for (i = 0; i < fcb->inode_item.st_size; i++) {
+ if (data[i] == '/')
+ data[i] = '\\';
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)data, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ goto end;
+ }
+
+ utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, (char*)data, fcb->inode_item.st_size);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(utf16);
+ goto end;
+ }
+
+ if (utf16[0] == '\\') { // absolute path
+ abspath.MaximumLength = abspath.Length = stringlen;
+ abspath.Buffer = utf16;
+ } else { // relative path
+ abspath.MaximumLength = fcb->par->full_filename.Length + sizeof(WCHAR) + stringlen;
+ abspath.Buffer = ExAllocatePoolWithTag(PagedPool, abspath.MaximumLength, ALLOC_TAG);
+
+ RtlCopyMemory(abspath.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+ abspath.Length = fcb->par->full_filename.Length;
+
+ if (fcb->par->par) {
+ abspath.Buffer[abspath.Length / sizeof(WCHAR)] = '\\';
+ abspath.Length += sizeof(WCHAR);
+ }
+
+ RtlCopyMemory(&abspath.Buffer[abspath.Length / sizeof(WCHAR)], utf16, stringlen);
+ abspath.Length += stringlen;
+
+ ExFreePool(utf16);
+ }
+
+#if defined(__REACTOS__) && (NTDDI_VERSION >= NTDDI_VISTA)
+ Status = FsRtlRemoveDotsFromPath(abspath.Buffer, abspath.Length, &newlen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("FsRtlRemoveDotsFromPath returned %08x\n", Status);
+ ExFreePool(abspath.Buffer);
+ goto end;
+ }
+
+ abspath.Length = newlen;
+#endif
+
+ TRACE("abspath = %.*S\n", abspath.Length / sizeof(WCHAR), abspath.Buffer);
+
+ TRACE("filename was %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+
+ if (FileObject->FileName.MaximumLength < oni->Name.Length + abspath.Length) {
+ ExFreePool(FileObject->FileName.Buffer);
+ FileObject->FileName.MaximumLength = oni->Name.Length + abspath.Length;
+ FileObject->FileName.Buffer = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.MaximumLength, ALLOC_TAG);
+ }
+
+ RtlCopyMemory(FileObject->FileName.Buffer, oni->Name.Buffer, oni->Name.Length);
+ RtlCopyMemory(&FileObject->FileName.Buffer[oni->Name.Length / sizeof(WCHAR)], abspath.Buffer, abspath.Length);
+ FileObject->FileName.Length = oni->Name.Length + abspath.Length;
+
+ TRACE("filename now %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+
+ ExFreePool(abspath.Buffer);
+
+ success = TRUE;
+end:
+ ExFreePool(data);
+ ExFreePool(oni);
+
+ return success;
+}
+
+NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
+ USHORT subnamelen, printnamelen, i;
+ ULONG stringlen;
+ DWORD reqlen;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ fcb* fcb = FileObject->FsContext;
+ char* data;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ if (fcb->type != BTRFS_TYPE_SYMLINK) {
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ goto end;
+ }
+
+ data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
+ TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
+ Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ subnamelen = stringlen;
+ printnamelen = stringlen;
+
+ reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
+
+ if (buflen < reqlen) {
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+ rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+ rdb->Reserved = 0;
+
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
+ rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+
+ Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ stringlen, &stringlen, data, fcb->inode_item.st_size);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
+ if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
+ rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
+ }
+
+ RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
+ &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
+
+ *retlen = reqlen;
+
+ ExFreePool(data);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ return Status;
+}
+
+static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type) {
+ KEY searchkey;
+ UINT32 crc32;
+ traverse_ptr tp;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
+ BOOL found = FALSE;
+ ULONG len = tp.item->size;
+
+ RtlCopyMemory(di, tp.item->data, tp.item->size);
+
+ di2 = di;
+ do {
+ if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
+ di2->type = type;
+ found = TRUE;
+ break;
+ }
+
+ if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) {
+ len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n;
+ di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n];
+ } else
+ break;
+ } while (len > 0);
+
+ if (found) {
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL);
+ } else
+ ExFreePool(di);
+ } else {
+ WARN("search for DIR_ITEM by crc32 failed\n");
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = index;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(di2, di, tp.item->size);
+ di2->type = type;
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ void* buffer = Irp->AssociatedIrp.SystemBuffer;
+ DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ NTSTATUS Status = STATUS_SUCCESS;
+ fcb* fcb;
+ ULONG tag, minlen;
+ UNICODE_STRING subname;
+ ANSI_STRING target;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LARGE_INTEGER offset;
+ USHORT i;
+
+ // FIXME - send notification if this succeeds? The attributes will have changed.
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fcb = FileObject->FsContext;
+
+ ERR("filename: %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ WARN("tried to set a reparse point on an existing symlink\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (!fcb->utf8.Buffer) {
+ ERR("error - utf8 on FCB not set\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // FIXME - die if not file
+ // FIXME - die if ADS
+
+ if (buflen < sizeof(ULONG)) {
+ WARN("buffer was not long enough to hold tag\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ RtlCopyMemory(&tag, buffer, sizeof(ULONG));
+
+ if (tag != IO_REPARSE_TAG_SYMLINK) {
+ FIXME("FIXME: handle arbitrary reparse tags (%08x)\n", tag);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto end;
+ }
+
+ minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
+ if (buflen < minlen) {
+ WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (!(rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) {
+ FIXME("FIXME: support non-relative symlinks\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ }
+
+ subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
+ subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
+
+ ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
+
+ fcb->type = BTRFS_TYPE_SYMLINK;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) {
+ INODE_REF* ir;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ir = (INODE_REF*)tp.item->data;
+
+ do {
+ utf8.Buffer = ir->name;
+ utf8.Length = utf8.MaximumLength = ir->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ goto end;
+ }
+
+ if (size > sizeof(INODE_REF) - 1 + ir->n) {
+ size -= sizeof(INODE_REF) - 1 + ir->n;
+
+ ir = (INODE_REF*)&ir->name[ir->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+ INODE_EXTREF* ier;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ier = (INODE_EXTREF*)tp.item->data;
+
+ do {
+ utf8.Buffer = ier->name;
+ utf8.Length = utf8.MaximumLength = ier->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ goto end;
+ }
+
+ if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
+ size -= sizeof(INODE_EXTREF) - 1 + ier->n;
+
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+ }
+
+ fcb->inode_item.st_mode |= __S_IFLNK;
+
+ Status = truncate_file(fcb, 0);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
+ goto end;
+ }
+
+ target.MaximumLength = target.Length;
+ target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status);
+ goto end;
+ }
+
+ for (i = 0; i < target.Length; i++) {
+ if (target.Buffer[i] == '\\')
+ target.Buffer[i] = '/';
+ }
+
+ offset.QuadPart = 0;
+ Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE);
+
+ ExFreePool(target.Buffer);
+
+ if (NT_SUCCESS(Status))
+ Status = consider_write(fcb->Vcb);
+
+end:
+ release_tree_lock(fcb->Vcb, TRUE);
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/resource.h
===================================================================
--- reactos/drivers/filesystems/btrfs/resource.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/resource.h (working copy)
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by btrfs.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
Index: reactos/drivers/filesystems/btrfs/search.c
===================================================================
--- reactos/drivers/filesystems/btrfs/search.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/search.c (working copy)
@@ -0,0 +1,273 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#ifndef __REACTOS__
+#include
+#include
+#include
+#include
+#endif
+
+#include
+#ifndef __REACTOS__
+#include
+#endif
+#include
+
+#ifndef __REACTOS__
+typedef struct _OBJECT_DIRECTORY_INFORMATION {
+ UNICODE_STRING Name;
+ UNICODE_STRING TypeName;
+} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION;
+#endif
+
+#ifndef _GNU_NTIFS_
+#ifndef __REACTOS__
+NTSTATUS WINAPI ZwQueryDirectoryObject(HANDLE DirectoryHandle, PVOID Buffer, ULONG Length,
+ BOOLEAN ReturnSingleEntry, BOOLEAN RestartScan, PULONG Context,
+ PULONG ReturnLength);
+#endif
+#endif
+
+VOID WINAPI IopNotifyPlugPlayNotification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
+ IN LPCGUID Event,
+ IN PVOID EventCategoryData1,
+ IN PVOID EventCategoryData2
+);
+
+static const WCHAR devpath[] = {'\\','D','e','v','i','c','e',0};
+
+static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, PDEVICE_OBJECT DeviceObject) {
+ ULONG tnsize;
+ MOUNTMGR_TARGET_NAME* tn;
+ KEVENT Event;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PIRP Irp;
+ NTSTATUS Status;
+ ULONG mmdltsize;
+ MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
+ MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
+
+ TRACE("found BTRFS volume\n");
+
+ tnsize = sizeof(MOUNTMGR_TARGET_NAME) - sizeof(WCHAR) + us->Length;
+ tn = ExAllocatePoolWithTag(NonPagedPool, tnsize, ALLOC_TAG);
+ tn->DeviceNameLength = us->Length;
+ RtlCopyMemory(tn->DeviceName, us->Buffer, tn->DeviceNameLength);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
+ mountmgr, tn, tnsize,
+ NULL, 0, FALSE, &Event, &IoStatusBlock);
+ if (!Irp) {
+ ERR("IoBuildDeviceIoControlRequest failed\n");
+ return;
+ }
+
+ Status = IoCallDriver(mountmgr, Irp);
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status))
+ ERR("IoCallDriver (1) returned %08x\n", Status);
+
+ ExFreePool(tn);
+
+ mmdltsize = sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) - 1 + us->Length;
+
+ mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
+ mmdlt->DeviceNameLength = us->Length;
+ RtlCopyMemory(&mmdlt->DeviceName, us->Buffer, us->Length);
+ TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
+ mountmgr, mmdlt, mmdltsize,
+ &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock);
+ if (!Irp) {
+ ERR("IoBuildDeviceIoControlRequest failed\n");
+ return;
+ }
+
+ Status = IoCallDriver(mountmgr, Irp);
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status))
+ ERR("IoCallDriver (2) returned %08x\n", Status);
+ else
+ TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
+
+ ExFreePool(mmdlt);
+}
+
+static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_ENTRY* volumes) {
+ KEVENT Event;
+ PIRP Irp;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+ PFILE_OBJECT FileObject;
+ PDEVICE_OBJECT DeviceObject;
+ LARGE_INTEGER Offset;
+ ULONG toread;
+ UINT8* data;
+ UNICODE_STRING us2;
+
+ TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer);
+
+ us2.Length = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + us->Length;
+ us2.MaximumLength = us2.Length;
+ us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG);
+ RtlCopyMemory(us2.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR));
+ us2.Buffer[wcslen(devpath)] = '\\';
+ RtlCopyMemory(&us2.Buffer[wcslen(devpath)+1], us->Buffer, us->Length);
+
+ TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer);
+
+ Status = IoGetDeviceObjectPointer(&us2, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ goto exit;
+ }
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Offset.QuadPart = superblock_addrs[0];
+
+ toread = sector_align(sizeof(superblock), DeviceObject->SectorSize);
+ data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
+
+ if (!Irp) {
+ ERR("IoBuildSynchronousFsdRequest failed\n");
+ goto deref;
+ }
+
+ Status = IoCallDriver(DeviceObject, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) {
+ superblock* sb = (superblock*)data;
+ volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
+
+ v->devobj = DeviceObject;
+ RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID));
+ RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID));
+ v->devnum = sb->dev_item.dev_id;
+ InsertTailList(volumes, &v->list_entry);
+
+ TRACE("volume found\n");
+ TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7],
+ v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]);
+ TRACE("device uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ v->devuuid.uuid[0], v->devuuid.uuid[1], v->devuuid.uuid[2], v->devuuid.uuid[3], v->devuuid.uuid[4], v->devuuid.uuid[5], v->devuuid.uuid[6], v->devuuid.uuid[7],
+ v->devuuid.uuid[8], v->devuuid.uuid[9], v->devuuid.uuid[10], v->devuuid.uuid[11], v->devuuid.uuid[12], v->devuuid.uuid[13], v->devuuid.uuid[14], v->devuuid.uuid[15]);
+ TRACE("device number %llx\n", v->devnum);
+
+ add_volume(mountmgr, &us2, DeviceObject);
+ }
+
+deref:
+ ExFreePool(data);
+ ObDereferenceObject(FileObject);
+
+exit:
+ ExFreePool(us2.Buffer);
+}
+
+void STDCALL look_for_vols(LIST_ENTRY* volumes) {
+ PFILE_OBJECT FileObject;
+ PDEVICE_OBJECT mountmgr;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING mmdevpath, us;
+ HANDLE h;
+ OBJECT_DIRECTORY_INFORMATION* odi;
+ ULONG odisize;
+ ULONG context;
+ BOOL restart;
+ NTSTATUS Status;
+
+ static const WCHAR hdv[] = {'H','a','r','d','d','i','s','k','V','o','l','u','m','e',0};
+ static const WCHAR device[] = {'D','e','v','i','c','e',0};
+
+ RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+ Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ return;
+ }
+
+ RtlInitUnicodeString(&us, devpath);
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.Attributes = OBJ_CASE_INSENSITIVE;
+ attr.ObjectName = &us;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ Status = ZwOpenDirectoryObject(&h, DIRECTORY_TRAVERSE, &attr);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwOpenDirectoryObject returned %08x\n", Status);
+ return;
+ }
+
+ odisize = sizeof(OBJECT_DIRECTORY_INFORMATION) * 16;
+ odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG);
+
+ restart = TRUE;
+ do {
+ Status = ZwQueryDirectoryObject(h, odi, odisize, FALSE, restart, &context, NULL/*&retlen*/);
+ restart = FALSE;
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status != STATUS_NO_MORE_ENTRIES)
+ ERR("ZwQueryDirectoryObject returned %08x\n", Status);
+ } else {
+ OBJECT_DIRECTORY_INFORMATION* odi2 = odi;
+
+ while (odi2->Name.Buffer) {
+ if (odi2->TypeName.Length == wcslen(device) * sizeof(WCHAR) &&
+ RtlCompareMemory(odi2->TypeName.Buffer, device, wcslen(device) * sizeof(WCHAR)) == wcslen(device) * sizeof(WCHAR) &&
+ odi2->Name.Length > wcslen(hdv) * sizeof(WCHAR) &&
+ RtlCompareMemory(odi2->Name.Buffer, hdv, wcslen(hdv) * sizeof(WCHAR)) == wcslen(hdv) * sizeof(WCHAR)) {
+ test_vol(mountmgr, &odi2->Name, volumes);
+ }
+ odi2 = &odi2[1];
+ }
+ }
+ } while (NT_SUCCESS(Status));
+
+ ZwClose(h);
+
+ ObDereferenceObject(FileObject);
+}
Index: reactos/drivers/filesystems/btrfs/security.c
===================================================================
--- reactos/drivers/filesystems/btrfs/security.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/security.c (working copy)
@@ -0,0 +1,923 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+typedef struct {
+ UCHAR revision;
+ UCHAR elements;
+ UCHAR auth[6];
+ UINT32 nums[8];
+} sid_header;
+
+static sid_header sid_BA = { 1, 2, SECURITY_NT_AUTHORITY, {32, 544}}; // BUILTIN\Administrators
+static sid_header sid_SY = { 1, 1, SECURITY_NT_AUTHORITY, {18}}; // NT AUTHORITY\SYSTEM
+static sid_header sid_BU = { 1, 2, SECURITY_NT_AUTHORITY, {32, 545}}; // BUILTIN\Users
+static sid_header sid_AU = { 1, 1, SECURITY_NT_AUTHORITY, {11}}; // NT AUTHORITY\Authenticated Users
+
+typedef struct {
+ UCHAR flags;
+ ACCESS_MASK mask;
+ sid_header* sid;
+} dacl;
+
+static dacl def_dacls[] = {
+ { 0, FILE_ALL_ACCESS, &sid_BA },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_BA },
+ { 0, FILE_ALL_ACCESS, &sid_SY },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_SY },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, &sid_BU },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE, &sid_AU },
+ { 0, FILE_ADD_SUBDIRECTORY, &sid_AU },
+ // FIXME - Mandatory Label\High Mandatory Level:(OI)(NP)(IO)(NW)
+ { 0, 0, NULL }
+};
+
+extern LIST_ENTRY uid_map_list;
+
+// UINT32 STDCALL get_uid() {
+// PACCESS_TOKEN at = PsReferencePrimaryToken(PsGetCurrentProcess());
+// HANDLE h;
+// ULONG len, size;
+// TOKEN_USER* tu;
+// NTSTATUS Status;
+// UINT32 uid = UID_NOBODY;
+// LIST_ENTRY* le;
+// uid_map* um;
+// // char s[256];
+//
+// Status = ObOpenObjectByPointer(at, OBJ_KERNEL_HANDLE, NULL, GENERIC_READ, NULL, KernelMode, &h);
+// if (!NT_SUCCESS(Status)) {
+// ERR("ObOpenObjectByPointer returned %08x\n", Status);
+// goto exit;
+// }
+//
+// Status = ZwQueryInformationToken(h, TokenUser, NULL, 0, &len);
+// if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+// ERR("ZwQueryInformationToken(1) returned %08x (len = %u)\n", Status, len);
+// goto exit2;
+// }
+//
+// // TRACE("len = %u\n", len);
+//
+// tu = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+//
+// Status = ZwQueryInformationToken(h, TokenUser, tu, len, &len);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("ZwQueryInformationToken(2) returned %08x\n", Status);
+// goto exit3;
+// }
+//
+// size = 8 + (4 * ((sid_header*)tu->User.Sid)->elements);
+//
+// le = uid_map_list.Flink;
+// while (le != &uid_map_list) {
+// um = CONTAINING_RECORD(le, uid_map, listentry);
+//
+// if (((sid_header*)um->sid)->elements == ((sid_header*)tu->User.Sid)->elements &&
+// RtlCompareMemory(um->sid, tu->User.Sid, size) == size) {
+// uid = um->uid;
+// break;
+// }
+//
+// le = le->Flink;
+// }
+//
+// // sid_to_string(tu->User.Sid, s);
+//
+// // TRACE("got SID: %s\n", s);
+// TRACE("uid = %u\n", uid);
+//
+// exit3:
+// ExFreePool(tu);
+//
+// exit2:
+// ZwClose(h);
+//
+// exit:
+// PsDereferencePrimaryToken(at);
+//
+// return uid;
+// }
+
+void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) {
+ unsigned int i, np;
+ UINT8 numdashes;
+ UINT64 val;
+ ULONG sidsize;
+ sid_header* sid;
+ uid_map* um;
+// char s[255];
+
+ if (sidstringlength < 4 ||
+ sidstring[0] != 'S' ||
+ sidstring[1] != '-' ||
+ sidstring[2] != '1' ||
+ sidstring[3] != '-') {
+ ERR("invalid SID\n");
+ return;
+ }
+
+ sidstring = &sidstring[4];
+ sidstringlength -= 4;
+
+ numdashes = 0;
+ for (i = 0; i < sidstringlength; i++) {
+ if (sidstring[i] == '-') {
+ numdashes++;
+ sidstring[i] = 0;
+ }
+ }
+
+ sidsize = 8 + (numdashes * 4);
+ sid = ExAllocatePoolWithTag(PagedPool, sidsize, ALLOC_TAG);
+
+ sid->revision = 0x01;
+ sid->elements = numdashes;
+
+ np = 0;
+ while (sidstringlength > 0) {
+ val = 0;
+ i = 0;
+ while (sidstring[i] != '-' && i < sidstringlength) {
+ if (sidstring[i] >= '0' && sidstring[i] <= '9') {
+ val *= 10;
+ val += sidstring[i] - '0';
+ } else
+ break;
+
+ i++;
+ }
+
+ i++;
+ TRACE("val = %u, i = %u, ssl = %u\n", (UINT32)val, i, sidstringlength);
+
+ if (np == 0) {
+ sid->auth[0] = (val & 0xff0000000000) >> 40;
+ sid->auth[1] = (val & 0xff00000000) >> 32;
+ sid->auth[2] = (val & 0xff000000) >> 24;
+ sid->auth[3] = (val & 0xff0000) >> 16;
+ sid->auth[4] = (val & 0xff00) >> 8;
+ sid->auth[5] = val & 0xff;
+ } else {
+ sid->nums[np-1] = (UINT32)val;
+ }
+
+ np++;
+
+ if (sidstringlength > i) {
+ sidstringlength -= i;
+
+ sidstring = &sidstring[i];
+ } else
+ break;
+ }
+
+// sid_to_string(sid, s);
+
+// TRACE("%s\n", s);
+ um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
+ um->sid = sid;
+ um->uid = uid;
+
+ InsertTailList(&uid_map_list, &um->listentry);
+}
+
+static void uid_to_sid(UINT32 uid, PSID* sid) {
+ LIST_ENTRY* le;
+ uid_map* um;
+ sid_header* sh;
+ UCHAR els;
+
+ le = uid_map_list.Flink;
+ while (le != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ if (um->uid == uid) {
+ *sid = ExAllocatePoolWithTag(PagedPool, RtlLengthSid(um->sid), ALLOC_TAG);
+ RtlCopyMemory(*sid, um->sid, RtlLengthSid(um->sid));
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ if (uid == 0) { // root
+ // FIXME - find actual Administrator account, rather than SYSTEM (S-1-5-18)
+ // (of form S-1-5-21-...-500)
+
+ els = 1;
+
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = els;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 5;
+
+ sh->nums[0] = 18;
+ } else {
+ // fallback to S-1-22-1-X, Samba's SID scheme
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = 2;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 22;
+
+ sh->nums[0] = 1;
+ sh->nums[1] = uid;
+ }
+
+ *sid = sh;
+}
+
+static UINT32 sid_to_uid(PSID sid) {
+ LIST_ENTRY* le;
+ uid_map* um;
+ sid_header* sh = sid;
+
+ le = uid_map_list.Flink;
+ while (le != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ if (RtlEqualSid(sid, um->sid))
+ return um->uid;
+
+ le = le->Flink;
+ }
+
+ if (RtlEqualSid(sid, &sid_SY))
+ return 0; // root
+
+ // Samba's SID scheme: S-1-22-1-X
+ if (sh->revision == 1 && sh->elements == 2 && sh->auth[0] == 0 && sh->auth[1] == 0 && sh->auth[2] == 0 && sh->auth[3] == 0 &&
+ sh->auth[4] == 0 && sh->auth[5] == 22 && sh->nums[0] == 1)
+ return sh->nums[1];
+
+ return UID_NOBODY;
+}
+
+static void gid_to_sid(UINT32 gid, PSID* sid) {
+ sid_header* sh;
+ UCHAR els;
+
+ // FIXME - do this properly?
+
+ // fallback to S-1-22-2-X, Samba's SID scheme
+ els = 2;
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = els;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 22;
+
+ sh->nums[0] = 2;
+ sh->nums[1] = gid;
+
+ *sid = sh;
+}
+
+static ACL* load_default_acl() {
+ ULONG size;
+ ACL* acl;
+ ACCESS_ALLOWED_ACE* aaa;
+ UINT32 i;
+
+ size = sizeof(ACL);
+ i = 0;
+ while (def_dacls[i].sid) {
+ size += sizeof(ACCESS_ALLOWED_ACE);
+ size += 8 + (def_dacls[i].sid->elements * sizeof(UINT32)) - sizeof(ULONG);
+ i++;
+ }
+
+ acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+ acl->AclRevision = ACL_REVISION;
+ acl->Sbz1 = 0;
+ acl->AclSize = size;
+ acl->AceCount = i;
+ acl->Sbz2 = 0;
+
+ aaa = (ACCESS_ALLOWED_ACE*)&acl[1];
+ i = 0;
+ while (def_dacls[i].sid) {
+ aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ aaa->Header.AceFlags = def_dacls[i].flags;
+ aaa->Header.AceSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + 8 + (def_dacls[i].sid->elements * sizeof(UINT32));
+ aaa->Mask = def_dacls[i].mask;
+
+ RtlCopyMemory(&aaa->SidStart, def_dacls[i].sid, 8 + (def_dacls[i].sid->elements * sizeof(UINT32)));
+
+ aaa = (ACCESS_ALLOWED_ACE*)((UINT8*)aaa + aaa->Header.AceSize);
+
+ i++;
+ }
+
+ return acl;
+}
+
+static ACL* inherit_acl(SECURITY_DESCRIPTOR* parsd, BOOL file) {
+ ULONG size;
+ NTSTATUS Status;
+ ACL *paracl, *acl;
+ BOOLEAN parhasdacl, pardefaulted;
+ ACE_HEADER *ah, *parah;
+ UINT32 i;
+ USHORT num_aces;
+
+ // FIXME - replace this with SeAssignSecurity
+
+ Status = RtlGetDaclSecurityDescriptor(parsd, &parhasdacl, ¶cl, &pardefaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetDaclSecurityDescriptor returned %08x\n", Status);
+ return NULL;
+ }
+
+ // FIXME - handle parhasdacl == FALSE
+
+// OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
+ num_aces = 0;
+ size = sizeof(ACL);
+ ah = (ACE_HEADER*)¶cl[1];
+ for (i = 0; i < paracl->AceCount; i++) {
+ if (!file && ah->AceFlags & CONTAINER_INHERIT_ACE) {
+ num_aces++;
+ size += ah->AceSize;
+
+ if (ah->AceFlags & INHERIT_ONLY_ACE) {
+ num_aces++;
+ size += ah->AceSize;
+ }
+ }
+
+ if (ah->AceFlags & OBJECT_INHERIT_ACE && (file || !(ah->AceFlags & CONTAINER_INHERIT_ACE))) {
+ num_aces++;
+ size += ah->AceSize;
+ }
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+ acl->AclRevision = ACL_REVISION;
+ acl->Sbz1 = 0;
+ acl->AclSize = size;
+ acl->AceCount = num_aces;
+ acl->Sbz2 = 0;
+
+ ah = (ACE_HEADER*)&acl[1];
+ parah = (ACE_HEADER*)¶cl[1];
+ for (i = 0; i < paracl->AceCount; i++) {
+ if (!file && parah->AceFlags & CONTAINER_INHERIT_ACE) {
+ if (parah->AceFlags & INHERIT_ONLY_ACE) {
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags &= ~INHERIT_ONLY_ACE;
+ ah->AceFlags |= INHERITED_ACE;
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags |= INHERITED_ACE;
+
+ if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE)
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ if (parah->AceFlags & OBJECT_INHERIT_ACE && (file || !(parah->AceFlags & CONTAINER_INHERIT_ACE))) {
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags |= INHERITED_ACE;
+
+ if (file)
+ ah->AceFlags &= ~INHERIT_ONLY_ACE;
+ else
+ ah->AceFlags |= INHERIT_ONLY_ACE;
+
+ if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE || file)
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ parah = (ACE_HEADER*)((UINT8*)parah + parah->AceSize);
+ }
+
+ return acl;
+}
+
+// static void STDCALL sid_to_string(PSID sid, char* s) {
+// sid_header* sh = (sid_header*)sid;
+// LARGE_INTEGER authnum;
+// UINT8 i;
+//
+// authnum.LowPart = sh->auth[5] | (sh->auth[4] << 8) | (sh->auth[3] << 16) | (sh->auth[2] << 24);
+// authnum.HighPart = sh->auth[1] | (sh->auth[0] << 8);
+//
+// sprintf(s, "S-%u-%u", sh->revision, (UINT32)authnum.QuadPart);
+//
+// for (i = 0; i < sh->elements; i++) {
+// sprintf(s, "%s-%u", s, sh->nums[i]);
+// }
+// }
+
+static BOOL get_sd_from_xattr(fcb* fcb) {
+ ULONG buflen;
+ NTSTATUS Status;
+ PSID sid, usersid;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen))
+ return FALSE;
+
+ TRACE("using xattr " EA_NTACL " for security descriptor\n");
+
+ if (fcb->inode_item.st_uid != UID_NOBODY) {
+ BOOLEAN defaulted;
+
+ Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &sid, &defaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ } else {
+ uid_to_sid(fcb->inode_item.st_uid, &usersid);
+
+ if (!RtlEqualSid(sid, usersid)) {
+ SECURITY_DESCRIPTOR *newsd, *newsd2;
+ ULONG sdsize, daclsize, saclsize, ownersize, groupsize;
+ ACL *dacl, *sacl;
+ PSID owner, group;
+
+ sdsize = daclsize = saclsize = ownersize = groupsize = 0;
+
+ Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, NULL, &sdsize, NULL, &daclsize, NULL, &saclsize, NULL, &ownersize, NULL, &groupsize);
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlSelfRelativeToAbsoluteSD 1 returned %08x\n", Status);
+ }
+
+ ERR("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize);
+
+ newsd2 = sdsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, sdsize, ALLOC_TAG);
+ dacl = daclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, daclsize, ALLOC_TAG);
+ sacl = saclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, saclsize, ALLOC_TAG);
+ owner = ownersize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, ownersize, ALLOC_TAG);
+ group = groupsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, groupsize, ALLOC_TAG);
+
+ Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, newsd2, &sdsize, dacl, &daclsize, sacl, &saclsize, owner, &ownersize, group, &groupsize);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSelfRelativeToAbsoluteSD returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ Status = RtlSetOwnerSecurityDescriptor(newsd2, usersid, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ buflen = 0;
+ Status = RtlAbsoluteToSelfRelativeSD(newsd2, NULL, &buflen);
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ if (buflen == 0 || NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ newsd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
+
+ Status = RtlAbsoluteToSelfRelativeSD(newsd2, newsd, &buflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ ExFreePool(newsd);
+ return FALSE;
+ }
+
+ ExFreePool(fcb->sd);
+
+ fcb->sd = newsd;
+
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ }
+
+ ExFreePool(usersid);
+ }
+ }
+
+ // FIXME - check GID here if not GID_NOBODY
+
+ return TRUE;
+}
+
+void fcb_get_sd(fcb* fcb) {
+ NTSTATUS Status;
+ SECURITY_DESCRIPTOR sd;
+ ULONG buflen;
+ ACL* acl = NULL;
+ PSID usersid = NULL, groupsid = NULL;
+
+ if (get_sd_from_xattr(fcb))
+ goto end;
+
+ Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+// if (fcb->inode_item.st_uid != UID_NOBODY) {
+ uid_to_sid(fcb->inode_item.st_uid, &usersid);
+ RtlSetOwnerSecurityDescriptor(&sd, usersid, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+// }
+
+// if (fcb->inode_item.st_gid != GID_NOBODY) {
+ gid_to_sid(fcb->inode_item.st_gid, &groupsid);
+ RtlSetGroupSecurityDescriptor(&sd, groupsid, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetGroupSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+// }
+
+ if (!fcb->par)
+ acl = load_default_acl();
+ else
+ acl = inherit_acl(fcb->par->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
+
+ Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, acl, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetDaclSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ // FIXME - SACL_SECURITY_INFORMATION
+
+ buflen = 0;
+
+ // get sd size
+ Status = RtlAbsoluteToSelfRelativeSD(&sd, NULL, &buflen);
+ if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
+ goto end;
+ }
+
+// fcb->sdlen = buflen;
+
+ if (buflen == 0 || Status == STATUS_SUCCESS) {
+ TRACE("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
+ goto end;
+ }
+
+ fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
+
+ Status = RtlAbsoluteToSelfRelativeSD(&sd, fcb->sd, &buflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
+ goto end;
+ }
+
+end:
+ if (acl)
+ ExFreePool(acl);
+
+ if (usersid)
+ ExFreePool(usersid);
+
+ if (groupsid)
+ ExFreePool(groupsid);
+}
+
+static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
+ NTSTATUS Status;
+ fcb* fcb = FileObject->FsContext;
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+// TRACE("buflen = %u, fcb->sdlen = %u\n", *buflen, fcb->sdlen);
+
+ // Why (void**)? Is this a bug in mingw?
+ Status = SeQuerySecurityDescriptorInfo(&flags, relsd, buflen, (void**)&fcb->sd);
+
+ if (Status == STATUS_BUFFER_TOO_SMALL)
+ TRACE("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
+ else if (!NT_SUCCESS(Status))
+ ERR("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ SECURITY_DESCRIPTOR* sd;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG buflen;
+ BOOL top_level;
+
+ TRACE("query security\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Information = 0;
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
+ TRACE("OWNER_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
+ TRACE("GROUP_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
+ TRACE("DACL_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
+ TRACE("SACL_SECURITY_INFORMATION\n");
+
+ TRACE("length = %u\n", IrpSp->Parameters.QuerySecurity.Length);
+
+ sd = map_user_buffer(Irp);
+// sd = Irp->AssociatedIrp.SystemBuffer;
+ TRACE("sd = %p\n", sd);
+
+ buflen = IrpSp->Parameters.QuerySecurity.Length;
+
+ Status = get_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
+
+ if (NT_SUCCESS(Status))
+ Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
+ else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ Irp->IoStatus.Information = buflen;
+ Status = STATUS_BUFFER_OVERFLOW;
+ } else
+ Irp->IoStatus.Information = 0;
+
+ TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, SECURITY_INFORMATION flags) {
+ NTSTATUS Status;
+ fcb* fcb = FileObject->FsContext;
+ SECURITY_DESCRIPTOR* oldsd;
+ INODE_ITEM* ii;
+ KEY searchkey;
+ traverse_ptr tp;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, flags);
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ oldsd = fcb->sd;
+
+ Status = SeSetSecurityDescriptorInfo(NULL, &flags, sd, (void**)&fcb->sd, PagedPool, IoGetFileObjectGenericMapping());
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
+ goto end;
+ }
+
+ ExFreePool(oldsd);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.sequence++;
+
+ if (flags & OWNER_SECURITY_INFORMATION) {
+ PSID owner;
+ BOOLEAN defaulted;
+
+ Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_uid = sid_to_uid(owner);
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert INODE_ITEM\n");
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(ii);
+ goto end;
+ }
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd));
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ Status = consider_write(Vcb);
+
+end:
+ release_tree_lock(Vcb, TRUE);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ BOOL top_level;
+
+ TRACE("set security\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Information = 0;
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
+ TRACE("OWNER_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
+ TRACE("GROUP_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
+ TRACE("DACL_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
+ TRACE("SACL_SECURITY_INFORMATION\n");
+
+ Status = set_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
+ IrpSp->Parameters.SetSecurity.SecurityInformation);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ TRACE("returning %08x\n", Status);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as) {
+ NTSTATUS Status;
+ PSID owner;
+ BOOLEAN defaulted;
+
+ Status = SeAssignSecurity(fcb->par ? fcb->par->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
+ &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeAssignSecurity returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ fcb->inode_item.st_uid = UID_NOBODY;
+ } else {
+ fcb->inode_item.st_uid = sid_to_uid(&owner);
+ }
+
+ return STATUS_SUCCESS;
+}
Index: reactos/drivers/filesystems/btrfs/treefuncs.c
===================================================================
--- reactos/drivers/filesystems/btrfs/treefuncs.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/treefuncs.c (working copy)
@@ -0,0 +1,1093 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+// #define DEBUG_TREE_LOCKS
+
+enum read_tree_status {
+ ReadTreeStatus_Pending,
+ ReadTreeStatus_Success,
+ ReadTreeStatus_Cancelled,
+ ReadTreeStatus_Error,
+ ReadTreeStatus_CRCError,
+ ReadTreeStatus_MissingDevice
+};
+
+struct read_tree_context;
+
+typedef struct {
+ struct read_tree_context* context;
+ UINT8* buf;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum read_tree_status status;
+} read_tree_stripe;
+
+typedef struct {
+ KEVENT Event;
+ NTSTATUS Status;
+ chunk* c;
+// UINT8* buf;
+ UINT32 buflen;
+ UINT64 num_stripes, stripes_left;
+ UINT64 type;
+ read_tree_stripe* stripes;
+} read_tree_context;
+
+static NTSTATUS STDCALL read_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_tree_stripe* stripe = conptr;
+ read_tree_context* context = (read_tree_context*)stripe->context;
+ UINT64 i;
+
+ if (stripe->status == ReadTreeStatus_Cancelled)
+ goto end;
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ tree_header* th = (tree_header*)stripe->buf;
+ UINT32 crc32;
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
+
+ if (crc32 == *((UINT32*)th->csum)) {
+ stripe->status = ReadTreeStatus_Success;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Pending) {
+ context->stripes[i].status = ReadTreeStatus_Cancelled;
+ IoCancelIrp(context->stripes[i].Irp);
+ }
+ }
+
+ goto end;
+ } else
+ stripe->status = ReadTreeStatus_CRCError;
+ } else {
+ stripe->status = ReadTreeStatus_Error;
+ }
+
+end:
+ if (context->stripes_left == 1)
+ KeSetEvent(&context->Event, 0, FALSE);
+ else
+ context->stripes_left--;
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ read_tree_context* context;
+ UINT64 i/*, type*/, offset;
+ NTSTATUS Status;
+ device** devices;
+
+ // FIXME - make this work with RAID
+
+ if (Vcb->log_to_phys_loaded) {
+ chunk* c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ci = c->chunk_item;
+ offset = c->offset;
+ devices = c->devices;
+ } else {
+ LIST_ENTRY* le = Vcb->sys_chunks.Flink;
+
+ ci = NULL;
+
+ while (le != &Vcb->sys_chunks) {
+ sys_chunk* sc = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ if (sc->key.obj_id == 0x100 && sc->key.obj_type == TYPE_CHUNK_ITEM && sc->key.offset <= addr) {
+ CHUNK_ITEM* chunk_item = sc->data;
+
+ if ((addr - sc->key.offset) < chunk_item->size && chunk_item->num_stripes > 0) {
+ ci = chunk_item;
+ offset = sc->key.offset;
+ cis = (CHUNK_ITEM_STRIPE*)&chunk_item[1];
+
+ devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * ci->num_stripes, ALLOC_TAG);
+ for (i = 0; i < ci->num_stripes; i++) {
+ devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
+ }
+
+ break;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (!ci) {
+ ERR("could not find chunk for %llx in bootstrap\n", addr);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+// if (ci->type & BLOCK_FLAG_DUPLICATE) {
+// type = BLOCK_FLAG_DUPLICATE;
+// } else if (ci->type & BLOCK_FLAG_RAID0) {
+// FIXME("RAID0 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID1) {
+// FIXME("RAID1 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID10) {
+// FIXME("RAID10 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID5) {
+// FIXME("RAID5 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID6) {
+// FIXME("RAID6 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else { // SINGLE
+// type = 0;
+// }
+
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_tree_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_stripe) * ci->num_stripes, ALLOC_TAG);
+ RtlZeroMemory(context->stripes, sizeof(read_tree_stripe) * ci->num_stripes);
+
+ context->buflen = Vcb->superblock.node_size;
+ context->num_stripes = ci->num_stripes;
+ context->stripes_left = context->num_stripes;
+// context->type = type;
+
+ // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ if (!devices[i]) {
+ context->stripes[i].status = ReadTreeStatus_MissingDevice;
+ context->stripes[i].buf = NULL;
+ context->stripes_left--;
+ } else {
+ context->stripes[i].context = (struct read_tree_context*)context;
+ context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+
+ context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+
+ if (!context->stripes[i].Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, Vcb->superblock.node_size, FALSE, FALSE, NULL);
+ if (!context->stripes[i].Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
+ }
+
+ IrpSp->Parameters.Read.Length = Vcb->superblock.node_size;
+ IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
+
+ context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+
+ IoSetCompletionRoutine(context->stripes[i].Irp, read_tree_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+
+ context->stripes[i].status = ReadTreeStatus_Pending;
+ }
+ }
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status != ReadTreeStatus_MissingDevice) {
+ IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
+ }
+ }
+
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+
+ // FIXME - if checksum error, write good data over bad
+
+ // check if any of the stripes succeeded
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Success) {
+ RtlCopyMemory(buf, context->stripes[i].buf, Vcb->superblock.node_size);
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+ }
+
+ // if not, see if we got a checksum error
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_CRCError) {
+#if DEBUG_LEVEL >= 2
+ tree_header* th = (tree_header*)context->stripes[i].buf;
+ UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
+// UINT64 j;
+
+ WARN("stripe %llu had a checksum error\n", i);
+ WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
+#endif
+
+// for (j = 0; j < ci->num_stripes; j++) {
+// WARN("stripe %llu: device = %p, status = %u\n", j, c->devices[j], context->stripes[j].status);
+// }
+// int3;
+
+ Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+ goto exit;
+ }
+ }
+
+ // failing that, return the first error we encountered
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Error) {
+ Status = context->stripes[i].iosb.Status;
+ goto exit;
+ }
+ }
+
+ // if we somehow get here, return STATUS_INTERNAL_ERROR
+
+ Status = STATUS_INTERNAL_ERROR;
+
+// for (i = 0; i < ci->num_stripes; i++) {
+// ERR("%llx: status = %u, NTSTATUS = %08x\n", i, context->stripes[i].status, context->stripes[i].iosb.Status);
+// }
+exit:
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].Irp) {
+ if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(context->stripes[i].Irp->MdlAddress);
+ IoFreeMdl(context->stripes[i].Irp->MdlAddress);
+ }
+ IoFreeIrp(context->stripes[i].Irp);
+ }
+
+ if (context->stripes[i].buf)
+ ExFreePool(context->stripes[i].buf);
+ }
+
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ if (!Vcb->log_to_phys_loaded)
+ ExFreePool(devices);
+
+ return Status;
+}
+
+tree* STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, const char* func, const char* file, unsigned int line) {
+ UINT8* buf;
+ NTSTATUS Status;
+ tree_header* th;
+ tree* t;
+ tree_data* td;
+ chunk* c;
+
+ TRACE("(%p, %llx)\n", Vcb, addr);
+
+ buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+
+ Status = read_tree(Vcb, addr, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_tree returned 0x%08x\n", Status);
+ ExFreePool(buf);
+ return NULL;
+ }
+
+ th = (tree_header*)buf;
+
+ t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&t->header, th, sizeof(tree_header));
+// t->address = addr;
+// t->level = th->level;
+ t->refcount = 1;
+ t->Vcb = Vcb;
+ t->parent = NULL;
+ t->root = r;
+// t->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ t->paritem = NULL;
+ t->size = 0;
+ t->new_address = 0;
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "loaded tree %p (%llx)\n", t, addr);
+#else
+ _debug_message(func, "loaded tree %p (%llx)\n", t, addr);
+#endif
+#endif
+
+ c = get_chunk_from_address(Vcb, addr);
+
+ if (c)
+ t->flags = c->chunk_item->type;
+ else
+ t->flags = 0;
+
+// ExInitializeResourceLite(&t->nonpaged->load_tree_lock);
+
+// t->items = ExAllocatePoolWithTag(PagedPool, num_items * sizeof(tree_data), ALLOC_TAG);
+ InitializeListHead(&t->itemlist);
+
+ if (t->header.level == 0) { // leaf node
+ leaf_node* ln = (leaf_node*)(buf + sizeof(tree_header));
+ unsigned int i;
+
+ for (i = 0; i < t->header.num_items; i++) {
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = ln[i].key;
+// TRACE("load_tree: leaf item %u (%x,%x,%x)\n", i, (UINT32)ln[i].key.obj_id, ln[i].key.obj_type, (UINT32)ln[i].key.offset);
+
+ td->data = ExAllocatePoolWithTag(PagedPool, ln[i].size, ALLOC_TAG);
+ RtlCopyMemory(td->data, buf + sizeof(tree_header) + ln[i].offset, ln[i].size);
+ td->size = ln[i].size;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+
+ InsertTailList(&t->itemlist, &td->list_entry);
+
+ t->size += ln[i].size;
+ }
+
+ t->size += t->header.num_items * sizeof(leaf_node);
+ } else {
+ internal_node* in = (internal_node*)(buf + sizeof(tree_header));
+ unsigned int i;
+
+ for (i = 0; i < t->header.num_items; i++) {
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = in[i].key;
+// TRACE("load_tree: internal item %u (%x,%x,%x)\n", i, (UINT32)in[i].key.obj_id, in[i].key.obj_type, (UINT32)in[i].key.offset);
+
+ td->treeholder.address = in[i].address;
+ td->treeholder.generation = in[i].generation;
+ td->treeholder.tree = NULL;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_unloaded;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+
+ InsertTailList(&t->itemlist, &td->list_entry);
+ }
+
+ t->size = t->header.num_items * sizeof(internal_node);
+ }
+
+ ExFreePool(buf);
+
+ InterlockedIncrement(&Vcb->open_trees);
+ InsertTailList(&Vcb->trees, &t->list_entry);
+
+ TRACE("returning %p\n", t);
+
+ return t;
+}
+
+static tree* free_tree2(tree* t, const char* func, const char* file, unsigned int line) {
+ LONG rc;
+ LIST_ENTRY* le;
+ tree_data* td;
+ tree* par;
+
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("(%p)\n", t);
+#endif
+
+ par = t->parent;
+
+// if (par) ExAcquireResourceExclusiveLite(&par->nonpaged->load_tree_lock, TRUE);
+
+ rc = InterlockedDecrement(&t->refcount);
+
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
+#else
+ _debug_message(func, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
+#endif
+#endif
+
+ if (rc < 0) {
+ ERR("error - negative refcount (%i)\n", rc);
+ int3;
+ }
+
+ if (rc == 0) {
+ root* r = t->root;
+ if (r && r->treeholder.tree != t)
+ r = NULL;
+
+// if (r) {
+// FsRtlEnterFileSystem();
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+// }
+
+ if (par) {
+ if (t->paritem)
+ t->paritem->treeholder.tree = NULL;
+
+// ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
+ }
+
+ if (t->parent)
+ t->parent = free_tree2(t->parent, func, file, line);
+
+// ExDeleteResourceLite(&t->nonpaged->load_tree_lock);
+
+// ExFreePool(t->nonpaged);
+
+#ifndef __REACTOS__
+ while ((le = RemoveHeadList(&t->itemlist)) != &t->itemlist) {
+#else
+ while (!IsListEmpty(&t->itemlist)) {
+ le = RemoveHeadList(&t->itemlist);
+#endif
+ td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (t->header.level == 0 && td->data)
+ ExFreePool(td->data);
+
+ ExFreePool(td);
+ }
+
+ InterlockedDecrement(&t->Vcb->open_trees);
+ RemoveEntryList(&t->list_entry);
+
+ if (r) {
+ r->treeholder.tree = NULL;
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// FsRtlExitFileSystem();
+ }
+
+ ExFreePool(t);
+
+ return NULL;
+ } else {
+// if (par) ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
+ }
+
+ return t;
+}
+
+BOOL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, const char* func, const char* file, unsigned int line) {
+// KIRQL irql;
+// tree_holder_nonpaged* thnp = th->nonpaged;
+ BOOL ret;
+
+// ExAcquireResourceExclusiveLite(&thnp->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+// KeAcquireSpinLock(&thnp->spin_lock, &irql);
+//
+// if (thnp->status == tree_header_loading) {
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// // FIXME - wait for Event
+// } else if (thnp->status == tree_header_unloaded || thnp->status == tree_header_unloading) {
+// if (thnp->status == tree_header_unloading) {
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+// // FIXME - wait for Event
+// }
+//
+// // FIXME - change status
+// thnp->status = tree_header_loading;
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// // FIXME - load
+// // FIXME - change status
+// // FIXME - trigger event
+// } else if (thnp->status == tree_header_loaded) {
+// _increase_tree_rc(th->tree, func, file, line);
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// ret = FALSE;
+// }
+
+ if (!th->tree) {
+ th->tree = _load_tree(Vcb, th->address, r, func, file, line);
+ th->tree->parent = t;
+ th->tree->paritem = td;
+
+ ret = TRUE;
+ } else {
+ _increase_tree_rc(th->tree, func, file, line);
+
+ ret = FALSE;
+ }
+
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+
+// ExReleaseResourceLite(&thnp->lock);
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+
+ return ret;
+}
+
+tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line) {
+ tree* ret;
+ root* r = t->root;
+
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+ ret = free_tree2(t, func, file, line);
+
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+
+ return ret;
+}
+
+static __inline tree_data* first_item(tree* t) {
+ LIST_ENTRY* le = t->itemlist.Flink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static __inline tree_data* next_item(tree* t, tree_data* td) {
+ LIST_ENTRY* le = td->list_entry.Flink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static BOOL STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ int cmp;
+ tree_data *td, *lasttd;
+
+ TRACE("(%p, %p, %p, %p, %p, %u)\n", Vcb, t, tp, searchkey, tc, ignore);
+
+ cmp = 1;
+ td = first_item(t);
+ lasttd = NULL;
+
+ do {
+ cmp = keycmp(searchkey, &td->key);
+// TRACE("(%u) comparing (%x,%x,%x) to (%x,%x,%x) - %i (ignore = %s)\n", t->header.level, (UINT32)searchkey->obj_id, searchkey->obj_type, (UINT32)searchkey->offset, (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset, cmp, td->ignore ? "TRUE" : "FALSE");
+ if (cmp == 1) {
+ if (!td->ignore || ignore)
+ lasttd = td;
+ td = next_item(t, td);
+ }
+
+ if (t->header.level == 0 && !ignore && td && td->ignore) {
+ while (td && td->ignore)
+ td = next_item(t, td);
+
+ if (td)
+ cmp = keycmp(searchkey, &td->key);
+ }
+ } while (td && cmp == 1);
+
+ if ((cmp == -1 || !td) && lasttd)
+ td = lasttd;
+
+ if (t->header.level == 0) {
+ tp->tree = t;
+ _increase_tree_rc(t, func, file, line);
+ tp->item = td;
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+ return TRUE;
+ } else {
+ BOOL b;
+
+// if (i > 0)
+// TRACE("entering tree from (%x,%x,%x) to (%x,%x,%x) (%p)\n", (UINT32)t->items[i].key.obj_id, t->items[i].key.obj_type, (UINT32)t->items[i].key.offset, (UINT32)t->items[i+1].key.obj_id, t->items[i+1].key.obj_type, (UINT32)t->items[i+1].key.offset, t->items[i].tree);
+
+ if (_do_load_tree(Vcb, &td->treeholder, t->root, t, td, func, file, line))
+ _increase_tree_rc(t, func, file, line);
+
+ b = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
+
+ td->treeholder.tree = _free_tree(td->treeholder.tree, func, file, line);
+ TRACE("tree now %p\n", td->treeholder.tree);
+
+ return b;
+ }
+}
+
+BOOL STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ BOOL b;
+// KIRQL irql;
+
+ TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
+
+// KeAcquireSpinLock(&r->load_tree_lock, &irql);
+
+ _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, func, file, line);
+
+// KeReleaseSpinLock(&r->load_tree_lock, irql);
+
+ b = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
+
+// FsRtlEnterFileSystem();
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+ /*r->treeholder.tree = */_free_tree(r->treeholder.tree, func, file, line);
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// FsRtlExitFileSystem();
+
+#ifdef DEBUG_PARANOID
+ if (b && !ignore && tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return b;
+}
+
+void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line) {
+ if (tp->tree) {
+ tp->tree = free_tree2(tp->tree, func, file, line);
+ }
+}
+
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ tree* t;
+ tree_data *td, *next;
+
+ next = next_item(tp->tree, tp->item);
+
+ if (!ignore) {
+ while (next && next->ignore)
+ next = next_item(tp->tree, next);
+ }
+
+ if (next) {
+ next_tp->tree = tp->tree;
+ _increase_tree_rc(next_tp->tree, func, file, line);
+ next_tp->item = next;
+
+#ifdef DEBUG_PARANOID
+ if (!ignore && next_tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return TRUE;
+ }
+
+ if (!tp->tree->parent)
+ return FALSE;
+
+ t = tp->tree;
+ do {
+ if (t->parent) {
+ td = next_item(t->parent, t->paritem);
+
+ if (td) break;
+ }
+
+ t = t->parent;
+ } while (t);
+
+ if (!t)
+ return FALSE;
+
+ if (_do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, func, file, line))
+ _increase_tree_rc(t->parent, func, file, line);
+
+ t = td->treeholder.tree;
+
+ while (t->header.level != 0) {
+ tree_data* fi;
+
+ fi = first_item(t);
+ _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, func, file, line);
+
+ t = fi->treeholder.tree;
+ }
+
+ next_tp->tree = t;
+ next_tp->item = first_item(t);
+
+ if (!ignore && next_tp->item->ignore) {
+ traverse_ptr ntp2;
+ BOOL b;
+
+ while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
+ _free_traverse_ptr(next_tp, func, file, line);
+ *next_tp = ntp2;
+
+ if (!next_tp->item->ignore)
+ break;
+ }
+
+ if (!b) {
+ _free_traverse_ptr(next_tp, func, file, line);
+ return FALSE;
+ }
+ }
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+#ifdef DEBUG_PARANOID
+ if (!ignore && next_tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return TRUE;
+}
+
+static __inline tree_data* prev_item(tree* t, tree_data* td) {
+ LIST_ENTRY* le = td->list_entry.Blink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static __inline tree_data* last_item(tree* t) {
+ LIST_ENTRY* le = t->itemlist.Blink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ tree* t;
+ tree_data* td;
+
+ // FIXME - support ignore flag
+ if (prev_item(tp->tree, tp->item)) {
+ prev_tp->tree = tp->tree;
+ _increase_tree_rc(prev_tp->tree, func, file, line);
+ prev_tp->item = prev_item(tp->tree, tp->item);
+
+ return TRUE;
+ }
+
+ if (!tp->tree->parent)
+ return FALSE;
+
+ t = tp->tree;
+ while (t && (!t->parent || !prev_item(t->parent, t->paritem))) {
+ t = t->parent;
+ }
+
+ if (!t)
+ return FALSE;
+
+ td = prev_item(t->parent, t->paritem);
+ if (_do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, func, file, line))
+ _increase_tree_rc(t->parent, func, file, line);
+
+ t = td->treeholder.tree;
+
+ while (t->header.level != 0) {
+ tree_data* li;
+
+ li = last_item(t);
+ _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, func, file, line);
+
+ t = li->treeholder.tree;
+ }
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+ prev_tp->tree = t;
+ prev_tp->item = last_item(t);
+
+ return TRUE;
+}
+
+BOOL STDCALL _get_item(device_extension* Vcb, root* r, UINT64 objid, UINT8 objtype, UINT64 offset, void* ptr, UINT32 size, const char* func, const char* file, unsigned int line) {
+ traverse_ptr tp;
+ KEY searchkey;
+
+ searchkey.obj_id = objid;
+ searchkey.obj_type = objtype;
+ searchkey.offset = offset;
+
+ if (!_find_item(Vcb, r, &tp, &searchkey, FALSE, func, file, line))
+ return FALSE;
+
+ TRACE("looked for (%llx,%x,%llx), got (%llx,%x,%llx)\n", objid, objtype, offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ _free_traverse_ptr(&tp, func, file, line);
+ return FALSE;
+ }
+
+ if (size > tp.item->size)
+ size = tp.item->size;
+
+ RtlCopyMemory(ptr, tp.item->data, size);
+
+ _free_traverse_ptr(&tp, func, file, line);
+ return TRUE;
+}
+
+// static void free_tree_holder(tree_holder* th) {
+// root* r = th->tree->root;
+//
+// // ExAcquireResourceExclusiveLite(&th->nonpaged->lock, TRUE);
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+//
+// free_tree2(th->tree, funcname, __FILE__, __LINE__);
+//
+// // ExReleaseResourceLite(&th->nonpaged->lock);
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// }
+
+void STDCALL free_tree_cache(LIST_ENTRY* tc) {
+ LIST_ENTRY* le;
+ tree_cache* tc2;
+ root* r;
+
+ while (tc->Flink != tc) {
+ le = tc->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ r = tc2->tree->root;
+
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+ while (le != tc) {
+ LIST_ENTRY* nextle = le->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree->root == r) {
+ tree* nt;
+ BOOL top = !tc2->tree->paritem;
+
+ nt = free_tree2(tc2->tree, funcname, __FILE__, __LINE__);
+ if (top && !nt && r->treeholder.tree == tc2->tree)
+ r->treeholder.tree = NULL;
+
+ RemoveEntryList(&tc2->list_entry);
+ ExFreePool(tc2);
+ }
+
+ le = nextle;
+ }
+
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+ }
+}
+
+void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write) {
+ LIST_ENTRY* le;
+ tree_cache* tc2;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree == t) {
+ if (write && !tc2->write) {
+ Vcb->write_trees++;
+ tc2->write = TRUE;
+ }
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ tc2 = ExAllocatePoolWithTag(PagedPool, sizeof(tree_cache), ALLOC_TAG);
+
+ TRACE("adding %p to tree cache\n", t);
+
+ tc2->tree = t;
+ tc2->write = write;
+ increase_tree_rc(t);
+ InsertTailList(&Vcb->tree_cache, &tc2->list_entry);
+
+// print_trees(tc);
+}
+
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp) {
+ traverse_ptr tp;
+ KEY searchkey;
+ int cmp;
+ tree_data *td, *paritem;
+ tree* t;
+#if DEBUG_LEVEL >= 3
+ LIST_ENTRY* le;
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+#endif
+
+ TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, tc, ptp);
+
+ searchkey.obj_id = obj_id;
+ searchkey.obj_type = obj_type;
+ searchkey.offset = offset;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, TRUE)) {
+ ERR("error: find_item failed\n");
+ return FALSE;
+ }
+
+// // don't insert into tree about to be deleted
+// while (tp.tree->header.num_items == 0) {
+// traverse_ptr prev_tp;
+//
+// ERR("tree about to be deleted, moving back\n");
+//
+// if (find_prev_item(Vcb, &tp, &prev_tp, NULL, TRUE)) {
+// free_traverse_ptr(&tp);
+// tp = prev_tp;
+// } else
+// break;
+// }
+
+ TRACE("tp.item = %p\n", tp.item);
+ TRACE("tp.item->key = %p\n", &tp.item->key);
+ cmp = keycmp(&searchkey, &tp.item->key);
+
+ if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
+ ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = searchkey;
+ td->size = size;
+ td->data = data;
+ td->ignore = FALSE;
+ td->inserted = TRUE;
+
+#if DEBUG_LEVEL >= 3
+ le = tp.tree->itemlist.Flink;
+ while (le != &tp.tree->itemlist) {
+ tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+ firstitem = td2->key;
+ break;
+ }
+
+ TRACE("inserting %llx,%x,%llx into tree beginning %llx,%x,%llx (num_items %x)\n", obj_id, obj_type, offset, firstitem.obj_id, firstitem.obj_type, firstitem.offset, tp.tree->header.num_items);
+#endif
+
+ if (cmp == -1) { // very first key in root
+ InsertHeadList(&tp.tree->itemlist, &td->list_entry);
+
+ paritem = tp.tree->paritem;
+ while (paritem) {
+// ERR("paritem = %llx,%x,%llx, tp.item->key = %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ if (!keycmp(&paritem->key, &tp.item->key)) {
+ paritem->key = searchkey;
+ } else
+ break;
+
+ paritem = paritem->treeholder.tree->paritem;
+ }
+
+ } else {
+ InsertAfter(&tp.tree->itemlist, &td->list_entry, &tp.item->list_entry); // FIXME - we don't need this
+ }
+
+ tp.tree->header.num_items++;
+ tp.tree->size += size + sizeof(leaf_node);
+// ERR("tree %p, num_items now %x\n", tp.tree, tp.tree->header.num_items);
+// ERR("size now %x\n", tp.tree->size);
+
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+
+ if (!ptp)
+ free_traverse_ptr(&tp);
+ else
+ *ptp = tp;
+
+ t = tp.tree;
+ while (t) {
+ t->header.generation = Vcb->superblock.generation;
+ t = t->parent;
+ }
+
+ // FIXME - free this correctly
+
+ return TRUE;
+}
+
+void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp) {
+ tree* t;
+ UINT64 gen;
+
+ TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->ignore ? "TRUE" : "FALSE");
+
+#ifdef DEBUG_PARANOID
+ if (tp->item->ignore) {
+ ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+ int3;
+ }
+#endif
+
+ tp->item->ignore = TRUE;
+
+ add_to_tree_cache(Vcb, tp->tree, TRUE);
+
+ tp->tree->header.num_items--;
+
+ if (tp->tree->header.level == 0)
+ tp->tree->size -= sizeof(leaf_node) + tp->item->size;
+ else
+ tp->tree->size -= sizeof(internal_node);
+
+ gen = tp->tree->Vcb->superblock.generation;
+
+ t = tp->tree;
+ while (t) {
+ t->header.generation = gen;
+ t = t->parent;
+ }
+
+// if (tp->tree->header.num_items == 0 && tp->tree->parent) {
+// traverse_ptr tp2;
+//
+// tp2.tree = tp->tree->parent;
+// tp2.item = tp->tree->paritem;
+//
+// delete_tree_item(&tp2, tc);
+// }
+}
Index: reactos/drivers/filesystems/btrfs/write.c
===================================================================
--- reactos/drivers/filesystems/btrfs/write.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/write.c (working copy)
@@ -0,0 +1,5714 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
+
+// BOOL did_split;
+BOOL chunk_test = FALSE;
+
+typedef struct {
+ KEVENT Event;
+ IO_STATUS_BLOCK iosb;
+} write_context;
+
+typedef struct {
+ EXTENT_ITEM ei;
+ UINT8 type;
+ EXTENT_DATA_REF edr;
+} EXTENT_ITEM_DATA_REF;
+
+typedef struct {
+ EXTENT_ITEM_TREE eit;
+ UINT8 type;
+ TREE_BLOCK_REF tbr;
+} EXTENT_ITEM_TREE2;
+
+typedef struct {
+ EXTENT_ITEM ei;
+ UINT8 type;
+ TREE_BLOCK_REF tbr;
+} EXTENT_ITEM_SKINNY_METADATA;
+
+typedef struct {
+ CHUNK_ITEM ci;
+ CHUNK_ITEM_STRIPE stripes[1];
+} CHUNK_ITEM2;
+
+typedef struct {
+ LIST_ENTRY list_entry;
+ UINT64 key;
+} ordered_list;
+
+typedef struct {
+ ordered_list ol;
+ ULONG length;
+ UINT32* checksums;
+ BOOL deleted;
+} changed_sector;
+
+static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size);
+static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
+static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size);
+
+static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ write_context* context = conptr;
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL write_data_phys(PDEVICE_OBJECT device, UINT64 address, void* data, UINT32 length) {
+ NTSTATUS Status;
+ LARGE_INTEGER offset;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ write_context* context = NULL;
+
+ TRACE("(%p, %llx, %p, %x)\n", device, address, data, length);
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(write_context));
+
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ offset.QuadPart = address;
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
+
+ Irp = IoAllocateIrp(device->StackSize, FALSE);
+
+ if (!Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit2;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (device->Flags & DO_BUFFERED_IO) {
+ Irp->AssociatedIrp.SystemBuffer = data;
+
+ Irp->Flags = IRP_BUFFERED_IO;
+ } else if (device->Flags & DO_DIRECT_IO) {
+ Irp->MdlAddress = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ DbgPrint("IoAllocateMdl failed\n");
+ goto exit;
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ Irp->UserBuffer = data;
+ }
+
+ IrpSp->Parameters.Write.Length = length;
+ IrpSp->Parameters.Write.ByteOffset = offset;
+
+ Irp->UserIosb = &context->iosb;
+
+ Irp->UserEvent = &context->Event;
+
+ IoSetCompletionRoutine(Irp, write_completion, context, TRUE, TRUE, TRUE);
+
+ // FIXME - support multiple devices
+ Status = IoCallDriver(device, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&context->Event, Suspended, KernelMode, FALSE, NULL);
+ Status = context->iosb.Status;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCallDriver returned %08x\n", Status);
+ }
+
+ if (device->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+exit2:
+ if (context)
+ ExFreePool(context);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) {
+ NTSTATUS Status;
+ unsigned int i = 0;
+ UINT32 crc32;
+
+ // FIXME - work with RAID
+
+ // FIXME - only write one superblock if on SSD (?)
+ while (superblock_addrs[i] > 0 && Vcb->length >= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("writing superblock %u\n", i);
+
+ Vcb->superblock.sb_phys_addr = superblock_addrs[i];
+ RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM));
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
+ crc32 = ~crc32;
+ TRACE("crc32 is %08x\n", crc32);
+ RtlCopyMemory(&Vcb->superblock.checksum, &crc32, sizeof(UINT32));
+
+ Status = write_data_phys(device->devobj, superblock_addrs[i], &Vcb->superblock, sizeof(superblock));
+
+ if (!NT_SUCCESS(Status))
+ break;
+
+ i++;
+ }
+
+ return Status;
+}
+
+static BOOL find_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) {
+ LIST_ENTRY* le;
+ space *s, *bestfit = NULL;
+
+ TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address);
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->type == SPACE_TYPE_FREE) {
+ if (s->size == length) {
+ *address = s->offset;
+ TRACE("returning exact fit at %llx\n", s->offset);
+ return TRUE;
+ } else if (s->size > length && (!bestfit || bestfit->size > s->size)) {
+ bestfit = s;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (bestfit) {
+ TRACE("returning best fit at %llx\n", bestfit->offset);
+ *address = bestfit->offset;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type) {
+ LIST_ENTRY *le = c->space.Flink, *nextle, *insbef;
+ space *s, *s2, *s3;
+#ifdef DEBUG_PARANOID
+ UINT64 lastaddr;
+#endif
+
+ TRACE("(%p, %llx, %llx, %x)\n", c, offset, size, type);
+
+#ifdef DEBUG_PARANOID
+ // TESTING
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+#endif
+
+ c->space_changed = TRUE;
+
+ le = c->space.Flink;
+ insbef = &c->space;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+ nextle = le->Flink;
+
+ if (s->offset >= offset + size) {
+ insbef = le;
+ break;
+ }
+
+ if (s->offset >= offset && s->offset + s->size <= offset + size) { // delete entirely
+ RemoveEntryList(&s->list_entry);
+
+ if (s->offset + s->size == offset + size) {
+ insbef = s->list_entry.Flink;
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ break;
+ }
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ } else if (s->offset < offset && s->offset + s->size > offset + size) { // split in two
+ s3 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s3->offset = offset + size;
+ s3->size = s->size - size - offset + s->offset;
+ s3->type = s->type;
+ InsertHeadList(&s->list_entry, &s3->list_entry);
+ insbef = &s3->list_entry;
+
+ s->size = offset - s->offset;
+ break;
+ } else if (s->offset + s->size > offset && s->offset + s->size <= offset + size) { // truncate before
+ s->size = offset - s->offset;
+ } else if (s->offset < offset + size && s->offset + s->size > offset + size) { // truncate after
+ s->size -= s->offset - offset + size;
+ s->offset = offset + size;
+
+ insbef = le;
+ break;
+ }
+
+ le = nextle;
+ }
+
+ s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s2->offset = offset;
+ s2->size = size;
+ s2->type = type;
+ InsertTailList(insbef, &s2->list_entry);
+
+ // merge entries if same type
+
+ if (s2->list_entry.Blink != &c->space) {
+ s = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
+
+ if (s->type == type) {
+ s->size += s2->size;
+
+ RemoveEntryList(&s2->list_entry);
+ ExFreePool(s2);
+
+ s2 = s;
+ }
+ }
+
+ if (s2->list_entry.Flink != &c->space) {
+ s = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
+
+ if (s->type == type) {
+ s2->size += s->size;
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ }
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+
+#ifdef DEBUG_PARANOID
+ // TESTING
+ lastaddr = c->offset;
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset != lastaddr) {
+ ERR("inconsistency detected!\n");
+ int3;
+ }
+
+ lastaddr = s->offset + s->size;
+
+ le = le->Flink;
+ }
+
+ if (lastaddr != c->offset + c->chunk_item->size) {
+ ERR("inconsistency detected - space doesn't run all the way to end of chunk\n");
+ int3;
+ }
+#endif
+}
+
+chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) {
+ LIST_ENTRY* le2;
+ chunk* c;
+
+ le2 = Vcb->chunks.Flink;
+ while (le2 != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le2, chunk, list_entry);
+
+// TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
+
+ if (address >= c->offset && address < c->offset + c->chunk_item->size)
+ return c;
+
+ le2 = le2->Flink;
+ }
+
+ return NULL;
+}
+
+typedef struct {
+ disk_hole* dh;
+ device* device;
+} stripe;
+
+static void add_provisional_disk_hole(device_extension* Vcb, stripe* s, UINT64 max_stripe_size) {
+// LIST_ENTRY* le = s->device->disk_holes.Flink;
+// disk_hole* dh;
+
+// ERR("old holes:\n");
+// while (le != &s->device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+
+ if (s->dh->size <= max_stripe_size) {
+ s->dh->provisional = TRUE;
+ } else {
+ disk_hole* newdh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
+
+ newdh->address = s->dh->address + max_stripe_size;
+ newdh->size = s->dh->size - max_stripe_size;
+ newdh->provisional = FALSE;
+ InsertTailList(&s->device->disk_holes, &newdh->listentry);
+
+ s->dh->size = max_stripe_size;
+ s->dh->provisional = TRUE;
+ }
+
+// ERR("new holes:\n");
+// le = s->device->disk_holes.Flink;
+// while (le != &s->device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+}
+
+static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ UINT64 lastaddr;
+
+ searchkey.obj_id = 0x100;
+ searchkey.obj_type = TYPE_CHUNK_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return 0xffffffffffffffff;
+ }
+
+ lastaddr = 0;
+
+ do {
+ if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
+ CHUNK_ITEM* ci = (CHUNK_ITEM*)tp.item->data;
+
+ if (tp.item->key.offset >= lastaddr + size) {
+ free_traverse_ptr(&tp);
+ return lastaddr;
+ }
+
+ lastaddr = tp.item->key.offset + ci->size;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return lastaddr;
+}
+
+static BOOL increase_dev_item_used(device_extension* Vcb, device* device, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DEV_ITEM* di;
+
+ searchkey.obj_id = 1;
+ searchkey.obj_type = TYPE_DEV_ITEM;
+ searchkey.offset = device->devitem.dev_id;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find DEV_ITEM for device %llx\n", device->devitem.dev_id);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ device->devitem.bytes_used += size;
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
+ RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM));
+
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void reset_disk_holes(device* device, BOOL commit) {
+ LIST_ENTRY* le = device->disk_holes.Flink;
+ disk_hole* dh;
+
+// ERR("old holes:\n");
+// while (le != &device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+
+ le = device->disk_holes.Flink;
+ while (le != &device->disk_holes) {
+ LIST_ENTRY* le2 = le->Flink;
+
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ if (dh->provisional) {
+ if (commit) {
+ RemoveEntryList(le);
+ ExFreePool(dh);
+ } else {
+ dh->provisional = FALSE;
+ }
+ }
+
+ le = le2;
+ }
+
+ if (!commit) {
+ le = device->disk_holes.Flink;
+ while (le != &device->disk_holes) {
+ LIST_ENTRY* le2 = le->Flink;
+
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ while (le2 != &device->disk_holes) {
+ disk_hole* dh2 = CONTAINING_RECORD(le2, disk_hole, listentry);
+
+ if (dh2->address == dh->address + dh->size) {
+ LIST_ENTRY* le3 = le2->Flink;
+ dh->size += dh2->size;
+
+ RemoveEntryList(le2);
+ ExFreePool(dh2);
+
+ le2 = le3;
+ } else
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+// ERR("new holes:\n");
+// le = device->disk_holes.Flink;
+// while (le != &device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+}
+
+static NTSTATUS add_to_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, ULONG size) {
+ sys_chunk *sc, *sc2;
+ LIST_ENTRY* le;
+ USHORT i;
+
+ if (Vcb->superblock.n + sizeof(KEY) + size > SYS_CHUNK_ARRAY_SIZE) {
+ ERR("error - bootstrap is full\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
+
+ sc->key.obj_id = obj_id;
+ sc->key.obj_type = obj_type;
+ sc->key.offset = offset;
+ sc->size = size;
+ sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
+ RtlCopyMemory(sc->data, data, sc->size);
+
+ le = Vcb->sys_chunks.Flink;
+ while (le != &Vcb->sys_chunks) {
+ sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ if (keycmp(&sc2->key, &sc->key) == 1)
+ break;
+
+ le = le->Flink;
+ }
+ InsertTailList(le, &sc->list_entry);
+
+ Vcb->superblock.n += sizeof(KEY) + size;
+
+ i = 0;
+ le = Vcb->sys_chunks.Flink;
+ while (le != &Vcb->sys_chunks) {
+ sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ TRACE("%llx,%x,%llx\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset);
+
+ RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY));
+ i += sizeof(KEY);
+
+ RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size);
+ i += sc2->size;
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) {
+ UINT64 max_stripe_size, max_chunk_size, stripe_size;
+ UINT64 total_size = 0, i, j, logaddr;
+ int num_stripes;
+ disk_hole* dh;
+ stripe* stripes;
+ ULONG cisize;
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ chunk* c = NULL;
+ space* s = NULL;
+ BOOL success = FALSE;
+ BLOCK_GROUP_ITEM* bgi;
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ total_size += Vcb->devices[i].devitem.num_bytes;
+ }
+ TRACE("total_size = %llx\n", total_size);
+
+ if (flags & BLOCK_FLAG_DATA) {
+ max_stripe_size = 0x40000000; // 1 GB
+ max_chunk_size = 10 * max_stripe_size;
+ } else if (flags & BLOCK_FLAG_METADATA) {
+ if (total_size > 0xC80000000) // 50 GB
+ max_stripe_size = 0x40000000; // 1 GB
+ else
+ max_stripe_size = 0x10000000; // 256 MB
+
+ max_chunk_size = max_stripe_size;
+ } else if (flags & BLOCK_FLAG_SYSTEM) {
+ max_stripe_size = 0x2000000; // 32 MB
+ max_chunk_size = 2 * max_stripe_size;
+ }
+
+ // FIXME - make sure whole number of sectors?
+ max_chunk_size = min(max_chunk_size, total_size / 10); // cap at 10%
+
+ TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size);
+
+ if (flags & BLOCK_FLAG_DUPLICATE) {
+ num_stripes = 2;
+ } else if (flags & BLOCK_FLAG_RAID0) {
+ FIXME("RAID0 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID1) {
+ FIXME("RAID1 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID10) {
+ FIXME("RAID10 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID5) {
+ FIXME("RAID5 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID6) {
+ FIXME("RAID6 not yet supported\n");
+ return NULL;
+ } else { // SINGLE
+ num_stripes = 1;
+ }
+
+ stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < num_stripes; i++) {
+ stripes[i].dh = NULL;
+
+ for (j = 0; j < Vcb->superblock.num_devices; j++) {
+ LIST_ENTRY* le = Vcb->devices[j].disk_holes.Flink;
+
+ while (le != &Vcb->devices[j].disk_holes) {
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ if (!dh->provisional) {
+ if (!stripes[i].dh || dh->size > stripes[i].dh->size) {
+ stripes[i].dh = dh;
+ stripes[i].device = &Vcb->devices[j];
+
+ if (stripes[i].dh->size >= max_stripe_size)
+ break;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (stripes[i].dh && stripes[i].dh->size >= max_stripe_size)
+ break;
+ }
+
+ if (stripes[i].dh) {
+ TRACE("good DH: device %llx, address %llx, size %llx\n", stripes[i].device->devitem.dev_id, stripes[i].dh->address, stripes[i].dh->size);
+ } else {
+ TRACE("good DH not found\n");
+ goto end;
+ }
+
+ add_provisional_disk_hole(Vcb, &stripes[i], max_stripe_size);
+ }
+
+ stripe_size = min(stripes[0].dh->size, max_stripe_size);
+ for (i = 1; i < num_stripes; i++) {
+ stripe_size = min(stripe_size, stripes[1].dh->size);
+ }
+ // FIXME - make sure stripe_size aligned properly
+ // FIXME - obey max_chunk_size
+
+ c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+
+ // add CHUNK_ITEM to tree 3
+
+ cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
+ ci = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+
+ ci->size = stripe_size; // FIXME for RAID
+ ci->root_id = Vcb->extent_root->id;
+ ci->stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
+ ci->type = flags;
+ ci->opt_io_alignment = ci->stripe_length;
+ ci->opt_io_width = ci->stripe_length;
+ ci->sector_size = stripes[0].device->devitem.minimal_io_size;
+ ci->num_stripes = num_stripes;
+ ci->sub_stripes = 1;
+
+ c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < num_stripes; i++) {
+ if (i == 0)
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+ else
+ cis = &cis[1];
+
+ cis->dev_id = stripes[i].device->devitem.dev_id;
+ cis->offset = stripes[i].dh->address;
+ cis->dev_uuid = stripes[i].device->devitem.device_uuid;
+
+ c->devices[i] = stripes[i].device;
+ }
+
+ logaddr = find_new_chunk_address(Vcb, ci->size);
+ if (logaddr == 0xffffffffffffffff) {
+ ERR("find_new_chunk_address failed\n");
+ ExFreePool(ci);
+ goto end;
+ }
+
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ci);
+ goto end;
+ }
+
+ if (flags & BLOCK_FLAG_SYSTEM) {
+ NTSTATUS Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_to_bootstrap returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ Vcb->superblock.chunk_root_generation = Vcb->superblock.generation;
+
+ c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+ RtlCopyMemory(c->chunk_item, ci, cisize);
+ c->size = cisize;
+ c->offset = logaddr;
+ c->used = c->oldused = 0;
+ c->space_changed = FALSE;
+ InitializeListHead(&c->space);
+
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = c->offset;
+ s->size = c->chunk_item->size;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ protect_superblocks(Vcb, c);
+
+ // add BLOCK_GROUP_ITEM to tree 2
+
+ bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG);
+ bgi->used = 0;
+ bgi->chunk_tree = 0x100;
+ bgi->flags = flags;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, logaddr, TYPE_BLOCK_GROUP_ITEM, ci->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(bgi);
+ goto end;
+ }
+
+ // add DEV_EXTENTs to tree 4
+
+ for (i = 0; i < num_stripes; i++) {
+ DEV_EXTENT* de;
+
+ de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG);
+ de->chunktree = Vcb->chunk_root->id;
+ de->objid = 0x100;
+ de->address = logaddr;
+ de->length = ci->size;
+ de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid;
+
+ if (!insert_tree_item(Vcb, Vcb->dev_root, stripes[i].device->devitem.dev_id, TYPE_DEV_EXTENT, stripes[i].dh->address, de, sizeof(DEV_EXTENT), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(de);
+ goto end;
+ }
+
+ if (!increase_dev_item_used(Vcb, stripes[i].device, ci->size)) {
+ ERR("increase_dev_item_used failed\n");
+ goto end;
+ }
+ }
+
+ for (i = 0; i < num_stripes; i++) {
+ BOOL b = FALSE;
+ for (j = 0; j < i; j++) {
+ if (stripes[j].device == stripes[i].device)
+ b = TRUE;
+ }
+
+ if (!b)
+ reset_disk_holes(stripes[i].device, TRUE);
+ }
+
+ success = TRUE;
+
+end:
+ ExFreePool(stripes);
+
+ if (!success) {
+ for (i = 0; i < num_stripes; i++) {
+ BOOL b = FALSE;
+ for (j = 0; j < i; j++) {
+ if (stripes[j].device == stripes[i].device)
+ b = TRUE;
+ }
+
+ if (!b)
+ reset_disk_holes(stripes[i].device, FALSE);
+ }
+
+ if (c) ExFreePool(c);
+ if (s) ExFreePool(s);
+ } else
+ InsertTailList(&Vcb->chunks, &c->list_entry);
+
+ return success ? c : NULL;
+}
+
+static void decrease_chunk_usage(chunk* c, UINT64 delta) {
+ c->used -= delta;
+
+ TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
+static void increase_chunk_usage(chunk* c, UINT64 delta) {
+ c->used += delta;
+
+ TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
+static NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length) {
+ KEY searchkey;
+ traverse_ptr tp;
+ CHUNK_ITEM2* ci;
+ NTSTATUS Status;
+ UINT32 i;
+
+ TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
+
+ // FIXME - use version cached in Vcb
+
+ searchkey.obj_id = 0x100; // fixed?
+ searchkey.obj_type = TYPE_CHUNK_ITEM;
+ searchkey.offset = address;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - unexpected item in chunk tree\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ci = (CHUNK_ITEM2*)tp.item->data;
+
+ if (tp.item->key.offset > address || tp.item->key.offset + ci->ci.size < address) {
+ ERR("error - address %llx was out of chunk bounds\n", address);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // FIXME - only do this for chunks marked DUPLICATE?
+ // FIXME - for multiple writes, if PENDING do waits at the end
+ // FIXME - work with RAID
+ for (i = 0; i < ci->ci.num_stripes; i++) {
+ Status = write_data_phys(Vcb->devices[0].devobj, address - tp.item->key.offset + ci->stripes[i].offset, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - write_data_phys failed\n");
+ goto end;
+ }
+ }
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) {
+ LIST_ENTRY *le, *nextle;
+ space *s, *s2;
+
+// // TESTING
+// le = c->space.Flink;
+// while (le != &c->space) {
+// s = CONTAINING_RECORD(le, space, list_entry);
+//
+// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
+//
+// le = le->Flink;
+// }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+ nextle = le->Flink;
+
+ if (s->type == SPACE_TYPE_DELETING)
+ s->type = SPACE_TYPE_FREE;
+ else if (s->type == SPACE_TYPE_WRITING)
+ s->type = SPACE_TYPE_USED;
+
+ if (le->Blink != &c->space) {
+ s2 = CONTAINING_RECORD(le->Blink, space, list_entry);
+
+ if (s2->type == s->type) { // do merge
+ s2->size += s->size;
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ }
+ }
+
+ le = nextle;
+ }
+
+// le = c->space.Flink;
+// while (le != &c->space) {
+// s = CONTAINING_RECORD(le, space, list_entry);
+//
+// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
+//
+// le = le->Flink;
+// }
+}
+
+static void clean_space_cache(device_extension* Vcb) {
+ LIST_ENTRY* le;
+ chunk* c;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->space_changed) {
+ clean_space_cache_chunk(Vcb, c);
+ c->space_changed = FALSE;
+ }
+
+ le = le->Flink;
+ }
+}
+
+static BOOL trees_consistent(device_extension* Vcb) {
+ ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header);
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write) {
+ if (tc2->tree->header.num_items == 0)
+ return FALSE;
+
+ if (tc2->tree->size > maxsize)
+ return FALSE;
+
+ if (tc2->tree->new_address == 0)
+ return FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ return TRUE;
+}
+
+static NTSTATUS add_parents(device_extension* Vcb) {
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write) {
+ if (tc2->tree->parent)
+ add_to_tree_cache(Vcb, tc2->tree->parent, TRUE);
+ else if (tc2->tree->root != Vcb->chunk_root && tc2->tree->root != Vcb->root_root) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
+ ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+
+ RtlCopyMemory(ri, tp.item->data, tp.item->size);
+ RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+ }
+
+ free_traverse_ptr(&tp);
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void print_trees(LIST_ENTRY* tc) {
+ LIST_ENTRY *le, *le2;
+
+ le = tc->Flink;
+ while (le != tc) {
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ UINT32 num_items = 0;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ firstitem = td->key;
+ num_items++;
+ }
+ le2 = le2->Flink;
+ }
+
+ ERR("tree: root %llx, first key %llx,%x,%llx, level %x, num_items %x / %x\n",
+ tc2->tree->header.tree_id, firstitem.obj_id, firstitem.obj_type, firstitem.offset, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
+
+ le = le->Flink;
+ }
+}
+
+static void add_parents_to_cache(device_extension* Vcb, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ while (t->parent) {
+ t = t->parent;
+
+ add_to_tree_cache(Vcb, t, TRUE);
+ }
+
+ if (t->root == Vcb->root_root || t->root == Vcb->chunk_root)
+ return;
+
+ searchkey.obj_id = t->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+
+ free_traverse_ptr(&tp);
+}
+
+static BOOL insert_tree_extent_skinny(device_extension* Vcb, tree* t, chunk* c, UINT64 address) {
+ EXTENT_ITEM_SKINNY_METADATA* eism;
+ traverse_ptr insert_tp;
+
+ eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
+
+ eism->ei.refcount = 1;
+ eism->ei.generation = Vcb->superblock.generation;
+ eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
+ eism->type = TYPE_TREE_BLOCK_REF;
+ eism->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, t->header.level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eism);
+ return FALSE;
+ }
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ t->new_address = address;
+
+ return TRUE;
+}
+
+static BOOL insert_tree_extent(device_extension* Vcb, tree* t, chunk* c) {
+ UINT64 address;
+ EXTENT_ITEM_TREE2* eit2;
+ traverse_ptr insert_tp;
+
+ TRACE("(%p, %p, %p, %p)\n", Vcb, t, c);
+
+ if (!find_address_in_chunk(Vcb, c, Vcb->superblock.node_size, &address))
+ return FALSE;
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+ return insert_tree_extent_skinny(Vcb, t, c, address);
+
+ eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
+
+ eit2->eit.extent_item.refcount = 1;
+ eit2->eit.extent_item.generation = Vcb->superblock.generation;
+ eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
+// eit2->eit.firstitem = wt->firstitem;
+ eit2->eit.level = t->header.level;
+ eit2->type = TYPE_TREE_BLOCK_REF;
+ eit2->tbr.offset = t->header.tree_id;
+
+// #ifdef DEBUG_PARANOID
+// if (wt->firstitem.obj_type == 0xcc) { // TESTING
+// ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
+// ERR("num_items = %u, level = %u, root = %x, delete = %u\n", wt->tree->header.num_items, wt->tree->header.level, (UINT32)wt->tree->root->id, wt->delete);
+// int3;
+// }
+// #endif
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eit2);
+ return FALSE;
+ }
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ t->new_address = address;
+
+ return TRUE;
+}
+
+static NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t) {
+ chunk *origchunk = NULL, *c;
+ LIST_ENTRY* le;
+ UINT64 flags = t->flags;
+
+ if (flags == 0)
+ flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE;
+
+// TRACE("flags = %x\n", (UINT32)wt->flags);
+
+// if (!chunk_test) { // TESTING
+// if ((c = alloc_chunk(Vcb, flags))) {
+// if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+// if (insert_tree_extent(Vcb, t, c)) {
+// chunk_test = TRUE;
+// return STATUS_SUCCESS;
+// }
+// }
+// }
+// }
+
+ if (t->header.address != 0) {
+ origchunk = get_chunk_from_address(Vcb, t->header.address);
+
+ if (insert_tree_extent(Vcb, t, origchunk))
+ return STATUS_SUCCESS;
+ }
+
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ // FIXME - make sure to avoid superblocks
+
+ if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+ if (insert_tree_extent(Vcb, t, c))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ // allocate new chunk if necessary
+ if ((c = alloc_chunk(Vcb, flags))) {
+ if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+ if (insert_tree_extent(Vcb, t, c))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb->superblock.node_size);
+
+ return STATUS_DISK_FULL;
+}
+
+static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+ chunk* c;
+ EXTENT_ITEM_SKINNY_METADATA* eism;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_METADATA_ITEM;
+ searchkey.offset = t->header.level;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ eism = (EXTENT_ITEM_SKINNY_METADATA*)tp.item->data;
+ if (t->header.level == 0 && eism->ei.flags & EXTENT_ITEM_SHARED_BACKREFS && eism->type == TYPE_TREE_BLOCK_REF) {
+ // convert shared data extents
+
+ LIST_ENTRY* le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF;
+ }
+
+ c = get_chunk_from_address(Vcb, address);
+
+ if (c) {
+ decrease_chunk_usage(c, Vcb->superblock.node_size);
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_DELETING);
+ } else
+ ERR("could not find chunk for address %llx\n", address);
+
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+}
+
+// TESTING
+// static void check_tree_num_items(tree* t) {
+// LIST_ENTRY* le2;
+// UINT32 ni;
+//
+// le2 = t->itemlist.Flink;
+// ni = 0;
+// while (le2 != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+// if (!td->ignore)
+// ni++;
+// le2 = le2->Flink;
+// }
+//
+// if (t->header.num_items != ni) {
+// ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
+// int3;
+// } else {
+// ERR("tree %p okay\n", t);
+// }
+// }
+//
+// static void check_trees_num_items(LIST_ENTRY* tc) {
+// LIST_ENTRY* le = tc->Flink;
+// while (le != tc) {
+// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+//
+// check_tree_num_items(tc2->tree);
+//
+// le = le->Flink;
+// }
+// }
+
+static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp, tp2, insert_tp;
+ EXTENT_REF_V0* erv0;
+
+ TRACE("(%p, %p, %p)\n", Vcb, td, t);
+
+ searchkey.obj_id = td->treeholder.address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ searchkey.obj_id = td->treeholder.address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ if (keycmp(&searchkey, &tp2.item->key)) {
+ ERR("could not find %llx,%x,%llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ erv0 = (EXTENT_REF_V0*)tp.item->data;
+
+ delete_tree_item(Vcb, &tp);
+ delete_tree_item(Vcb, &tp2);
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+ EXTENT_ITEM_SKINNY_METADATA* eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
+
+ eism->ei.refcount = 1;
+ eism->ei.generation = erv0->gen;
+ eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
+ eism->type = TYPE_TREE_BLOCK_REF;
+ eism->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+ } else {
+ EXTENT_ITEM_TREE2* eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
+
+ eit2->eit.extent_item.refcount = 1;
+ eit2->eit.extent_item.generation = erv0->gen;
+ eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
+ eit2->eit.firstitem = td->key;
+ eit2->eit.level = t->header.level - 1;
+ eit2->type = TYPE_TREE_BLOCK_REF;
+ eit2->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+ }
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+ add_parents_to_cache(Vcb, tp.tree);
+ add_parents_to_cache(Vcb, tp2.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+}
+
+static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ EXTENT_ITEM_V0* eiv0;
+ chunk* c;
+
+ // FIXME - deal with refcounts > 1
+
+ TRACE("(%p, %llx, %p)\n", Vcb, address, t);
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+ if (reduce_tree_extent_skinny(Vcb, address, t)) {
+ return STATUS_SUCCESS;
+ }
+ }
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ int3;
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
+
+ if (eiv0->refcount > 1) {
+ FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0->refcount);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (ei->refcount > 1) {
+ FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei->refcount);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0 && ei->flags & EXTENT_ITEM_SHARED_BACKREFS) {
+ // convert shared data extents
+
+ LIST_ENTRY* le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF;
+ }
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ // if EXTENT_ITEM_V0, delete corresponding B4 item
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ traverse_ptr tp2;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ if (tp2.item->key.obj_id == searchkey.obj_id && tp2.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp2);
+ }
+ free_traverse_ptr(&tp2);
+ }
+ }
+
+ if (!(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
+ LIST_ENTRY* le;
+
+ // when writing old internal trees, convert related extents
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+// ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (t->header.level > 0) {
+ convert_old_tree_extent(Vcb, td, t);
+ } else if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert old data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_old_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ c = get_chunk_from_address(Vcb, address);
+
+ if (c) {
+ decrease_chunk_usage(c, tp.item->key.offset);
+
+ add_to_space_list(c, address, tp.item->key.offset, SPACE_TYPE_DELETING);
+ } else
+ ERR("could not find chunk for address %llx\n", address);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS allocate_tree_extents(device_extension* Vcb) {
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->new_address == 0) {
+ chunk* c;
+
+ Status = get_tree_new_address(Vcb, tc2->tree);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_tree_new_address returned %08x\n", Status);
+ return Status;
+ }
+
+ TRACE("allocated extent %llx\n", tc2->tree->new_address);
+
+ if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb, tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ c = get_chunk_from_address(Vcb, tc2->tree->new_address);
+
+ if (c) {
+ increase_chunk_usage(c, Vcb->superblock.node_size);
+ } else {
+ ERR("could not find chunk for address %llx\n", tc2->tree->new_address);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS update_root_root(device_extension* Vcb) {
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && !tc2->tree->parent) {
+ if (tc2->tree->root != Vcb->root_root && tc2->tree->root != Vcb->chunk_root) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ TRACE("updating the address for root %llx to %llx\n", searchkey.obj_id, tc2->tree->new_address);
+
+ tc2->tree->root->root_item.block_number = tc2->tree->new_address;
+ tc2->tree->root->root_item.root_level = tc2->tree->header.level;
+ tc2->tree->root->root_item.generation = Vcb->superblock.generation;
+ tc2->tree->root->root_item.generation2 = Vcb->superblock.generation;
+
+ if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, delete and create new entry
+ ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+
+ RtlCopyMemory(ri, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else
+ RtlCopyMemory(tp.item->data, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
+
+ free_traverse_ptr(&tp);
+ }
+
+ tc2->tree->root->treeholder.address = tc2->tree->new_address;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+enum write_tree_status {
+ WriteTreeStatus_Pending,
+ WriteTreeStatus_Success,
+ WriteTreeStatus_Error,
+ WriteTreeStatus_Cancelling,
+ WriteTreeStatus_Cancelled
+};
+
+struct write_tree_context;
+
+typedef struct {
+ struct write_tree_context* context;
+ UINT8* buf;
+ device* device;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum write_tree_status status;
+ LIST_ENTRY list_entry;
+} write_tree_stripe;
+
+typedef struct {
+ KEVENT Event;
+ LIST_ENTRY stripes;
+} write_tree_context;
+
+static NTSTATUS STDCALL write_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ write_tree_stripe* stripe = conptr;
+ write_tree_context* context = (write_tree_context*)stripe->context;
+ LIST_ENTRY* le;
+ BOOL complete;
+
+ if (stripe->status == WriteTreeStatus_Cancelling) {
+ stripe->status = WriteTreeStatus_Cancelled;
+ goto end;
+ }
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ stripe->status = WriteTreeStatus_Success;
+ } else {
+ le = context->stripes.Flink;
+
+ stripe->status = WriteTreeStatus_Error;
+
+ while (le != &context->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (s2->status == WriteTreeStatus_Pending) {
+ s2->status = WriteTreeStatus_Cancelling;
+ IoCancelIrp(s2->Irp);
+ }
+
+ le = le->Flink;
+ }
+ }
+
+end:
+ le = context->stripes.Flink;
+ complete = TRUE;
+
+ while (le != &context->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (s2->status == WriteTreeStatus_Pending || s2->status == WriteTreeStatus_Cancelling) {
+ complete = FALSE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (complete)
+ KeSetEvent(&context->Event, 0, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc) {
+ chunk* c;
+ CHUNK_ITEM_STRIPE* cis;
+ write_tree_stripe* stripe;
+ UINT64 i;
+
+ c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+
+ // FIXME - make this work with RAID
+
+ for (i = 0; i < c->chunk_item->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ // FIXME - handle missing devices
+
+ stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_stripe), ALLOC_TAG);
+ stripe->context = (struct write_tree_context*)wtc;
+ stripe->buf = data;
+ stripe->device = c->devices[i];
+ RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK));
+ stripe->status = WriteTreeStatus_Pending;
+
+ stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE);
+
+ if (!stripe->Irp) {
+ ERR("IoAllocateIrp failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (stripe->device->devobj->Flags & DO_BUFFERED_IO) {
+ stripe->Irp->AssociatedIrp.SystemBuffer = data;
+
+ stripe->Irp->Flags = IRP_BUFFERED_IO;
+ } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
+ stripe->Irp->MdlAddress = IoAllocateMdl(data, Vcb->superblock.node_size, FALSE, FALSE, NULL);
+ if (!stripe->Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ stripe->Irp->UserBuffer = data;
+ }
+
+ IrpSp->Parameters.Write.Length = Vcb->superblock.node_size;
+ IrpSp->Parameters.Write.ByteOffset.QuadPart = addr - c->offset + cis[i].offset;
+
+ stripe->Irp->UserIosb = &stripe->iosb;
+
+ IoSetCompletionRoutine(stripe->Irp, write_tree_completion, stripe, TRUE, TRUE, TRUE);
+
+ InsertTailList(&wtc->stripes, &stripe->list_entry);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void free_stripes(write_tree_context* wtc) {
+ LIST_ENTRY *le, *le2, *nextle;
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(stripe->Irp->MdlAddress);
+ IoFreeMdl(stripe->Irp->MdlAddress);
+ }
+
+ le = le->Flink;
+ }
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ nextle = le->Flink;
+
+ if (stripe->buf) {
+ ExFreePool(stripe->buf);
+
+ le2 = le->Flink;
+ while (le2 != &wtc->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le2, write_tree_stripe, list_entry);
+
+ if (s2->buf == stripe->buf)
+ s2->buf = NULL;
+
+ le2 = le2->Flink;
+ }
+
+ }
+
+ ExFreePool(stripe);
+
+ le = nextle;
+ }
+}
+
+static NTSTATUS write_trees(device_extension* Vcb) {
+ UINT8 level;
+ UINT8 *data, *body;
+ UINT32 crc32;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ write_tree_context* wtc;
+
+ TRACE("(%p)\n", Vcb);
+
+ for (level = 0; level <= 255; level++) {
+ BOOL nothing_found = TRUE;
+
+ TRACE("level = %u\n", level);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ KEY firstitem, searchkey;
+ LIST_ENTRY* le2;
+ traverse_ptr tp;
+ EXTENT_ITEM_TREE* eit;
+
+ if (tc2->tree->new_address == 0) {
+ ERR("error - tried to write tree with no new address\n");
+ int3;
+ }
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ firstitem = td->key;
+ break;
+ }
+ le2 = le2->Flink;
+ }
+
+ if (tc2->tree->parent) {
+ tc2->tree->paritem->key = firstitem;
+ tc2->tree->paritem->treeholder.address = tc2->tree->new_address;
+ tc2->tree->paritem->treeholder.generation = Vcb->superblock.generation;
+ }
+
+ if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) {
+ searchkey.obj_id = tc2->tree->new_address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+// traverse_ptr next_tp;
+// BOOL b;
+// tree_data* paritem;
+
+ ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ free_traverse_ptr(&tp);
+
+// searchkey.obj_id = 0;
+// searchkey.obj_type = 0;
+// searchkey.offset = 0;
+//
+// find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+//
+// paritem = NULL;
+// do {
+// if (tp.tree->paritem != paritem) {
+// paritem = tp.tree->paritem;
+// ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
+// }
+//
+// ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ eit = (EXTENT_ITEM_TREE*)tp.item->data;
+ eit->firstitem = firstitem;
+
+ free_traverse_ptr(&tp);
+ }
+
+ nothing_found = FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ if (nothing_found)
+ break;
+ }
+
+ TRACE("allocated tree extents\n");
+
+ wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG);
+ KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
+ InitializeListHead(&wtc->stripes);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+#ifdef DEBUG_PARANOID
+ UINT32 num_items = 0, size = 0;
+ LIST_ENTRY* le2;
+ BOOL crash = FALSE;
+#endif
+
+ if (tc2->write) {
+#ifdef DEBUG_PARANOID
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ num_items++;
+
+ if (tc2->tree->header.level == 0)
+ size += td->size;
+ }
+ le2 = le2->Flink;
+ }
+
+ if (tc2->tree->header.level == 0)
+ size += num_items * sizeof(leaf_node);
+ else
+ size += num_items * sizeof(internal_node);
+
+ if (num_items != tc2->tree->header.num_items) {
+ ERR("tree %llx, level %x: num_items was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
+ crash = TRUE;
+ }
+
+ if (size != tc2->tree->size) {
+ ERR("tree %llx, level %x: size was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, size, tc2->tree->size);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->new_address == 0) {
+ ERR("tree %llx, level %x: tried to write tree to address 0\n", tc2->tree->root->id, tc2->tree->header.level);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->header.num_items == 0) {
+ ERR("tree %llx, level %x: tried to write empty tree\n", tc2->tree->root->id, tc2->tree->header.level);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", tc2->tree->root->id, tc2->tree->header.level, tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+ crash = TRUE;
+ }
+
+ if (crash) {
+ ERR("tree %p\n", tc2->tree);
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ ERR("%llx,%x,%llx inserted=%u\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->inserted);
+ }
+ le2 = le2->Flink;
+ }
+ int3;
+ }
+#endif
+ tc2->tree->header.address = tc2->tree->new_address;
+ tc2->tree->header.generation = Vcb->superblock.generation;
+ tc2->tree->header.flags |= HEADER_FLAG_MIXED_BACKREF;
+
+ data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+ body = data + sizeof(tree_header);
+
+ RtlCopyMemory(data, &tc2->tree->header, sizeof(tree_header));
+ RtlZeroMemory(body, Vcb->superblock.node_size - sizeof(tree_header));
+
+ if (tc2->tree->header.level == 0) {
+ leaf_node* itemptr = (leaf_node*)body;
+ int i = 0;
+ LIST_ENTRY* le2;
+ UINT8* dataptr = data + Vcb->superblock.node_size;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ dataptr = dataptr - td->size;
+
+ itemptr[i].key = td->key;
+ itemptr[i].offset = (UINT8*)dataptr - (UINT8*)body;
+ itemptr[i].size = td->size;
+ i++;
+
+ RtlCopyMemory(dataptr, td->data, td->size);
+ }
+
+ le2 = le2->Flink;
+ }
+ } else {
+ internal_node* itemptr = (internal_node*)body;
+ int i = 0;
+ LIST_ENTRY* le2;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ itemptr[i].key = td->key;
+ itemptr[i].address = td->treeholder.address;
+ itemptr[i].generation = td->treeholder.generation;
+ i++;
+ }
+
+ le2 = le2->Flink;
+ }
+ }
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&((tree_header*)data)->fs_uuid, Vcb->superblock.node_size - sizeof(((tree_header*)data)->csum));
+ crc32 = ~crc32;
+ *((UINT32*)data) = crc32;
+ TRACE("setting crc32 to %08x\n", crc32);
+
+ Status = write_tree(Vcb, tc2->tree->new_address, data, wtc);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_tree returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ if (wtc->stripes.Flink != &wtc->stripes) {
+ // launch writes and wait
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ IoCallDriver(stripe->device->devobj, stripe->Irp);
+
+ le = le->Flink;
+ }
+
+ KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (!NT_SUCCESS(stripe->iosb.Status)) {
+ Status = stripe->iosb.Status;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ free_stripes(wtc);
+ }
+
+end:
+ ExFreePool(wtc);
+
+ return Status;
+}
+
+static NTSTATUS write_superblocks(device_extension* Vcb) {
+ UINT64 i;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && !tc2->tree->parent) {
+ if (tc2->tree->root == Vcb->root_root) {
+ Vcb->superblock.root_tree_addr = tc2->tree->new_address;
+ Vcb->superblock.root_level = tc2->tree->header.level;
+ } else if (tc2->tree->root == Vcb->chunk_root) {
+ Vcb->superblock.chunk_tree_addr = tc2->tree->new_address;
+ Vcb->superblock.chunk_root_generation = tc2->tree->header.generation;
+ Vcb->superblock.chunk_root_level = tc2->tree->header.level;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ if (Vcb->devices[i].devobj) {
+ Status = write_superblock(Vcb, &Vcb->devices[i]);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_superblock returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS update_chunk_usage(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ traverse_ptr tp;
+ BLOCK_GROUP_ITEM* bgi;
+
+ TRACE("(%p)\n", Vcb);
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->used != c->oldused) {
+ searchkey.obj_id = c->offset;
+ searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
+ searchkey.offset = c->chunk_item->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ int3;
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(bgi, tp.item->data, tp.item->size);
+ bgi->used = c->used;
+
+ TRACE("adjusting usage of chunk %llx to %llx\n", c->offset, c->used);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(bgi);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
+ TRACE("chunk_item type = %llx\n", c->chunk_item->type);
+
+ if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
+ FIXME("RAID0 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID1) {
+ FIXME("RAID1 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE) {
+ Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + (2 * (c->used - c->oldused));
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
+ FIXME("RAID10 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
+ FIXME("RAID5 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+ FIXME("RAID6 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else { // SINGLE
+ Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + c->used - c->oldused;
+ }
+
+ TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
+
+ free_traverse_ptr(&tp);
+
+ c->oldused = c->used;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void get_first_item(tree* t, KEY* key) {
+ LIST_ENTRY* le;
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ *key = td->key;
+ return;
+ }
+}
+
+static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirstitem, UINT32 numitems, UINT32 size) {
+ tree *nt, *pt;
+ tree_data* td;
+ tree_data* oldlastitem;
+// write_tree* wt2;
+// // tree_data *firsttd, *lasttd;
+// // LIST_ENTRY* le;
+// #ifdef DEBUG_PARANOID
+// KEY lastkey1, lastkey2;
+// traverse_ptr tp, next_tp;
+// ULONG numitems1, numitems2;
+// #endif
+
+ TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t->root->id, newfirstitem->key.obj_id, newfirstitem->key.obj_type, newfirstitem->key.offset);
+
+// #ifdef DEBUG_PARANOID
+// lastkey1.obj_id = 0xffffffffffffffff;
+// lastkey1.obj_type = 0xff;
+// lastkey1.offset = 0xffffffffffffffff;
+//
+// if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
+// ERR("error - find_item failed\n");
+// else {
+// lastkey1 = tp.item->key;
+// numitems1 = 0;
+// while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// numitems1++;
+// }
+// free_traverse_ptr(&tp);
+// }
+// #endif
+
+ nt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&nt->header, &t->header, sizeof(tree_header));
+ nt->header.address = 0;
+ nt->header.generation = Vcb->superblock.generation;
+ nt->header.num_items = t->header.num_items - numitems;
+ nt->header.flags = HEADER_FLAG_MIXED_BACKREF;
+
+ nt->refcount = 0;
+ nt->Vcb = Vcb;
+ nt->parent = t->parent;
+ nt->root = t->root;
+// nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ nt->new_address = 0;
+ nt->flags = t->flags;
+ InitializeListHead(&nt->itemlist);
+
+// ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
+
+ oldlastitem = CONTAINING_RECORD(newfirstitem->list_entry.Blink, tree_data, list_entry);
+
+// // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
+// // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
+// //
+// // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
+// // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
+// // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
+// //
+// // le = wt->tree->itemlist.Flink;
+// // while (le != &wt->tree->itemlist) {
+// // td = CONTAINING_RECORD(le, tree_data, list_entry);
+// // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // le = le->Flink;
+// // }
+
+ nt->itemlist.Flink = &newfirstitem->list_entry;
+ nt->itemlist.Blink = t->itemlist.Blink;
+ nt->itemlist.Flink->Blink = &nt->itemlist;
+ nt->itemlist.Blink->Flink = &nt->itemlist;
+
+ t->itemlist.Blink = &oldlastitem->list_entry;
+ t->itemlist.Blink->Flink = &t->itemlist;
+
+// // le = wt->tree->itemlist.Flink;
+// // while (le != &wt->tree->itemlist) {
+// // td = CONTAINING_RECORD(le, tree_data, list_entry);
+// // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // le = le->Flink;
+// // }
+// //
+// // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
+// // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
+// //
+// // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
+// // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
+// // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
+
+ nt->size = t->size - size;
+ t->size = size;
+ t->header.num_items = numitems;
+ add_to_tree_cache(Vcb, nt, TRUE);
+
+ InterlockedIncrement(&Vcb->open_trees);
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("created new split tree %p\n", nt);
+#endif
+ InsertTailList(&Vcb->trees, &nt->list_entry);
+
+// // // TESTING
+// // td = wt->tree->items;
+// // while (td) {
+// // if (!td->ignore) {
+// // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // }
+// // td = td->next;
+// // }
+
+// // oldlastitem->next = NULL;
+// // wt->tree->lastitem = oldlastitem;
+
+// // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
+
+ if (nt->parent) {
+ increase_tree_rc(nt->parent);
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = newfirstitem->key;
+
+ InsertAfter(&t->itemlist, &td->list_entry, &t->paritem->list_entry);
+
+ td->ignore = FALSE;
+ td->inserted = TRUE;
+ td->treeholder.tree = nt;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ nt->paritem = td;
+
+ nt->parent->header.num_items++;
+ nt->parent->size += sizeof(internal_node);
+
+ goto end;
+ }
+
+ TRACE("adding new tree parent\n");
+
+ if (nt->header.level == 255) {
+ ERR("cannot add parent to tree at level 255\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ pt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&pt->header, &nt->header, sizeof(tree_header));
+ pt->header.address = 0;
+ pt->header.num_items = 2;
+ pt->header.level = nt->header.level + 1;
+ pt->header.flags = HEADER_FLAG_MIXED_BACKREF;
+
+ pt->refcount = 2;
+ pt->Vcb = Vcb;
+ pt->parent = NULL;
+ pt->paritem = NULL;
+ pt->root = t->root;
+ pt->new_address = 0;
+// pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ pt->size = pt->header.num_items * sizeof(internal_node);
+ pt->flags = t->flags;
+ InitializeListHead(&pt->itemlist);
+
+// ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
+
+ InterlockedIncrement(&Vcb->open_trees);
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("created new parent tree %p\n", pt);
+#endif
+ InsertTailList(&Vcb->trees, &pt->list_entry);
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ get_first_item(t, &td->key);
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+ td->treeholder.address = 0;
+ td->treeholder.generation = Vcb->superblock.generation;
+ td->treeholder.tree = t;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ InsertTailList(&pt->itemlist, &td->list_entry);
+ t->paritem = td;
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = newfirstitem->key;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+ td->treeholder.address = 0;
+ td->treeholder.generation = Vcb->superblock.generation;
+ td->treeholder.tree = nt;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ InsertTailList(&pt->itemlist, &td->list_entry);
+ nt->paritem = td;
+
+ add_to_tree_cache(Vcb, pt, TRUE);
+
+ t->root->treeholder.tree = pt;
+
+ t->parent = pt;
+ nt->parent = pt;
+
+end:
+
+// #ifdef DEBUG_PARANOID
+// lastkey2.obj_id = 0xffffffffffffffff;
+// lastkey2.obj_type = 0xff;
+// lastkey2.offset = 0xffffffffffffffff;
+//
+// if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
+// ERR("error - find_item failed\n");
+// else {
+// lastkey2 = tp.item->key;
+//
+// numitems2 = 0;
+// while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// numitems2++;
+// }
+// free_traverse_ptr(&tp);
+// }
+//
+// ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
+// ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
+// ERR("numitems1 = %u\n", numitems1);
+// ERR("numitems2 = %u\n", numitems2);
+// #endif
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL split_tree(device_extension* Vcb, tree* t) {
+ LIST_ENTRY* le;
+ UINT32 size, ds, numitems;
+
+ size = 0;
+ numitems = 0;
+
+ // FIXME - naïve implementation: maximizes number of filled trees
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ if (t->header.level == 0)
+ ds = sizeof(leaf_node) + td->size;
+ else
+ ds = sizeof(internal_node);
+
+ // FIXME - move back if previous item was deleted item with same key
+ if (size + ds > Vcb->superblock.node_size - sizeof(tree_header))
+ return split_tree_at(Vcb, t, td, numitems, size);
+
+ size += ds;
+ numitems++;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t) {
+ LIST_ENTRY* le;
+ tree_data* nextparitem = NULL;
+ NTSTATUS Status;
+ tree *next_tree, *par;
+
+ TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t->root->id, t->header.level, t->size);
+
+ // FIXME - doesn't capture everything, as it doesn't ascend
+ // FIXME - write proper function and put it in treefuncs.c
+ le = t->paritem->list_entry.Flink;
+ while (le != &t->parent->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ nextparitem = td;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!nextparitem)
+ return STATUS_SUCCESS;
+
+ // FIXME - loop, and capture more than one tree if we can
+
+ TRACE("nextparitem: key = %llx,%x,%llx, address = %llx, tree = %p\n", nextparitem->key.obj_id, nextparitem->key.obj_type, nextparitem->key.offset, nextparitem->address, nextparitem->tree);
+// nextparitem = t->paritem;
+
+// ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
+
+ if (do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem))
+ increase_tree_rc(t->parent);
+
+// ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
+
+ next_tree = nextparitem->treeholder.tree;
+
+ if (t->size + next_tree->size <= Vcb->superblock.node_size - sizeof(tree_header)) {
+ // merge two trees into one
+
+ t->header.num_items += next_tree->header.num_items;
+ t->size += next_tree->size;
+
+ t->itemlist.Blink->Flink = next_tree->itemlist.Flink;
+ t->itemlist.Blink->Flink->Blink = t->itemlist.Blink;
+ t->itemlist.Blink = next_tree->itemlist.Blink;
+ t->itemlist.Blink->Flink = &t->itemlist;
+
+// // TESTING
+// le = t->itemlist.Flink;
+// while (le != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+// if (!td->ignore) {
+// ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+// }
+// le = le->Flink;
+// }
+
+ next_tree->itemlist.Flink = next_tree->itemlist.Blink = &next_tree->itemlist;
+
+ next_tree->header.num_items = 0;
+ next_tree->size = 0;
+
+ if (next_tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ free_tree(next_tree);
+ return Status;
+ }
+ } else if (next_tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ free_tree(next_tree);
+ return Status;
+ }
+ }
+
+ if (!nextparitem->ignore) {
+ nextparitem->ignore = TRUE;
+ next_tree->parent->header.num_items--;
+ next_tree->parent->size -= sizeof(internal_node);
+ }
+
+ par = next_tree->parent;
+ while (par) {
+ add_to_tree_cache(Vcb, par, TRUE);
+ par = par->parent;
+ }
+
+ RemoveEntryList(&nextparitem->list_entry);
+ ExFreePool(next_tree->paritem);
+ next_tree->paritem = NULL;
+
+ free_tree(next_tree);
+
+ // remove next_tree from tree cache
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree == next_tree) {
+ free_tree(next_tree);
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ break;
+ }
+
+ le = le->Flink;
+ }
+ } else {
+ // rebalance by moving items from second tree into first
+ ULONG avg_size = (t->size + next_tree->size) / 2;
+ KEY firstitem = {0, 0, 0};
+
+ TRACE("attempting rebalance\n");
+
+ le = next_tree->itemlist.Flink;
+ while (le != &next_tree->itemlist && t->size < avg_size && next_tree->header.num_items > 1) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+ ULONG size;
+
+ if (!td->ignore) {
+ if (next_tree->header.level == 0)
+ size = sizeof(leaf_node) + td->size;
+ else
+ size = sizeof(internal_node);
+ } else
+ size = 0;
+
+ if (t->size + size < Vcb->superblock.node_size - sizeof(tree_header)) {
+ RemoveEntryList(&td->list_entry);
+ InsertTailList(&t->itemlist, &td->list_entry);
+
+ if (!td->ignore) {
+ next_tree->size -= size;
+ t->size += size;
+ next_tree->header.num_items--;
+ t->header.num_items++;
+ }
+ } else
+ break;
+
+ le = next_tree->itemlist.Flink;
+ }
+
+ le = next_tree->itemlist.Flink;
+ while (le != &next_tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ firstitem = td->key;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+// ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
+
+ // FIXME - once ascension is working, make this work with parent's parent, etc.
+ if (next_tree->paritem)
+ next_tree->paritem->key = firstitem;
+
+ par = next_tree;
+ while (par) {
+ add_to_tree_cache(Vcb, par, TRUE);
+ par = par->parent;
+ }
+
+ free_tree(next_tree);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL do_splits(device_extension* Vcb) {
+// LIST_ENTRY *le, *le2;
+// write_tree* wt;
+// tree_data* td;
+ UINT8 level, max_level;
+ UINT32 min_size;
+ BOOL empty, done_deletions = FALSE;
+ NTSTATUS Status;
+ tree_cache* tc2;
+
+ TRACE("(%p)\n", Vcb);
+
+ max_level = 0;
+
+ for (level = 0; level <= 255; level++) {
+ LIST_ENTRY *le, *nextle;
+
+ empty = TRUE;
+
+ TRACE("doing level %u\n", level);
+
+ le = Vcb->tree_cache.Flink;
+
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ nextle = le->Flink;
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ empty = FALSE;
+
+ if (tc2->tree->header.num_items == 0) {
+ LIST_ENTRY* le2;
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+
+ done_deletions = TRUE;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ firstitem = td->key;
+ break;
+ }
+
+ ERR("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
+ tc2->tree->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
+
+ if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ if (tc2->tree->parent) {
+ if (!tc2->tree->paritem->ignore) {
+ tc2->tree->paritem->ignore = TRUE;
+ tc2->tree->parent->header.num_items--;
+ tc2->tree->parent->size -= sizeof(internal_node);
+ }
+
+ RemoveEntryList(&tc2->tree->paritem->list_entry);
+ ExFreePool(tc2->tree->paritem);
+ tc2->tree->paritem = NULL;
+
+ free_tree(tc2->tree);
+
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ } else {
+ FIXME("trying to delete top root, not sure what to do here\n"); // FIXME
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ TRACE("splitting overlarge tree (%x > %x)\n", tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+ Status = split_tree(Vcb, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("split_tree returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+ }
+
+ le = nextle;
+ }
+
+ if (!empty) {
+ max_level = level;
+ } else {
+ TRACE("nothing found for level %u\n", level);
+ break;
+ }
+ }
+
+ min_size = (Vcb->superblock.node_size - sizeof(tree_header)) / 2;
+
+ for (level = 0; level <= max_level; level++) {
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level && tc2->tree->header.num_items > 0 && tc2->tree->parent && tc2->tree->size < min_size) {
+ Status = try_tree_amalgamate(Vcb, tc2->tree);
+ if (!NT_SUCCESS(Status)) {
+ ERR("try_tree_amalgamate returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ // simplify trees if top tree only has one entry
+
+ if (done_deletions) {
+ for (level = max_level; level > 0; level--) {
+ LIST_ENTRY *le, *nextle;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ nextle = le->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ if (!tc2->tree->parent && tc2->tree->header.num_items == 1) {
+ LIST_ENTRY* le2 = tc2->tree->itemlist.Flink;
+ tree_data* td;
+ tree* child_tree = NULL;
+
+ while (le2 != &tc2->tree->itemlist) {
+ td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore)
+ break;
+ le2 = le2->Flink;
+ }
+
+ ERR("deleting top-level tree in root %llx with one item\n", tc2->tree->root->id);
+
+ if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ if (!td->treeholder.tree) { // load first item if not already loaded
+ KEY searchkey = {0,0,0};
+ traverse_ptr tp;
+
+ if (!find_item(Vcb, tc2->tree->root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", tc2->tree->root->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ child_tree = td->treeholder.tree;
+
+ if (child_tree) {
+ child_tree->parent = NULL;
+ free_tree(tc2->tree);
+ }
+
+ free_tree(tc2->tree);
+
+ if (child_tree)
+ child_tree->root->treeholder.tree = child_tree;
+
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ }
+ }
+
+ le = nextle;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL do_write(device_extension* Vcb) {
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ // If only changing superblock, e.g. changing label, we still need to rewrite
+ // the root tree so the generations mach. Otherwise you won't be able to mount on Linux.
+ if (Vcb->write_trees > 0) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ add_to_tree_cache(Vcb, Vcb->root_root->treeholder.tree, TRUE);
+
+ free_traverse_ptr(&tp);
+ }
+
+ do {
+ Status = add_parents(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_parents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = do_splits(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_splits returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = allocate_tree_extents(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_parents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = update_chunk_usage(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_chunk_usage returned %08x\n", Status);
+ goto end;
+ }
+ } while (!trees_consistent(Vcb));
+
+ TRACE("trees consistent\n");
+
+ Status = update_root_root(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_root returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = write_trees(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_trees returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = write_superblocks(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_superblocks returned %08x\n", Status);
+ goto end;
+ }
+
+ clean_space_cache(Vcb);
+
+ Vcb->superblock.generation++;
+
+// print_trees(tc); // TESTING
+
+ Status = STATUS_SUCCESS;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ tc2->write = FALSE;
+
+ le = le->Flink;
+ }
+
+ Vcb->write_trees = 0;
+
+end:
+ TRACE("do_write returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS consider_write(device_extension* Vcb) {
+ // FIXME - call do_write if Vcb->write_trees high
+
+ return STATUS_SUCCESS;
+}
+
+static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* ins) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > ins->key) {
+ le->Blink->Flink = &ins->list_entry;
+ ins->list_entry.Blink = le->Blink;
+ le->Blink = &ins->list_entry;
+ ins->list_entry.Flink = le;
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ InsertTailList(list, &ins->list_entry);
+}
+
+static UINT64 get_extent_data_ref_hash(UINT64 root, UINT64 objid, UINT64 offset) {
+ UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff;
+
+ // FIXME - can we test this?
+
+ // FIXME - make sure numbers here are little-endian
+ high_crc = calc_crc32c(high_crc, (UINT8*)&root, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&objid, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&offset, sizeof(UINT64));
+
+ return ((UINT64)high_crc << 31) ^ (UINT64)low_crc;
+}
+
+NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT8 *siptr, *type;
+ ULONG len;
+ UINT64 hash;
+ EXTENT_DATA_REF* edr;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ // create new entry
+
+ len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+ free_traverse_ptr(&tp);
+
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+ ei->refcount = 1;
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = EXTENT_ITEM_DATA;
+
+ type = (UINT8*)&ei[1];
+ *type = TYPE_EXTENT_DATA_REF;
+
+ edr = (EXTENT_DATA_REF*)&type[1];
+ edr->root = subvol->id;
+ edr->objid = inode;
+ edr->offset = offset;
+ edr->count = 1;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - update free space in superblock and CHUNK_ITEM
+
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ }
+
+ if (ei->flags != EXTENT_ITEM_DATA) {
+ ERR("error - flag was not EXTENT_ITEM_DATA\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
+
+ hash = get_extent_data_ref_hash(subvol->id, inode, offset);
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ siptr = (UINT8*)&ei[1];
+
+ // FIXME - increase subitem refcount if there already?
+ do {
+ if (*siptr == TYPE_EXTENT_DATA_REF) {
+ UINT64 sihash;
+
+ edr = (EXTENT_DATA_REF*)&siptr[1];
+ sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset);
+
+ if (sihash >= hash)
+ break;
+
+ siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+
+ if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+ len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+ } else
+ break;
+ // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+
+ RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+ ei->refcount++;
+
+ type = (UINT8*)ei + (siptr - tp.item->data);
+ *type = TYPE_EXTENT_DATA_REF;
+
+ edr = (EXTENT_DATA_REF*)&type[1];
+ edr->root = subvol->id;
+ edr->objid = inode;
+ edr->offset = offset;
+ edr->count = 1;
+
+ if (siptr < tp.item->data + tp.item->size)
+ RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr);
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ EXTENT_DATA_REF edr;
+ LIST_ENTRY list_entry;
+} data_ref;
+
+static void add_data_ref(LIST_ENTRY* data_refs, UINT64 root, UINT64 objid, UINT64 offset) {
+ data_ref* dr = ExAllocatePoolWithTag(PagedPool, sizeof(data_ref), ALLOC_TAG);
+
+ // FIXME - increase count if entry there already
+ // FIXME - put in order?
+
+ dr->edr.root = root;
+ dr->edr.objid = objid;
+ dr->edr.offset = offset;
+ dr->edr.count = 1;
+
+ InsertTailList(data_refs, &dr->list_entry);
+}
+
+static void free_data_refs(LIST_ENTRY* data_refs) {
+ LIST_ENTRY* le;
+
+ while ((le = RemoveHeadList(data_refs)) != data_refs) {
+ data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
+
+ ExFreePool(dr);
+ }
+}
+
+static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LIST_ENTRY data_refs;
+ LIST_ENTRY* le;
+ UINT64 refcount;
+ EXTENT_ITEM* ei;
+ ULONG eisize;
+ UINT8* type;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size != sizeof(EXTENT_ITEM_V0)) {
+ TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ InitializeListHead(&data_refs);
+
+ do {
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ tree* t;
+
+ // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
+
+ t = load_tree(Vcb, tp.item->key.offset, NULL);
+
+ if (!t) {
+ ERR("couldn't load tree at %llx\n", tp.item->key.offset);
+ free_traverse_ptr(&tp);
+ free_data_refs(&data_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0) {
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address == address)
+ add_data_ref(&data_refs, t->header.tree_id, td->key.obj_id, td->key.offset);
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ free_tree(t);
+
+ delete_tree_item(Vcb, &tp);
+ }
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (IsListEmpty(&data_refs)) {
+ WARN("no data refs found\n");
+ return STATUS_SUCCESS;
+ }
+
+ // create new entry
+
+ refcount = 0;
+
+ le = data_refs.Flink;
+ while (le != &data_refs) {
+ refcount++;
+ le = le->Flink;
+ }
+
+ eisize = sizeof(EXTENT_ITEM) + ((sizeof(char) + sizeof(EXTENT_DATA_REF)) * refcount);
+ ei = ExAllocatePoolWithTag(PagedPool, eisize, ALLOC_TAG);
+
+ ei->refcount = refcount;
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = EXTENT_ITEM_DATA;
+
+ type = (UINT8*)&ei[1];
+
+ le = data_refs.Flink;
+ while (le != &data_refs) {
+ data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
+
+ type[0] = TYPE_EXTENT_DATA_REF;
+ RtlCopyMemory(&type[1], &dr->edr, sizeof(EXTENT_DATA_REF));
+
+ type = &type[1 + sizeof(EXTENT_DATA_REF)];
+
+ le = le->Flink;
+ }
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_data_refs(&data_refs);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ UINT8 type;
+ void* data;
+ BOOL allocated;
+ LIST_ENTRY list_entry;
+} extent_ref;
+
+static void free_extent_refs(LIST_ENTRY* extent_refs) {
+ LIST_ENTRY* le;
+
+ while ((le = RemoveHeadList(extent_refs)) != extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+}
+
+static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp;
+ LIST_ENTRY extent_refs;
+ LIST_ENTRY *le, *next_le;
+ EXTENT_ITEM *ei, *newei;
+ UINT8* siptr;
+ ULONG len;
+ UINT64 count;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+
+ InitializeListHead(&extent_refs);
+
+ siptr = (UINT8*)&ei[1];
+
+ do {
+ extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+
+ er->type = *siptr;
+ er->data = siptr+1;
+ er->allocated = FALSE;
+
+ InsertTailList(&extent_refs, &er->list_entry);
+
+ if (*siptr == TYPE_TREE_BLOCK_REF) {
+ siptr += sizeof(TREE_BLOCK_REF);
+ len -= sizeof(TREE_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_EXTENT_DATA_REF) {
+ siptr += sizeof(EXTENT_DATA_REF);
+ len -= sizeof(EXTENT_DATA_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
+ siptr += sizeof(SHARED_BLOCK_REF);
+ len -= sizeof(SHARED_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_DATA_REF) {
+ siptr += sizeof(SHARED_DATA_REF);
+ len -= sizeof(SHARED_DATA_REF) + 1;
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ next_le = le->Flink;
+
+ if (er->type == TYPE_SHARED_DATA_REF) {
+ // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
+ SHARED_DATA_REF* sdr = er->data;
+ tree* t = load_tree(Vcb, sdr->offset, NULL);
+
+ if (!t) {
+ ERR("couldn't load tree at %llx\n", sdr->offset);
+ free_traverse_ptr(&tp);
+ free_data_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0) {
+ LIST_ENTRY* le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+
+ if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address == address) {
+ extent_ref* er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+ EXTENT_DATA_REF* edr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA_REF), ALLOC_TAG);
+
+ edr->root = t->header.tree_id;
+ edr->objid = td->key.obj_id;
+ edr->offset = td->key.offset;
+ edr->count = 1;
+
+ er2->type = TYPE_EXTENT_DATA_REF;
+ er2->data = edr;
+ er2->allocated = TRUE;
+
+ InsertTailList(&extent_refs, &er2->list_entry); // FIXME - list should be in order
+ }
+ }
+ }
+
+ le2 = le2->Flink;
+ }
+ }
+
+ free_tree(t);
+
+ RemoveEntryList(&er->list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+ // FIXME - also do for SHARED_BLOCK_REF?
+
+ le = next_le;
+ }
+
+ if (IsListEmpty(&extent_refs)) {
+ WARN("no extent refs found\n");
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ len = 0;
+ count = 0;
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ len++;
+ if (er->type == TYPE_TREE_BLOCK_REF) {
+ len += sizeof(TREE_BLOCK_REF);
+ } else if (er->type == TYPE_EXTENT_DATA_REF) {
+ len += sizeof(EXTENT_DATA_REF);
+ } else {
+ ERR("unexpected extent subitem %x\n", er->type);
+ }
+
+ count++;
+
+ le = le->Flink;
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM) + len, ALLOC_TAG);
+ RtlCopyMemory(newei, ei, sizeof(EXTENT_ITEM));
+ newei->refcount = count;
+
+ siptr = (UINT8*)&newei[1];
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ *siptr = er->type;
+ siptr++;
+
+ if (er->type == TYPE_TREE_BLOCK_REF) {
+ RtlCopyMemory(siptr, er->data, sizeof(TREE_BLOCK_REF));
+ } else if (er->type == TYPE_EXTENT_DATA_REF) {
+ RtlCopyMemory(siptr, er->data, sizeof(EXTENT_DATA_REF));
+ } else {
+ ERR("unexpected extent subitem %x\n", er->type);
+ }
+
+ le = le->Flink;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, newei, sizeof(EXTENT_ITEM) + len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(newei);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_extent_refs(&extent_refs);
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len) {
+ UINT8* siptr = (UINT8*)&ei[1];
+
+ do {
+ if (*siptr == TYPE_TREE_BLOCK_REF) {
+ siptr += sizeof(TREE_BLOCK_REF) + 1;
+ len -= sizeof(TREE_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_EXTENT_DATA_REF) {
+ siptr += sizeof(EXTENT_DATA_REF) + 1;
+ len -= sizeof(EXTENT_DATA_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
+ return TRUE;
+ } else if (*siptr == TYPE_SHARED_DATA_REF) {
+ return TRUE;
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ return FALSE;
+ }
+ } while (len > 0);
+
+ return FALSE;
+}
+
+NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT8* siptr;
+ ULONG len;
+ EXTENT_DATA_REF* edr;
+ BOOL found;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (!(ei->flags & EXTENT_ITEM_DATA)) {
+ ERR("error - EXTENT_ITEM_DATA flag not set\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
+
+ if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ }
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ siptr = (UINT8*)&ei[1];
+ found = FALSE;
+
+ do {
+ if (*siptr == TYPE_EXTENT_DATA_REF) {
+ edr = (EXTENT_DATA_REF*)&siptr[1];
+
+ if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
+ found = TRUE;
+ break;
+ }
+
+ siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+
+ if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+ len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+ } else
+ break;
+// // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ if (!found) {
+ WARN("could not find extent data ref\n");
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ // FIXME - decrease subitem refcount if there already?
+
+ len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed
+ chunk* c;
+ LIST_ENTRY* le2;
+
+ if (changed_sector_list) {
+ changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = address;
+ sc->checksums = NULL;
+ sc->length = size / Vcb->superblock.sector_size;
+
+ sc->deleted = TRUE;
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ c = NULL;
+ le2 = Vcb->chunks.Flink;
+ while (le2 != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le2, chunk, list_entry);
+
+ TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
+
+ if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
+ break;
+
+ le2 = le2->Flink;
+ }
+ if (le2 == &Vcb->chunks) c = NULL;
+
+ if (c) {
+ decrease_chunk_usage(c, size);
+
+ add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
+ }
+
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+ RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+ ei->refcount--;
+ ei->generation = Vcb->superblock.generation;
+
+ if (tp.item->data + len != siptr)
+ RtlCopyMemory((UINT8*)ei + (siptr - tp.item->data), siptr + sizeof(UINT8) + sizeof(EXTENT_DATA_REF), tp.item->size - (siptr - tp.item->data) - sizeof(UINT8) - sizeof(EXTENT_DATA_REF));
+
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > value)
+ return FALSE;
+ else if (ol->key == value)
+ return TRUE;
+
+ le = le->Flink;
+ }
+
+ return FALSE;
+}
+
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, changed_sector_list);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.offset < end_data && tp.item->key.offset + ed->decoded_size >= start_data) {
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME - compression not supported at present\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, ed->encryption);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ // FIXME - is ed->decoded_size the size of the whole extent, or just this bit of it?
+
+ if (ed->type == EXTENT_TYPE_INLINE) {
+ if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+ delete_tree_item(Vcb, &tp);
+
+ fcb->inode_item.st_blocks -= ed->decoded_size;
+ } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = ed->decoded_size - (end_data - tp.item->key.offset);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = start_data - tp.item->key.offset;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[0], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
+ } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = start_data - tp.item->key.offset;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[0], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ size = tp.item->key.offset + ed->decoded_size - end_data;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - start_data;
+ }
+ } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+
+ if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+ if (ed2->address != 0) {
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= ed->decoded_size;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = ed->decoded_size - (end_data - tp.item->key.offset);
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
+ ned2->num_bytes = ed2->num_bytes - (end_data - tp.item->key.offset);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0)
+ fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = start_data - tp.item->key.offset;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - start_data;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = start_data - tp.item->key.offset;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = tp.item->key.offset + ed->decoded_size - end_data;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
+ ned2->num_bytes = tp.item->key.offset + ed->decoded_size - end_data;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ }
+ }
+ }
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > fcb->inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
+ break;
+ }
+ } while (b);
+
+ // FIXME - do bitmap analysis of changed extents, and free what we can
+
+ Status = STATUS_SUCCESS;
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+static BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ UINT64 address;
+ NTSTATUS Status;
+ EXTENT_ITEM_DATA_REF* eidr;
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
+ changed_sector* sc;
+ traverse_ptr tp;
+ int i;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, data, changed_sector_list);
+
+ if (!find_address_in_chunk(Vcb, c, length, &address))
+ return FALSE;
+
+ eidr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_DATA_REF), ALLOC_TAG);
+ eidr->ei.refcount = 1;
+ eidr->ei.generation = Vcb->superblock.generation;
+ eidr->ei.flags = EXTENT_ITEM_DATA;
+ eidr->type = TYPE_EXTENT_DATA_REF;
+ eidr->edr.root = fcb->subvol->id;
+ eidr->edr.objid = fcb->inode;
+ eidr->edr.offset = start_data;
+ eidr->edr.count = 1;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, length, eidr, sizeof(EXTENT_ITEM_DATA_REF), &tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eidr);
+ return FALSE;
+ }
+
+ tp.tree->header.generation = eidr->ei.generation;
+
+ free_traverse_ptr(&tp);
+
+ Status = write_data(Vcb, address, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (changed_sector_list) {
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = address;
+ sc->length = length / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ // add extent data to inode
+ ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
+ ed->generation = Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = address;
+ ed2->size = length;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, ed, edsize, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ed);
+ return FALSE;
+ }
+
+ increase_chunk_usage(c, length);
+ add_to_space_list(c, address, length, SPACE_TYPE_WRITING);
+
+ fcb->inode_item.st_blocks += length;
+
+ return TRUE;
+}
+
+static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
+ LIST_ENTRY* changed_sector_list, traverse_ptr* edtp, traverse_ptr* eitp) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ EXTENT_ITEM* ei;
+ NTSTATUS Status;
+ changed_sector* sc;
+ chunk* c;
+ int i;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data,
+ length, data, changed_sector_list, edtp, eitp);
+
+ ed = ExAllocatePoolWithTag(PagedPool, edtp->item->size, ALLOC_TAG);
+ RtlCopyMemory(ed, edtp->item->data, edtp->item->size);
+
+ ed->decoded_size += length;
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->size += length;
+ ed2->num_bytes += length;
+
+ delete_tree_item(Vcb, edtp);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, edtp->item->key.obj_id, edtp->item->key.obj_type, edtp->item->key.offset, ed, edtp->item->size, NULL)) {
+ TRACE("insert_tree_item failed\n");
+
+ ExFreePool(ed);
+ return FALSE;
+ }
+
+ ei = ExAllocatePoolWithTag(PagedPool, eitp->item->size, ALLOC_TAG);
+ RtlCopyMemory(ei, eitp->item->data, eitp->item->size);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, eitp->item->key.obj_id, eitp->item->key.obj_type, eitp->item->key.offset + length, ei, eitp->item->size, NULL)) {
+ ERR("insert_tree_item failed\n");
+
+ ExFreePool(ei);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, eitp);
+
+ Status = write_data(Vcb, eitp->item->key.obj_id + eitp->item->key.offset, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (changed_sector_list) {
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = eitp->item->key.obj_id + eitp->item->key.offset;
+ sc->length = length / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ c = get_chunk_from_address(Vcb, eitp->item->key.obj_id);
+
+ if (c) {
+ increase_chunk_usage(c, length);
+
+ add_to_space_list(c, eitp->item->key.obj_id + eitp->item->key.offset, length, SPACE_TYPE_WRITING);
+ }
+
+ fcb->inode_item.st_blocks += length;
+
+ return TRUE;
+}
+
+static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
+ LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ BOOL success = FALSE;
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ EXTENT_ITEM* ei;
+ chunk* c;
+ LIST_ENTRY* le;
+ space* s;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find last EXTENT_DATA item\n");
+ return FALSE;
+ }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
+ WARN("previous EXTENT_DATA not found\n");
+ goto end;
+ }
+
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (ed->type != EXTENT_TYPE_REGULAR) {
+ TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
+ goto end;
+ }
+
+ if (tp.item->key.offset + ed->decoded_size != start_data) {
+ TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp.item->key.offset, ed->decoded_size, start_data);
+ goto end;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME: compression not yet supported\n");
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("encryption not supported\n");
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ goto end;
+ }
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->size - ed2->offset != ed->decoded_size) {
+ TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
+ goto end;
+ }
+
+ searchkey.obj_id = ed2->address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = ed2->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find extent in tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ ERR("error - extent %llx,%llx not found in tree\n", ed2->address, ed2->size);
+ int3; // TESTING
+ goto end2;
+ }
+
+ if (tp2.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, ed2->address, ed2->size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ goto end2;
+ }
+
+ free_traverse_ptr(&tp2);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end2;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+
+ // FIXME - test this
+ if (extent_item_is_shared(ei, tp2.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ goto end2;
+ }
+
+ free_traverse_ptr(&tp2);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end2;
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+ }
+
+ if (ei->refcount != 1) {
+ TRACE("extent refcount was not 1\n");
+ goto end2;
+ }
+
+ if (ei->flags != EXTENT_ITEM_DATA) {
+ ERR("error - extent was not a data extent\n");
+ goto end2;
+ }
+
+ c = get_chunk_from_address(Vcb, ed2->address);
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset == ed2->address + ed2->size) {
+ if (s->type == SPACE_TYPE_FREE && s->size >= length) {
+ success = extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, &tp, &tp2);
+ }
+ break;
+ } else if (s->offset > ed2->address + ed2->size)
+ break;
+
+ le = le->Flink;
+ }
+
+end2:
+ free_traverse_ptr(&tp2);
+
+end:
+ free_traverse_ptr(&tp);
+
+ return success;
+}
+
+NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb, r->id, inode, start, length);
+
+ ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+
+ ed->generation = Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = 0;
+ ed2->size = 0;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(Vcb, r, inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ed);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+// static void print_tree(tree* t) {
+// LIST_ENTRY* le = t->itemlist.Flink;
+// while (le != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+// ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
+// le = le->Flink;
+// }
+// }
+
+static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ UINT64 flags;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
+
+ // FIXME - split data up if not enough space for just one extent
+
+ if (start_data > 0 && try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+
+ // if there is a gap before start_data, plug it with a sparse extent
+ if (start_data > 0) {
+ traverse_ptr tp;
+ NTSTATUS Status;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+// if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
+// traverse_ptr next_tp;
+//
+// ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
+// searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+// print_tree(tp.tree);
+//
+// if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+// ERR("---\n");
+// ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset);
+// print_tree(next_tp.tree);
+//
+// free_traverse_ptr(&next_tp);
+// } else
+// ERR("next item not found\n");
+//
+// int3;
+// free_traverse_ptr(&tp);
+// return STATUS_INTERNAL_ERROR;
+// }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size < start_data) {
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data);
+ else {
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size,
+ start_data - tp.item->key.offset - ((EXTENT_DATA*)tp.item->data)->decoded_size);
+ }
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ // FIXME - how do we know which RAID level to put this to?
+ flags = BLOCK_FLAG_DATA; // SINGLE
+
+// if (!chunk_test) { // TESTING
+// if ((c = alloc_chunk(Vcb, flags, NULL))) {
+// ERR("chunk_item->type = %llx\n", c->chunk_item->type);
+// ERR("size = %llx\n", c->chunk_item->size);
+// ERR("used = %llx\n", c->used);
+//
+// if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+// if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
+// // chunk_test = TRUE;
+// ERR("SUCCESS\n");
+// return STATUS_SUCCESS;
+// } else
+// ERR(":-(\n");
+// } else
+// ERR("???\n");
+// }
+// }
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ if ((c = alloc_chunk(Vcb, flags))) {
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ // FIXME - rebalance chunks if free space elsewhere?
+ WARN("couldn't find any data chunks with %llx bytes free\n", length);
+
+ return STATUS_DISK_FULL;
+}
+
+void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list) {
+ LIST_ENTRY* le = changed_sector_list->Flink;
+ changed_sector* cs;
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT32* data;
+
+ // FIXME - use local tree_cache
+
+ if (!Vcb->checksum_root) {
+ ERR("no checksum root\n");
+ goto exit;
+ }
+
+ while (le != changed_sector_list) {
+ UINT64 startaddr, endaddr;
+ ULONG len;
+ UINT32* checksums;
+ RTL_BITMAP bmp;
+ ULONG* bmparr;
+ ULONG runlength, index;
+
+ cs = (changed_sector*)le;
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ // FIXME - check entry is TYPE_EXTENT_CSUM?
+
+ if (tp.item->key.offset < cs->ol.key && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key)
+ startaddr = tp.item->key.offset;
+ else
+ startaddr = cs->ol.key;
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ if (tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key + (cs->length * Vcb->superblock.sector_size))
+ endaddr = tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32));
+ else
+ endaddr = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ free_traverse_ptr(&tp);
+
+ TRACE("cs starts at %llx (%x sectors)\n", cs->ol.key, cs->length);
+ TRACE("startaddr = %llx\n", startaddr);
+ TRACE("endaddr = %llx\n", endaddr);
+
+ len = (endaddr - startaddr) / Vcb->superblock.sector_size;
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * len, ALLOC_TAG);
+ bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG);
+
+ RtlInitializeBitMap(&bmp, bmparr, len);
+ RtlSetAllBits(&bmp);
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ // set bit = free space, cleared bit = allocated sector
+
+// ERR("start loop\n");
+ while (tp.item->key.offset < endaddr) {
+// ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ if (tp.item->key.offset >= startaddr) {
+ RtlCopyMemory(&checksums[(tp.item->key.offset - startaddr) / Vcb->superblock.sector_size], tp.item->data, tp.item->size);
+
+ RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, tp.item->size / sizeof(UINT32));
+
+ delete_tree_item(Vcb, &tp);
+ }
+
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ } else
+ break;
+ }
+// ERR("end loop\n");
+
+ free_traverse_ptr(&tp);
+
+ if (cs->deleted) {
+ RtlSetBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ } else {
+ RtlCopyMemory(&checksums[(cs->ol.key - startaddr) / Vcb->superblock.sector_size], cs->checksums, cs->length * sizeof(UINT32));
+ RtlClearBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ }
+
+ runlength = RtlFindFirstRunClear(&bmp, &index);
+
+ while (runlength != 0) {
+ do {
+ ULONG rl;
+
+ if (runlength * sizeof(UINT32) > MAX_CSUM_SIZE)
+ rl = MAX_CSUM_SIZE / sizeof(UINT32);
+ else
+ rl = runlength;
+
+ data = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * rl, ALLOC_TAG);
+ RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
+
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(data);
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+ goto exit;
+ }
+
+ runlength -= rl;
+ index += rl;
+ } while (runlength > 0);
+
+ runlength = RtlFindNextForwardRunClear(&bmp, index, &index);
+ }
+
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+
+ le = le->Flink;
+ }
+
+exit:
+ while ((le = RemoveHeadList(changed_sector_list)) != changed_sector_list) {
+ cs = (changed_sector*)le;
+
+ if (cs->checksums)
+ ExFreePool(cs->checksums);
+
+ ExFreePool(cs);
+ }
+}
+
+NTSTATUS truncate_file(fcb* fcb, UINT64 end) {
+ LIST_ENTRY changed_sector_list;
+ NTSTATUS Status;
+ BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
+
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
+
+ // FIXME - convert into inline extent if short enough
+
+ Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), nocsum ? NULL : &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents failed\n");
+ return Status;
+ }
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ // FIXME - inform cache manager of this
+
+ TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
+
+ if (!nocsum)
+ update_checksum_tree(fcb->Vcb, &changed_sector_list);
+
+ return STATUS_SUCCESS;
+}
+
+static UINT64 get_extent_item_refcount(device_extension* Vcb, UINT64 address) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT64 rc;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find EXTENT_ITEM for %llx\n", address);
+ return 0;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ rc = ei->refcount;
+
+ free_traverse_ptr(&tp);
+
+ return rc;
+}
+
+static NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ EXTENT_DATA* ed;
+ BOOL b, do_cow;
+ EXTENT_DATA2* eds;
+ UINT64 size, new_start, new_end, last_write = 0;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find last EXTENT_DATA item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset > start_data) {
+ ERR("previous EXTENT_DATA not found (found %llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ ed = (EXTENT_DATA*)tp.item->data;
+ eds = (EXTENT_DATA2*)&ed->data[0];
+
+ b = find_next_item(Vcb, &tp, &next_tp, TRUE);
+
+ switch (ed->type) {
+ case EXTENT_TYPE_REGULAR:
+ {
+ UINT64 rc = get_extent_item_refcount(Vcb, eds->address);
+
+ if (rc == 0) {
+ ERR("get_extent_item_refcount failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do_cow = rc > 1;
+ break;
+ }
+
+ case EXTENT_TYPE_INLINE:
+ do_cow = TRUE;
+ break;
+
+ case EXTENT_TYPE_PREALLOC:
+ FIXME("FIXME - handle prealloc extents\n"); // FIXME
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+
+ default:
+ ERR("error - unknown extent type %x\n", ed->type);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME: compression not yet supported\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("encryption not supported\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : eds->num_bytes;
+
+ TRACE("extent: start = %llx, length = %llx\n", tp.item->key.offset, size);
+
+ new_start = tp.item->key.offset < start_data ? start_data : tp.item->key.offset;
+ new_end = tp.item->key.offset + size > start_data + length ? (start_data + length) : (tp.item->key.offset + size);
+
+ TRACE("new_start = %llx\n", new_start);
+ TRACE("new_end = %llx\n", new_end);
+
+ if (do_cow) {
+ TRACE("doing COW write\n");
+
+ Status = excise_extents(Vcb, fcb, new_start, new_start + new_end, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ goto end;
+ }
+ } else {
+ UINT64 writeaddr;
+
+ writeaddr = eds->address + eds->offset + new_start - tp.item->key.offset;
+ TRACE("doing non-COW write to %llx\n", writeaddr);
+
+ Status = write_data(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - write_data returned %08x\n", Status);
+ goto end;
+ }
+
+ if (changed_sector_list) {
+ unsigned int i;
+ changed_sector* sc;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = writeaddr;
+ sc->length = (new_end - new_start) / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + new_start - start_data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+ }
+
+ last_write = new_end;
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data + length)
+ b = FALSE;
+ }
+ } while (b);
+
+ if (last_write < start_data + length) {
+ new_start = last_write;
+ new_end = start_data + length;
+
+ TRACE("new_start = %llx\n", new_start);
+ TRACE("new_end = %llx\n", new_end);
+
+ Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+#ifdef DEBUG_PARANOID
+static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ UINT64 length, oldlength, lastoff;
+
+ if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
+ return;
+
+ TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol\n");
+ goto failure2;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find EXTENT_DATA at offset 0\n");
+ goto failure;
+ }
+
+ length = oldlength = ((EXTENT_DATA*)tp.item->data)->decoded_size;
+ lastoff = tp.item->key.offset;
+
+ TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+
+ while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type) {
+ free_traverse_ptr(&next_tp);
+ break;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ length = ((EXTENT_DATA*)tp.item->data)->decoded_size;
+
+ TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+
+ if (tp.item->key.offset != lastoff + oldlength) {
+ ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
+ goto failure;
+ }
+
+ oldlength = length;
+ lastoff = tp.item->key.offset;
+ }
+
+// if (fcb->inode_item.st_blocks != lastoff + oldlength) {
+// ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
+// goto failure;
+// }
+
+ free_traverse_ptr(&tp);
+
+ return;
+
+failure:
+ free_traverse_ptr(&tp);
+
+failure2:
+ int3;
+}
+
+// static void check_extent_tree_consistent(device_extension* Vcb) {
+// KEY searchkey;
+// traverse_ptr tp, next_tp;
+// UINT64 lastaddr;
+// BOOL b, inconsistency;
+//
+// searchkey.obj_id = 0;
+// searchkey.obj_type = 0;
+// searchkey.offset = 0;
+//
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
+// }
+//
+// lastaddr = 0;
+// inconsistency = FALSE;
+//
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// // ERR("inconsistency!\n");
+// // int3;
+// inconsistency = TRUE;
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+// }
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+//
+// if (!inconsistency)
+// return;
+//
+// ERR("Inconsistency detected:\n");
+//
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
+// }
+//
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// ERR("inconsistency!\n");
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+// }
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+//
+// int3;
+// }
+#endif
+
+NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_DATA* ed2;
+ UINT64 newlength, start_data, end_data;
+ UINT32 bufhead;
+ BOOL make_inline;
+ UINT8* data;
+ LIST_ENTRY changed_sector_list;
+ INODE_ITEM *ii, *origii;
+ BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE*/, write_eof = FALSE;
+ NTSTATUS Status;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ fcb* fcb;
+
+ TRACE("(%p, %p, %llx, %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");
+ return STATUS_SUCCESS;
+ }
+
+ if (!FileObject) {
+ ERR("error - FileObject was NULL\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ fcb = FileObject->FsContext;
+
+ if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
+ WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1) {
+ offset = fcb->Header.FileSize;
+ write_eof = TRUE;
+ }
+
+ TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
+
+ nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
+ nocow = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATACOW;
+
+ newlength = fcb->ads ? fcb->adssize : fcb->inode_item.st_size;
+
+ if (fcb->deleted)
+ newlength = 0;
+
+ TRACE("newlength = %llx\n", newlength);
+
+// if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
+// ERR("lazy writer on the TV\n");
+// lazy_writer = TRUE;
+// }
+
+ if (offset.QuadPart + *length > newlength) {
+ if (paging_io && !write_eof) {
+ if (offset.QuadPart >= newlength) {
+ WARN("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, offset.QuadPart, *length);
+ ERR("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ ERR("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
+ fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+ return STATUS_SUCCESS;
+ }
+
+ *length = newlength - offset.QuadPart;
+ } else {
+ newlength = offset.QuadPart + *length;
+ changed_length = TRUE;
+
+ TRACE("extending length to %llx\n", newlength);
+ }
+ }
+
+ if (fcb->ads) {
+ UINT16 datalen;
+ UINT8* data2;
+ UINT32 maxlen;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (changed_length) {
+ // find maximum length of xattr
+ maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = fcb->adshash;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find key for xattr\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
+
+ free_traverse_ptr(&tp);
+
+ if (newlength > maxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen);
+ return STATUS_DISK_FULL;
+ }
+
+ fcb->adssize = newlength;
+
+ data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG);
+ RtlCopyMemory(data2, data, datalen);
+
+ if (offset.QuadPart > datalen)
+ RtlZeroMemory(&data2[datalen], offset.QuadPart - datalen);
+ } else
+ data2 = data;
+
+ if (*length > 0)
+ RtlCopyMemory(&data2[offset.QuadPart], buf, *length);
+
+ set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, newlength);
+
+ if (data) ExFreePool(data);
+ if (data2 != data) ExFreePool(data2);
+
+ if (newlength > fcb->Header.AllocationSize.QuadPart)
+ fcb->Header.AllocationSize.QuadPart = newlength;
+
+ fcb->Header.FileSize.QuadPart = newlength;
+ fcb->Header.ValidDataLength.QuadPart = newlength;
+ } else {
+ if (!no_cache && FileObject->PrivateCacheMap) {
+ BOOL wait;
+
+// ERR("FileObject->Flags: %x, changed_length = %u, write_eof = %u\n", FileObject->Flags, changed_length, write_eof);
+// if (changed_length) {
+// CC_FILE_SIZES ccfs;
+// UINT64 oldalloc;
+//
+// fcb->inode_item.st_size = newlength;
+//
+// ccfs.AllocationSize = fcb->Header.AllocationSize;
+// ccfs.FileSize = fcb->Header.FileSize;
+// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//
+// TRACE("new FileSize = %llx\n", fcb->Header.FileSize);
+//
+// CcSetFileSizes(FileObject, &ccfs);
+//
+// searchkey.obj_id = fcb->inode;
+// searchkey.obj_type = TYPE_EXTENT_DATA;
+// searchkey.offset = 0xffffffffffffffff;
+//
+// if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// oldalloc = 0;
+// if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+// oldalloc = tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size;
+// }
+// TRACE("oldalloc = %llx, newalloc = %llx\n", oldalloc, fcb->Header.AllocationSize.QuadPart);
+//
+// // FIXME - handle inline extents
+// if (fcb->Header.AllocationSize.QuadPart > oldalloc) {
+// Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, oldalloc, fcb->Header.AllocationSize.QuadPart - oldalloc);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("insert_sparse_extent returned %08x\n", Status);
+// free_traverse_ptr(&tp);
+// return Status;
+// }
+// }
+//
+// fcb->inode_item.st_size = newlength;
+//
+// free_traverse_ptr(&tp);
+// }
+//
+// if (write_eof || FileObject->Flags & FO_SYNCHRONOUS_IO) {
+// TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+// FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + *length;
+// TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+// }
+
+ // FIXME - uncomment this when async is working
+// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
+ wait = TRUE;
+
+ TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf);
+ if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
+ TRACE("CcCopyWrite failed.\n");
+
+ IoMarkIrpPending(Irp);
+ return STATUS_PENDING;
+ }
+ TRACE("CcCopyWrite finished\n");
+
+ return STATUS_SUCCESS;
+ } else {
+ make_inline = newlength <= fcb->Vcb->max_inline;
+
+ if (make_inline) {
+ start_data = 0;
+ end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
+ bufhead = sizeof(EXTENT_DATA) - 1;
+ } else {
+ start_data = offset.QuadPart & ~(fcb->Vcb->superblock.sector_size - 1);
+ end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size);
+ bufhead = 0;
+ }
+
+ if (end_data > fcb->Header.AllocationSize.QuadPart)
+ fcb->Header.AllocationSize.QuadPart = end_data;
+
+ fcb->Header.AllocationSize.QuadPart = sector_align(newlength, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = newlength;
+ fcb->Header.ValidDataLength.QuadPart = newlength;
+ TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
+
+ // FIXME - make sure freed if necessary
+ data = ExAllocatePoolWithTag(PagedPool, end_data - start_data + bufhead, ALLOC_TAG);
+ RtlZeroMemory(data + bufhead, end_data - start_data);
+
+ TRACE("start_data = %llx\n", start_data);
+ TRACE("end_data = %llx\n", end_data);
+
+ if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) {
+ if (changed_length) {
+ if (fcb->inode_item.st_size > start_data)
+ Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL);
+ else
+ Status = STATUS_SUCCESS;
+ } else
+ Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, end_data - start_data, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ }
+
+ RtlCopyMemory(data + bufhead + offset.QuadPart - start_data, buf, *length);
+
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
+
+ if (make_inline || !nocow) {
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, nocsum ? NULL : &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+
+ if (!make_inline) {
+ Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ } else {
+ ed2 = (EXTENT_DATA*)data;
+ ed2->generation = fcb->Vcb->superblock.generation;
+ ed2->decoded_size = newlength;
+ ed2->compression = BTRFS_COMPRESSION_NONE;
+ ed2->encryption = BTRFS_ENCRYPTION_NONE;
+ ed2->encoding = BTRFS_ENCODING_NONE;
+ ed2->type = EXTENT_TYPE_INLINE;
+
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, NULL);
+
+ fcb->inode_item.st_blocks += newlength;
+ }
+ } else {
+ Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - do_nocow_write returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ }
+
+ if (!no_cache && !FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ TRACE("calling CcInitializeCacheMap...\n");
+ CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+
+ CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+
+ changed_length = FALSE;
+ }
+ }
+ }
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+// ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+
+// if (!no_cache) {
+// if (!FileObject->PrivateCacheMap) {
+// CC_FILE_SIZES ccfs;
+//
+// ccfs.AllocationSize = fcb->Header.AllocationSize;
+// ccfs.FileSize = fcb->Header.FileSize;
+// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//
+// TRACE("calling CcInitializeCacheMap...\n");
+// CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+//
+// changed_length = FALSE;
+// }
+// }
+
+ if (fcb->ads)
+ origii = &fcb->par->inode_item;
+ else
+ origii = &fcb->inode_item;
+
+ origii->transid = Vcb->superblock.generation;
+ origii->sequence++;
+ origii->st_ctime = now;
+
+ if (!fcb->ads) {
+ TRACE("setting st_size to %llx\n", newlength);
+ origii->st_size = newlength;
+ origii->st_mtime = now;
+ }
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, origii, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
+
+ if (!nocsum)
+ update_checksum_tree(Vcb, &changed_sector_list);
+
+ if (changed_length) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ }
+
+ if (write_eof || FileObject->Flags & FO_SYNCHRONOUS_IO) {
+ TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+ FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + *length;
+ TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+ }
+
+ // FIXME - make sure this still called if STATUS_PENDING and async
+// if (!no_cache) {
+// if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
+// ERR("CcCopyWrite failed.\n");
+// }
+// }
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ void* buf;
+ NTSTATUS Status;
+ LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset;
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject ? FileObject->FsContext : NULL;
+ BOOL locked = FALSE;
+// LARGE_INTEGER freq, time1, time2;
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (fcb && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
+ return STATUS_ACCESS_DENIED;
+
+ if (!(Irp->Flags & IRP_NOCACHE) && !(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer->DataSectionObject) {
+ IO_STATUS_BLOCK iosb;
+
+ TRACE("CcFlushCache(%p, %llx, %x, %p)\n", FileObject->SectionObjectPointer, offset, IrpSp->Parameters.Write.Length, &iosb);
+ CcFlushCache(FileObject->SectionObjectPointer, &offset, IrpSp->Parameters.Write.Length, &iosb);
+ if (!NT_SUCCESS(iosb.Status)) {
+ ERR("CcFlushCache returned %08x\n", iosb.Status);
+ return iosb.Status;
+ }
+ TRACE("CcFlushCache finished\n");
+
+ TRACE("CcPurgeCacheSection(%p, %llx, %x, %u)\n", FileObject->SectionObjectPointer, offset, IrpSp->Parameters.Write.Length, FALSE);
+ CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, IrpSp->Parameters.Write.Length, FALSE);
+ TRACE("CcPurgeCacheSection finished\n");
+ }
+
+// time1 = KeQueryPerformanceCounter(&freq);
+
+ TRACE("write\n");
+
+ Irp->IoStatus.Information = 0;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_COMPLETE:
+ FIXME("unsupported - IRP_MN_COMPLETE\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL_DPC:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
+ break;
+
+ case IRP_MN_COMPRESSED:
+ FIXME("unsupported - IRP_MN_COMPRESSED\n");
+ break;
+
+ case IRP_MN_DPC:
+ FIXME("unsupported - IRP_MN_DPC\n");
+ break;
+
+ case IRP_MN_MDL:
+ FIXME("unsupported - IRP_MN_MDL\n");
+ break;
+
+ case IRP_MN_MDL_DPC:
+ FIXME("unsupported - IRP_MN_MDL_DPC\n");
+ break;
+
+ case IRP_MN_NORMAL:
+ TRACE("IRP_MN_NORMAL\n");
+ break;
+
+ default:
+ WARN("unknown minor function %x\n", IrpSp->MinorFunction);
+ break;
+ }
+
+ TRACE("offset = %llx\n", offset.QuadPart);
+ TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
+
+ if (!Irp->AssociatedIrp.SystemBuffer)
+ buf = map_user_buffer(Irp);
+ else
+ buf = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("buf = %p\n", buf);
+
+ acquire_tree_lock(Vcb, TRUE);
+ locked = TRUE;
+
+ if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
+ WARN("tried to write to locked region\n");
+ Status = STATUS_FILE_LOCK_CONFLICT;
+ goto exit;
+ }
+
+// ERR("Irp->Flags = %x\n", Irp->Flags);
+ Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE);
+ if (!NT_SUCCESS(Status)) {
+ if (Status != STATUS_PENDING)
+ ERR("write_file2 returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = consider_write(Vcb);
+
+ if (NT_SUCCESS(Status)) {
+ Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
+
+#ifdef DEBUG_PARANOID
+ check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
+
+// check_extent_tree_consistent(Vcb);
+#endif
+ }
+
+exit:
+ if (locked)
+ release_tree_lock(Vcb, TRUE);
+
+// time2 = KeQueryPerformanceCounter(NULL);
+
+// ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs
===================================================================
--- reactos/drivers/filesystems/btrfs (revision 0)
+++ reactos/drivers/filesystems/btrfs (working copy)
Property changes on: reactos/drivers/filesystems/btrfs
___________________________________________________________________
Added: bugtraq:logregex
## -0,0 +1 ##
+((CORE|ROSTESTS|ROSAPPS)-\d+)(,? ?((CORE|ROSTESTS|ROSAPPS)-\d+))*(,? ?(and |or )?((CORE|ROSTESTS|ROSAPPS)-\d+))?
Added: bugtraq:message
## -0,0 +1 ##
+See issue %BUGID% for more details.
\ No newline at end of property
Added: bugtraq:url
## -0,0 +1 ##
+https://jira.reactos.org/browse/%BUGID%
\ No newline at end of property
Added: tsvn:logminsize
## -0,0 +1 ##
+10
\ No newline at end of property
Index: reactos/drivers/filesystems/btrfs/btrfs.c
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.c (working copy)
@@ -0,0 +1,4485 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifdef _DEBUG
+#define DEBUG
+#endif
+
+#include "btrfs_drv.h"
+#ifndef __REACTOS__
+#ifndef _MSC_VER
+#include
+#else
+#include
+#endif
+#endif
+#include "btrfs.h"
+#ifndef __REACTOS__
+#include
+#else
+#include
+#endif
+
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
+ BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+#define COMPAT_RO_SUPPORTED 0
+
+enum read_data_status {
+ ReadDataStatus_Pending,
+ ReadDataStatus_Success,
+ ReadDataStatus_Cancelling,
+ ReadDataStatus_Cancelled,
+ ReadDataStatus_Error,
+ ReadDataStatus_CRCError,
+ ReadDataStatus_MissingDevice
+};
+
+struct read_data_context;
+
+typedef struct {
+ struct read_data_context* context;
+ UINT8* buf;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum read_data_status status;
+} read_data_stripe;
+
+typedef struct {
+ KEVENT Event;
+ NTSTATUS Status;
+ chunk* c;
+ UINT32 buflen;
+ UINT64 num_stripes;
+ UINT64 type;
+ read_data_stripe* stripes;
+} read_data_context;
+
+static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
+static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
+
+PDRIVER_OBJECT drvobj;
+PDEVICE_OBJECT devobj;
+#ifndef __REACTOS__
+BOOL have_sse42 = FALSE;
+#endif
+UINT64 num_reads = 0;
+LIST_ENTRY uid_map_list;
+LIST_ENTRY volumes;
+
+#ifdef DEBUG
+PFILE_OBJECT comfo = NULL;
+PDEVICE_OBJECT comdo = NULL;
+#endif
+
+static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
+
+typedef struct {
+ KEVENT Event;
+ IO_STATUS_BLOCK iosb;
+} read_context;
+
+#ifdef DEBUG
+static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_context* context = conptr;
+
+// DbgPrint("dbg_completion\n");
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+#endif
+
+#ifdef DEBUG_LONG_MESSAGES
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
+#else
+void STDCALL _debug_message(const char* func, char* s, ...) {
+#endif
+#ifdef DEBUG
+ LARGE_INTEGER offset;
+// IO_STATUS_BLOCK io;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ PIRP Irp;
+ va_list ap;
+ char *buf2 = NULL, *buf;
+ read_context* context = NULL;
+ UINT32 length;
+
+ if (!comdo) {
+ DbgPrint("comdo is NULL :-(\n");
+ return;
+ }
+
+ buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
+// DbgPrint("buf2 = %p\n", buf2);
+
+ if (!buf2) {
+ DbgPrint("Couldn't allocate buffer in debug_message\n");
+ return;
+ }
+
+// strcpy(buf2, func);
+// strcat(buf2, ": ");
+#ifdef DEBUG_LONG_MESSAGES
+ sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+#else
+ sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+#endif
+ buf = &buf2[strlen(buf2)];
+
+ va_start(ap, s);
+ vsprintf(buf, s, ap);
+
+ length = (UINT32)strlen(buf2);
+
+ offset.u.LowPart = 0;
+ offset.u.HighPart = 0;
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
+// DbgPrint("context = %p\n", context);
+ RtlZeroMemory(context, sizeof(read_context));
+
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+// status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, comdo, buf2, (ULONG)strlen(buf2), &offset, &context->Event, &context->iosb);
+ Irp = IoAllocateIrp(comdo->StackSize, FALSE);
+
+ if (!Irp) {
+ DbgPrint("IoAllocateIrp failed\n");
+ goto exit2;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (comdo->Flags & DO_BUFFERED_IO) {
+ Irp->AssociatedIrp.SystemBuffer = buf2;
+
+ Irp->Flags = IRP_BUFFERED_IO;
+ } else if (comdo->Flags & DO_DIRECT_IO) {
+ Irp->MdlAddress = IoAllocateMdl(buf2, length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ DbgPrint("IoAllocateMdl failed\n");
+ goto exit;
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ Irp->UserBuffer = buf2;
+ }
+
+ IrpSp->Parameters.Write.Length = length;
+ IrpSp->Parameters.Write.ByteOffset = offset;
+
+ Irp->UserIosb = &context->iosb;
+
+ Irp->UserEvent = &context->Event;
+
+ IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
+
+ Status = IoCallDriver(comdo, Irp);
+
+// LARGE_INTEGER timeout;
+// timeout.QuadPart = -1000000; // 100ms
+
+ if (Status == STATUS_PENDING) {
+// KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, /*&timeout*/NULL);
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+ Status = context->iosb.Status;
+ }
+
+ if (comdo->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("failed to write to COM1 - error %08x\n", Status);
+ goto exit;
+// return;
+// } else {
+// DbgPrint("wrote %u bytes\n", io.Information);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+exit2:
+ va_end(ap);
+
+ if (context)
+ ExFreePool(context);
+
+ if (buf2)
+ ExFreePool(buf2);
+#else
+ va_list ap;
+ char *buf2 = NULL, *buf;
+
+ buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
+
+ if (!buf2) {
+ DbgPrint("Couldn't allocate buffer in debug_message\n");
+ return;
+ }
+
+#ifdef DEBUG_LONG_MESSAGES
+ sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+#else
+ sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+#endif
+ buf = &buf2[strlen(buf2)];
+
+ va_start(ap, s);
+ vsprintf(buf, s, ap);
+
+ DbgPrint("%s", buf2);
+
+ va_end(ap);
+
+ if (buf2)
+ ExFreePool(buf2);
+#endif
+}
+
+ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment )
+{
+ if( Alignment & ( Alignment - 1 ) )
+ {
+ //
+ // Alignment not a power of 2
+ // Just returning
+ //
+ return NumberToBeAligned;
+ }
+ if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
+ {
+ NumberToBeAligned = NumberToBeAligned + Alignment;
+ NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
+ }
+ return NumberToBeAligned;
+}
+
+int keycmp(const KEY* key1, const KEY* key2) {
+ if (key1->obj_id < key2->obj_id) {
+ return -1;
+ } else if (key1->obj_id > key2->obj_id) {
+ return 1;
+ }
+
+ if (key1->obj_type < key2->obj_type) {
+ return -1;
+ } else if (key1->obj_type > key2->obj_type) {
+ return 1;
+ }
+
+ if (key1->offset < key2->offset) {
+ return -1;
+ } else if (key1->offset > key2->offset) {
+ return 1;
+ }
+
+ return 0;
+}
+
+BOOL is_top_level(PIRP Irp) {
+ if (!IoGetTopLevelIrp()) {
+ IoSetTopLevelIrp(Irp);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
+ UNICODE_STRING dosdevice_nameW;
+
+ TRACE("DriverUnload\n");
+
+ free_cache();
+
+ IoUnregisterFileSystem(DriverObject->DeviceObject);
+
+ dosdevice_nameW.Buffer = dosdevice_name;
+ dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+
+ IoDeleteSymbolicLink(&dosdevice_nameW);
+ IoDeleteDevice(DriverObject->DeviceObject);
+
+ // FIXME - free volumes
+
+#ifdef DEBUG
+ if (comfo)
+ ObDereferenceObject(comfo);
+#endif
+}
+
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+ KEY searchkey;
+ traverse_ptr tp, prev_tp;
+
+ // get last entry
+ searchkey.obj_id = 0xffffffffffffffff;
+ searchkey.obj_type = 0xff;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ return FALSE;
+ }
+
+ while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = prev_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
+ r->lastinode = tp.item->key.obj_id;
+ free_traverse_ptr(&tp);
+ TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
+ return TRUE;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ r->lastinode = SUBVOL_ROOT_INODE;
+
+ WARN("no INODE_ITEMs in tree %llx\n", r->id);
+
+ return TRUE;
+}
+
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DIR_ITEM* xa;
+ ULONG size, xasize;
+
+ TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ xa = (DIR_ITEM*)tp.item->data;
+ size = tp.item->size;
+
+ while (TRUE) {
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+
+ *datalen = xa->m;
+
+ if (xa->m > 0) {
+ *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
+ RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
+ } else
+ *data = NULL;
+
+ free_traverse_ptr(&tp);
+ return TRUE;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (size > xasize) {
+ size -= xasize;
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ } else
+ break;
+ }
+
+ TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+}
+
+void STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen) {
+ KEY searchkey;
+ traverse_ptr tp;
+ ULONG xasize;
+ DIR_ITEM* xa;
+
+ TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
+
+ if (!keycmp(&tp.item->key, &searchkey)) { // key exists
+ UINT8* newdata;
+ ULONG size = tp.item->size;
+
+ xa = (DIR_ITEM*)tp.item->data;
+
+ while (TRUE) {
+ ULONG oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ UINT64 pos;
+
+ // replace
+ newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
+
+ pos = (UINT8*)xa - tp.item->data;
+ if (pos + oldxasize < tp.item->size) { // copy after changed xattr
+ RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize);
+ }
+
+ if (pos > 0) { // copy before changed xattr
+ RtlCopyMemory(newdata, tp.item->data, pos);
+ xa = (DIR_ITEM*)(newdata + pos);
+ } else
+ xa = (DIR_ITEM*)newdata;
+
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL);
+
+ break;
+ }
+
+ if (xa->m + xa->n >= size) { // FIXME - test this works
+ // not found, add to end of data
+ newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
+
+ RtlCopyMemory(newdata, tp.item->data, tp.item->size);
+
+ xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size);
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL);
+
+ break;
+ } else {
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ size -= oldxasize;
+ }
+ }
+ } else {
+ // add new DIR_ITEM struct
+
+ xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
+
+ xa->key.obj_id = 0;
+ xa->key.obj_type = 0;
+ xa->key.offset = 0;
+ xa->transid = Vcb->superblock.generation;
+ xa->m = datalen;
+ xa->n = (UINT16)strlen(name);
+ xa->type = BTRFS_TYPE_EA;
+ RtlCopyMemory(xa->name, name, strlen(name));
+ RtlCopyMemory(xa->name + strlen(name), data, datalen);
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+}
+
+BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DIR_ITEM* xa;
+
+ TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return FALSE;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) { // key exists
+ ULONG size = tp.item->size;
+
+ xa = (DIR_ITEM*)tp.item->data;
+
+ while (TRUE) {
+ ULONG oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
+
+ if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
+ ULONG newsize;
+ UINT8 *newdata, *dioff;
+
+ newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newsize == 0) {
+ TRACE("xattr %s deleted\n", name);
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+ }
+
+ // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
+ newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
+
+ if ((UINT8*)xa > tp.item->data) {
+ RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data);
+ dioff = newdata + ((UINT8*)xa - tp.item->data);
+ } else {
+ dioff = newdata;
+ }
+
+ if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
+ RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL);
+
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+ }
+
+ if (xa->m + xa->n >= size) { // FIXME - test this works
+ WARN("xattr %s not found\n", name);
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+ } else {
+ xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
+ size -= oldxasize;
+ }
+ }
+ } else {
+ WARN("xattr %s not found\n", name);
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+ }
+}
+
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize) {
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT8* di2;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (tp.item->size + disize > maxlen) {
+ WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
+ RtlCopyMemory(di2, tp.item->data, tp.item->size);
+ RtlCopyMemory(di2 + tp.item->size, di, disize);
+
+ delete_tree_item(Vcb, &tp);
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL);
+
+ ExFreePool(di);
+ } else {
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode) {
+ KEY searchkey;
+ traverse_ptr tp, prev_tp;
+ UINT64 dirpos;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return 0;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = prev_tp;
+
+ TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ dirpos = tp.item->key.offset + 1;
+ } else
+ dirpos = 2;
+
+ free_traverse_ptr(&tp);
+
+ return dirpos;
+}
+
+static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ BOOL top_level;
+
+ TRACE("close\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (DeviceObject == devobj) {
+ TRACE("Closing file system\n");
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ // FIXME - unmount if called for volume
+ // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
+
+ Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
+
+exit:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG length, ULONG* pbr) {
+ UINT8* xattrdata;
+ UINT16 xattrlen;
+ ULONG readlen;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
+
+ if (pbr) *pbr = 0;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &xattrdata, &xattrlen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (start >= xattrlen) {
+ TRACE("tried to read beyond end of stream\n");
+ Status = STATUS_END_OF_FILE;
+ goto end;
+ }
+
+ if (length == 0) {
+ WARN("tried to read zero bytes\n");
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+
+ if (start + length < xattrlen)
+ readlen = length;
+ else
+ readlen = (ULONG)xattrlen - (ULONG)start;
+
+ RtlCopyMemory(data + start, xattrdata, readlen);
+
+ if (pbr) *pbr = readlen;
+
+ Status = STATUS_SUCCESS;
+
+end:
+ ExFreePool(xattrdata);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_data_stripe* stripe = conptr;
+ read_data_context* context = (read_data_context*)stripe->context;
+ UINT64 i;
+ BOOL complete;
+
+ if (stripe->status == ReadDataStatus_Cancelling) {
+ stripe->status = ReadDataStatus_Cancelled;
+ goto end;
+ }
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ // FIXME - calculate and compare checksum
+
+ stripe->status = ReadDataStatus_Success;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Pending) {
+ context->stripes[i].status = ReadDataStatus_Cancelling;
+ IoCancelIrp(context->stripes[i].Irp);
+ }
+ }
+
+ goto end;
+ } else {
+ stripe->status = ReadDataStatus_Error;
+ }
+
+end:
+ complete = TRUE;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Pending || context->stripes[i].status == ReadDataStatus_Cancelling) {
+ complete = FALSE;
+ break;
+ }
+ }
+
+ if (complete)
+ KeSetEvent(&context->Event, 0, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT8* buf) {
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ read_data_context* context;
+ UINT64 i/*, type*/, offset;
+ NTSTATUS Status;
+ device** devices;
+
+ // FIXME - make this work with RAID
+
+ if (Vcb->log_to_phys_loaded) {
+ chunk* c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ci = c->chunk_item;
+ offset = c->offset;
+ devices = c->devices;
+ }
+
+// if (ci->type & BLOCK_FLAG_DUPLICATE) {
+// type = BLOCK_FLAG_DUPLICATE;
+// } else if (ci->type & BLOCK_FLAG_RAID0) {
+// FIXME("RAID0 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID1) {
+// FIXME("RAID1 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID10) {
+// FIXME("RAID10 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID5) {
+// FIXME("RAID5 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID6) {
+// FIXME("RAID6 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else { // SINGLE
+// type = 0;
+// }
+
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_data_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG);
+ RtlZeroMemory(context->stripes, sizeof(read_data_stripe) * ci->num_stripes);
+
+ context->buflen = length;
+ context->num_stripes = ci->num_stripes;
+// context->type = type;
+
+ // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ if (!devices[i]) {
+ context->stripes[i].status = ReadDataStatus_MissingDevice;
+ context->stripes[i].buf = NULL;
+ } else {
+ context->stripes[i].context = (struct read_data_context*)context;
+ context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+
+ context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+
+ if (!context->stripes[i].Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, length, FALSE, FALSE, NULL);
+ if (!context->stripes[i].Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
+ }
+
+ IrpSp->Parameters.Read.Length = length;
+ IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
+
+ context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+
+ IoSetCompletionRoutine(context->stripes[i].Irp, read_data_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+
+ context->stripes[i].status = ReadDataStatus_Pending;
+ }
+ }
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status != ReadDataStatus_MissingDevice) {
+ IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
+ }
+ }
+
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+
+ // FIXME - if checksum error, write good data over bad
+
+ // check if any of the stripes succeeded
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Success) {
+ RtlCopyMemory(buf, context->stripes[i].buf, length);
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+ }
+
+ // if not, see if we got a checksum error
+
+// for (i = 0; i < ci->num_stripes; i++) {
+// if (context->stripes[i].status == ReadDataStatus_CRCError) {
+// WARN("stripe %llu had a checksum error\n", i);
+//
+// Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+// goto exit;
+// }
+// }
+
+ // failing that, return the first error we encountered
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadDataStatus_Error) {
+ Status = context->stripes[i].iosb.Status;
+ goto exit;
+ }
+ }
+
+ // if we somehow get here, return STATUS_INTERNAL_ERROR
+
+ Status = STATUS_INTERNAL_ERROR;
+
+exit:
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].Irp) {
+ if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(context->stripes[i].Irp->MdlAddress);
+ IoFreeMdl(context->stripes[i].Irp->MdlAddress);
+ }
+ IoFreeIrp(context->stripes[i].Irp);
+ }
+
+ if (context->stripes[i].buf)
+ ExFreePool(context->stripes[i].buf);
+ }
+
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ return Status;
+}
+
+NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr) {
+ KEY searchkey;
+ NTSTATUS Status;
+ traverse_ptr tp, next_tp;
+ EXTENT_DATA* ed;
+ UINT64 bytes_read = 0;
+
+ TRACE("(%p, %llx, %llx, %p, %llx, %llx, %p)\n", Vcb, subvol->id, inode, data, start, length, pbr);
+
+ if (pbr)
+ *pbr = 0;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("couldn't find any entries in subvol %llx\n", subvol->id);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+ if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ free_traverse_ptr(&tp);
+ ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+ if (tp.item->key.offset > start) {
+ ERR("first EXTENT_DATA was after offset\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_INTERNAL_ERROR; // FIXME - correct error?
+ goto exit;
+ }
+
+// while (TRUE) {
+// BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//
+// if (!foundnext || next_tp.item->key.obj_id != inode ||
+// next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset > start) {
+// if (foundnext)
+// free_traverse_ptr(&next_tp);
+//
+// break;
+// }
+//
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+
+ do {
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (tp.item->key.offset + ed->decoded_size < start) {
+ ERR("Tried to read beyond end of file\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_END_OF_FILE;
+ goto exit;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME - compression not yet supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("Encryption not supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("Other encodings not supported\n");
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ switch (ed->type) {
+ case EXTENT_TYPE_INLINE:
+ {
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT64 read;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ RtlCopyMemory(data + bytes_read, &ed->data[off], read);
+
+ bytes_read += read;
+ length -= read;
+ break;
+ }
+
+ case EXTENT_TYPE_REGULAR:
+ {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT32 to_read, read;
+ UINT8* buf;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ if (ed2->address == 0) {
+ RtlZeroMemory(data + bytes_read, read);
+ } else {
+ to_read = sector_align(read, Vcb->superblock.sector_size);
+
+ buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+
+ // FIXME - load checksums
+
+ Status = read_data(Vcb, ed2->address + ed2->offset + off, to_read, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_data returned %08x\n", Status);
+ ExFreePool(buf);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ RtlCopyMemory(data + bytes_read, buf, read);
+
+ ExFreePool(buf);
+ }
+
+ bytes_read += read;
+ length -= read;
+
+ break;
+ }
+
+ case EXTENT_TYPE_PREALLOC:
+ {
+ UINT64 off = start + bytes_read - tp.item->key.offset;
+ UINT32 read;
+
+ read = ed->decoded_size - off;
+ if (read > length) read = length;
+
+ RtlZeroMemory(data + bytes_read, read);
+
+ bytes_read += read;
+ length -= read;
+
+ break;
+ }
+
+ default:
+ WARN("Unsupported extent data type %u\n", ed->type);
+ free_traverse_ptr(&tp);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ if (length > 0) {
+ BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (!foundnext)
+ break;
+ else if (next_tp.item->key.obj_id != inode ||
+ next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
+ next_tp.item->key.offset != tp.item->key.offset + ed->decoded_size
+ ) {
+ free_traverse_ptr(&next_tp);
+ break;
+ } else {
+ TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset);
+
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } else
+ break;
+ } while (TRUE);
+
+ free_traverse_ptr(&tp);
+
+ Status = STATUS_SUCCESS;
+ if (pbr)
+ *pbr = bytes_read;
+
+exit:
+ return Status;
+}
+
+// static NTSTATUS STDCALL read_file(PFILE_OBJECT FileObject, PIRP Irp) {
+static NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ UINT8* data;
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ UINT64 start;
+ ULONG length, bytes_read;
+ NTSTATUS Status;
+ BOOL top_level;
+
+// __asm__("int3");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ TRACE("read\n");
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_COMPLETE:
+ FIXME("unsupported - IRP_MN_COMPLETE\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL_DPC:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
+ break;
+
+ case IRP_MN_COMPRESSED:
+ FIXME("unsupported - IRP_MN_COMPRESSED\n");
+ break;
+
+ case IRP_MN_DPC:
+ FIXME("unsupported - IRP_MN_DPC\n");
+ break;
+
+ case IRP_MN_MDL:
+ FIXME("unsupported - IRP_MN_MDL\n");
+ break;
+
+ case IRP_MN_MDL_DPC:
+ FIXME("unsupported - IRP_MN_MDL_DPC\n");
+ break;
+
+ case IRP_MN_NORMAL:
+ TRACE("IRP_MN_NORMAL\n");
+ break;
+
+ default:
+ WARN("unknown minor %u\n", IrpSp->MinorFunction);
+ }
+
+ data = map_user_buffer(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ start = IrpSp->Parameters.Read.ByteOffset.QuadPart;
+ length = IrpSp->Parameters.Read.Length;
+ bytes_read = 0;
+
+ if (!fcb || !fcb->Vcb || !fcb->subvol) {
+ Status = STATUS_INTERNAL_ERROR; // FIXME - invalid param error?
+ goto exit;
+ }
+
+ TRACE("file = %.*S (fcb = %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+ TRACE("offset = %llx, 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");
+
+ // FIXME - shouldn't be able to read from a directory
+
+ if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) {
+ WARN("tried to read locked region\n");
+ Status = STATUS_FILE_LOCK_CONFLICT;
+ goto exit;
+ }
+
+ if (length == 0) {
+ WARN("tried to read zero bytes\n");
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (start >= fcb->Header.ValidDataLength.QuadPart) {
+ WARN("tried to read with offset after file end (%llx >= %llx)\n", start, fcb->Header.ValidDataLength.QuadPart);
+ Status = STATUS_END_OF_FILE;
+ goto exit;
+ }
+
+ fcb->Header.FileSize.QuadPart = fcb->ads ? fcb->adssize : fcb->inode_item.st_size;
+
+ TRACE("FileObject %p fcb %p FileSize = %llx st_size = %llx (%p)\n", FileObject, fcb, fcb->Header.FileSize.QuadPart, fcb->inode_item.st_size, &fcb->inode_item.st_size);
+// int3;
+
+ if (length + start > fcb->Header.ValidDataLength.QuadPart)
+ length = fcb->Header.ValidDataLength.QuadPart - start;
+
+ if (!(Irp->Flags & IRP_NOCACHE)) {
+ BOOL wait;
+
+ Status = STATUS_SUCCESS;
+
+// try {
+ if (!FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ ERR("calling CcInitializeCacheMap (%llx, %llx, %llx)\n",
+ ccfs.AllocationSize.QuadPart, ccfs.FileSize.QuadPart, ccfs.ValidDataLength.QuadPart);
+ CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+
+ CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+ }
+
+ // FIXME - uncomment this when async is working
+ // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
+ wait = TRUE;
+
+ ERR("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus);
+ if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) {
+ TRACE("CcCopyRead failed\n");
+
+ IoMarkIrpPending(Irp);
+ Status = STATUS_PENDING;
+ goto exit;
+ }
+ ERR("CcCopyRead finished\n");
+// } except (EXCEPTION_EXECUTE_HANDLER) {
+// Status = GetExceptionCode();
+// }
+
+ if (NT_SUCCESS(Status)) {
+ Status = Irp->IoStatus.Status;
+ bytes_read = Irp->IoStatus.Information;
+ } else
+ ERR("EXCEPTION - %08x\n", Status);
+ } else {
+ if (fcb->ads)
+ Status = read_stream(fcb, data, start, length, &bytes_read);
+ else
+ Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, start, length, &bytes_read);
+
+ TRACE("read %u bytes\n", bytes_read);
+
+ Irp->IoStatus.Information = bytes_read;
+ }
+
+ if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
+ FileObject->CurrentByteOffset.QuadPart = start + bytes_read;
+
+exit:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ Irp->IoStatus.Status = Status;
+
+ TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
+ TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
+ TRACE("returning %08x\n", Status);
+
+ if (Status != STATUS_PENDING)
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
+
+ Status = write_file(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = Status;
+
+ TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
+
+ if (Status != STATUS_PENDING)
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ FIXME("STUB: query ea\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ FIXME("STUB: set ea\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb->readonly)
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+
+ // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ BOOL top_level;
+
+ TRACE("flush buffers\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
+
+ if (fcb->Header.PagingIoResource) {
+ ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
+ ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ }
+
+ Status = Irp->IoStatus.Status;
+ }
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ ULONG BytesCopied = 0;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ WCHAR* fs_name = L"Btrfs";
+ ULONG fs_name_len = 5 * sizeof(WCHAR);
+
+ TRACE("query volume information\n");
+
+ FsRtlEnterFileSystem();
+ top_level = is_top_level(Irp);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
+ case FileFsAttributeInformation:
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+ BOOL overflow = FALSE;
+ ULONG orig_fs_name_len = fs_name_len;
+
+ TRACE("FileFsAttributeInformation\n");
+
+ if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
+ if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
+ fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
+ else
+ fs_name_len = 0;
+
+ overflow = TRUE;
+ }
+
+ data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
+ FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
+ FILE_SUPPORTS_REPARSE_POINTS;
+ if (Vcb->readonly)
+ data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
+
+ // should also be FILE_FILE_COMPRESSION when supported
+ data->MaximumComponentNameLength = 255; // FIXME - check
+ data->FileSystemNameLength = orig_fs_name_len * sizeof(WCHAR);
+ RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
+
+ BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
+ Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+ break;
+ }
+
+ case FileFsControlInformation:
+ FIXME("STUB: FileFsControlInformation\n");
+ break;
+
+ case FileFsDeviceInformation:
+ FIXME("STUB: FileFsDeviceInformation\n");
+ break;
+
+ case FileFsDriverPathInformation:
+ FIXME("STUB: FileFsDriverPathInformation\n");
+ break;
+
+ case FileFsFullSizeInformation:
+ {
+ FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
+ UINT64 totalsize, freespace;
+
+ TRACE("FileFsFullSizeInformation\n");
+
+ // FIXME - calculate correctly for RAID
+ totalsize = Vcb->superblock.total_bytes / Vcb->superblock.sector_size;
+ freespace = (Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size;
+
+ ffsi->TotalAllocationUnits.QuadPart = totalsize;
+ ffsi->ActualAvailableAllocationUnits.QuadPart = freespace;
+ ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
+ ffsi->SectorsPerAllocationUnit = 1;
+ ffsi->BytesPerSector = Vcb->superblock.sector_size;
+
+ BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileFsObjectIdInformation:
+ FIXME("STUB: FileFsObjectIdInformation\n");
+ break;
+
+ case FileFsSizeInformation:
+ {
+ FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
+ UINT64 totalsize, freespace;
+
+ TRACE("FileFsSizeInformation\n");
+
+ // FIXME - calculate correctly for RAID
+ // FIXME - is this returning the right free space?
+ totalsize = Vcb->superblock.dev_item.num_bytes / Vcb->superblock.sector_size;
+ freespace = (Vcb->superblock.dev_item.num_bytes - Vcb->superblock.dev_item.bytes_used) / Vcb->superblock.sector_size;
+
+ ffsi->TotalAllocationUnits.QuadPart = totalsize;
+ ffsi->AvailableAllocationUnits.QuadPart = freespace;
+ ffsi->SectorsPerAllocationUnit = 1;
+ ffsi->BytesPerSector = Vcb->superblock.sector_size;
+
+ BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileFsVolumeInformation:
+ {
+ FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+ FILE_FS_VOLUME_INFORMATION ffvi;
+ BOOL overflow = FALSE;
+ ULONG label_len, orig_label_len;
+
+ TRACE("FileFsVolumeInformation\n");
+ TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
+
+ acquire_tree_lock(Vcb, FALSE);
+
+// orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
+ RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ orig_label_len = label_len;
+
+ if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
+ if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
+ label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
+ else
+ label_len = 0;
+
+ overflow = TRUE;
+ }
+
+ TRACE("label_len = %u\n", label_len);
+
+ ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
+ ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
+ ffvi.VolumeLabelLength = orig_label_len;
+ ffvi.SupportsObjects = FALSE;
+
+ RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
+
+ if (label_len > 0) {
+ ULONG bytecount;
+
+// RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
+ RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
+ }
+
+ release_tree_lock(Vcb, FALSE);
+
+ BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
+ Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+ break;
+ }
+
+ default:
+ Status = STATUS_INVALID_PARAMETER;
+ WARN("unknown FsInformatClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
+ break;
+ }
+
+// if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
+// WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
+// BytesCopied = IrpSp->Parameters.QueryVolume.Length;
+// Status = STATUS_BUFFER_OVERFLOW;
+// }
+
+ Irp->IoStatus.Status = Status;
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+ Irp->IoStatus.Information = 0;
+ else
+ Irp->IoStatus.Information = BytesCopied;
+
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("query volume information returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_context* context = conptr;
+
+// DbgPrint("read_completion\n");
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+// static void test_tree_deletion(device_extension* Vcb) {
+// KEY searchkey/*, endkey*/;
+// traverse_ptr tp, next_tp;
+// root* r;
+//
+// searchkey.obj_id = 0x100;
+// searchkey.obj_type = 0x54;
+// searchkey.offset = 0xca4ab2f5;
+//
+// // endkey.obj_id = 0x100;
+// // endkey.obj_type = 0x60;
+// // endkey.offset = 0x15a;
+//
+// r = Vcb->roots;
+// while (r && r->id != 0x102)
+// r = r->next;
+//
+// if (!r) {
+// ERR("error - could not find root\n");
+// return;
+// }
+//
+// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
+// ERR("error - could not find key\n");
+// return;
+// }
+//
+// while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
+// tp.item->ignore = TRUE;
+// add_to_tree_cache(tc, tp.tree);
+//
+// if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// } else
+// break;
+// }
+//
+// free_traverse_ptr(&tp);
+// }
+
+// static void test_tree_splitting(device_extension* Vcb) {
+// int i;
+//
+// for (i = 0; i < 1000; i++) {
+// char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
+//
+// insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
+// }
+// }
+
+static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
+ ULONG utf8len;
+ NTSTATUS Status;
+
+ TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (utf8len > MAX_LABEL_SIZE) {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
+ }
+
+ // FIXME - check for '/' and '\\' and reject
+
+ acquire_tree_lock(Vcb, TRUE);
+
+// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
+ if (!NT_SUCCESS(Status))
+ goto release;
+
+ if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
+ RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
+
+// test_tree_deletion(Vcb); // TESTING
+// test_tree_splitting(Vcb);
+
+ Status = consider_write(Vcb);
+
+release:
+ release_tree_lock(Vcb, TRUE);
+
+end:
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ BOOL top_level;
+
+ TRACE("set volume information\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
+ }
+
+ switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
+ case FileFsControlInformation:
+ FIXME("STUB: FileFsControlInformation\n");
+ break;
+
+ case FileFsLabelInformation:
+ TRACE("FileFsLabelInformation\n");
+
+ Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
+ break;
+
+ case FileFsObjectIdInformation:
+ FIXME("STUB: FileFsObjectIdInformation\n");
+ break;
+
+ default:
+ WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
+ break;
+ }
+
+end:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ DIR_ITEM* di;
+ LONG len;
+
+ di = (DIR_ITEM*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
+ ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)di > tp.item->data) {
+ RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
+ dioff = newdi + ((UINT8*)di - tp.item->data);
+ } else {
+ dioff = newdi;
+ }
+
+ if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
+ RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL);
+ }
+
+ break;
+ }
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
+ di = (DIR_ITEM*)&di->name[di->n + di->m];
+ } while (len > 0);
+ } else {
+ WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index) {
+ KEY searchkey;
+ traverse_ptr tp;
+ BOOL changed = FALSE;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = parinode;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ INODE_REF* ir;
+ ULONG len;
+
+ ir = (INODE_REF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
+
+ if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+ changed = TRUE;
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)ir > tp.item->data) {
+ RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
+ iroff = newir + ((UINT8*)ir - tp.item->data);
+ } else {
+ iroff = newir;
+ }
+
+ if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL);
+ }
+
+ if (index)
+ *index = ir->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ ir = (INODE_REF*)&ir->name[ir->n];
+ } else
+ break;
+ } while (len > 0);
+
+ if (!changed) {
+ WARN("found INODE_REF entry, but couldn't find filename\n");
+ }
+ } else {
+ WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (changed)
+ return STATUS_SUCCESS;
+
+ if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
+ return STATUS_INTERNAL_ERROR;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ INODE_EXTREF* ier;
+ ULONG len;
+
+ ier = (INODE_EXTREF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
+
+ if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+ changed = TRUE;
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)ier > tp.item->data) {
+ RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
+ ieroff = newier + ((UINT8*)ier - tp.item->data);
+ } else {
+ ieroff = newier;
+ }
+
+ if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
+
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL);
+ }
+
+ if (index)
+ *index = ier->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ } else
+ break;
+ } while (len > 0);
+ } else {
+ WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
+}
+
+void delete_fcb(fcb* fcb, PFILE_OBJECT FileObject) {
+ ULONG bytecount;
+ NTSTATUS Status;
+ char* utf8 = NULL;
+ UINT32 crc32;
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ UINT64 parinode, index;
+ INODE_ITEM *ii, *dirii;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ LIST_ENTRY changed_sector_list;
+ LARGE_INTEGER freq, time1, time2;
+
+ // FIXME - throw error if try to delete subvol root(?)
+
+ // FIXME - delete all children if deleting directory
+
+ if (fcb->deleted) {
+ ERR("trying to delete already-deleted file\n");
+ return;
+ }
+
+ if (!fcb->par) {
+ ERR("error - trying to delete root FCB\n");
+ return;
+ }
+
+ time1 = KeQueryPerformanceCounter(&freq);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ if (fcb->ads) {
+ char* s;
+ TRACE("deleting ADS\n");
+
+ s = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.Length + 1, ALLOC_TAG);
+ RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+ s[fcb->adsxattr.Length] = 0;
+
+ if (!delete_xattr(fcb->Vcb, fcb->par->subvol, fcb->par->inode, s, fcb->adshash)) {
+ ERR("failed to delete xattr %s\n", s);
+ }
+
+ ExFreePool(s);
+
+ fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(fcb->Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ goto exit;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->par->inode, fcb->par->subvol->id);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(fcb->Vcb, &tp);
+
+ insert_tree_item(fcb->Vcb, fcb->par->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->par->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = now;
+
+ goto success;
+ }
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ return;
+ }
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, bytecount + 1, ALLOC_TAG);
+
+ RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ utf8[bytecount] = 0;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount);
+
+ ERR("deleting %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ if (fcb->par->subvol == fcb->subvol)
+ parinode = fcb->par->inode;
+ else
+ parinode = SUBVOL_ROOT_INODE;
+
+ // delete DIR_ITEM (0x54)
+
+ Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return;
+ }
+
+ // delete INODE_REF (0xc)
+
+ index = 0;
+
+ Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fcb->utf8, &index);
+
+ // delete DIR_INDEX (0x60)
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = index;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+
+ // delete INODE_ITEM (0x1)
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - INODE_ITEM not found\n");
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ ii = (INODE_ITEM*)tp.item->data;
+ TRACE("nlink = %u\n", ii->st_nlink);
+
+ if (ii->st_nlink > 1) {
+ INODE_ITEM* newii;
+
+ newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
+ newii->st_nlink--;
+ newii->transid = fcb->Vcb->superblock.generation;
+ newii->sequence++;
+ newii->st_ctime = now;
+
+ TRACE("replacing (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ delete_tree_item(fcb->Vcb, &tp);
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newii, sizeof(INODE_ITEM), NULL))
+ ERR("error - failed to insert item\n");
+
+ free_traverse_ptr(&tp);
+
+ goto success2;
+ }
+
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ // delete XATTR_ITEM (0x18)
+
+ while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ if (tp.item->key.obj_id == fcb->inode) {
+ // FIXME - do metadata thing here too?
+ if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ delete_tree_item(fcb->Vcb, &tp);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ } else
+ break;
+ }
+
+ free_traverse_ptr(&tp);
+
+ // excise extents
+
+ InitializeListHead(&changed_sector_list);
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ goto exit;
+ }
+
+ if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
+ update_checksum_tree(fcb->Vcb, &changed_sector_list);
+ }
+
+success2:
+ // update INODE_ITEM of parent
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto exit;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, fcb->subvol->id);
+ free_traverse_ptr(&tp);
+ goto exit;
+ }
+
+ TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
+ fcb->par->inode_item.st_size -= bytecount * 2;
+ TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
+ fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+ fcb->par->inode_item.st_mtime = now;
+
+ dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(dirii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(fcb->Vcb, &tp);
+
+ insert_tree_item(fcb->Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+success:
+ consider_write(fcb->Vcb);
+
+ fcb->deleted = TRUE;
+
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+
+ if (FileObject && FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ }
+
+ // FIXME - set deleted flag of any open FCBs for ADS
+
+ TRACE("sending notification for deletion of %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ FILE_ACTION_REMOVED, NULL);
+
+ time2 = KeQueryPerformanceCounter(NULL);
+
+ ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
+
+exit:
+ if (utf8)
+ ExFreePool(utf8);
+}
+
+void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
+ ULONG rc;
+
+ rc = InterlockedDecrement(&fcb->refcount);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+// WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#else
+ _debug_message(func, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+#endif
+#endif
+
+ if (rc > 0)
+ return;
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+
+ if (fcb->filepart.Buffer)
+ RtlFreeUnicodeString(&fcb->filepart);
+
+ ExDeleteResourceLite(&fcb->nonpaged->resource);
+ ExFreePool(fcb->nonpaged);
+
+ if (fcb->par/* && fcb->par != fcb->par->Vcb->root_fcb*/) {
+ RemoveEntryList(&fcb->list_entry);
+ _free_fcb(fcb->par, func, file, line);
+ }
+
+ if (fcb->prev)
+ fcb->prev->next = fcb->next;
+
+ if (fcb->next)
+ fcb->next->prev = fcb->prev;
+
+ if (fcb->Vcb->fcbs == fcb)
+ fcb->Vcb->fcbs = fcb->next;
+
+ if (fcb->full_filename.Buffer)
+ ExFreePool(fcb->full_filename.Buffer);
+
+ if (fcb->sd)
+ ExFreePool(fcb->sd);
+
+ if (fcb->adsxattr.Buffer)
+ ExFreePool(fcb->adsxattr.Buffer);
+
+ if (fcb->utf8.Buffer)
+ ExFreePool(fcb->utf8.Buffer);
+
+ FsRtlUninitializeFileLock(&fcb->lock);
+
+ ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+
+ ExFreePool(fcb);
+#ifdef DEBUG_FCB_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "freeing fcb %p\n", fcb);
+#else
+ _debug_message(func, "freeing fcb %p\n", fcb);
+#endif
+#endif
+}
+
+static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
+ fcb* fcb;
+ ccb* ccb;
+
+ TRACE("FileObject = %p\n", FileObject);
+
+ fcb = FileObject->FsContext;
+ if (!fcb) {
+ TRACE("FCB was NULL, returning success\n");
+ return STATUS_SUCCESS;
+ }
+
+ ccb = FileObject->FsContext2;
+
+ TRACE("close called for %.*S (fcb == %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+
+ FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
+
+ // FIXME - make sure notification gets sent if file is being deleted
+
+ if (ccb) {
+ if (ccb->query_string.Buffer)
+ RtlFreeUnicodeString(&ccb->query_string);
+
+ ExFreePool(ccb);
+ }
+
+ if (fcb->refcount == 1)
+ CcUninitializeCacheMap(FileObject, NULL, NULL);
+
+ free_fcb(fcb);
+
+ return STATUS_SUCCESS;
+}
+
+static void STDCALL uninit(device_extension* Vcb) {
+ LIST_ENTRY *le, *le2;
+ chunk* c;
+ space* s;
+
+ while (Vcb->roots) {
+ root* r = Vcb->roots->next;
+
+ // FIXME - also free root trees
+ ExDeleteResourceLite(&Vcb->roots->nonpaged->load_tree_lock);
+ ExFreePool(Vcb->roots->nonpaged);
+ ExFreePool(Vcb->roots);
+
+ Vcb->roots = r;
+ }
+
+ while ((le = RemoveHeadList(&Vcb->chunks)) != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ while ((le2 = RemoveHeadList(&c->space)) != &c->space) {
+ s = CONTAINING_RECORD(le2, space, list_entry);
+
+ ExFreePool(s);
+ }
+
+ if (c->devices)
+ ExFreePool(c->devices);
+
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
+ }
+
+ free_fcb(Vcb->volume_fcb);
+ free_fcb(Vcb->root_fcb);
+
+ ExDeleteResourceLite(&Vcb->fcb_lock);
+ ExDeleteResourceLite(&Vcb->load_lock);
+ ExDeleteResourceLite(&Vcb->tree_lock);
+
+ // FIXME - flush trees
+
+ ZwClose(Vcb->flush_thread_handle);
+
+ ExFreePool(Vcb);
+}
+
+static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb;
+ BOOL top_level;
+
+ TRACE("cleanup\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (DeviceObject == devobj) {
+ TRACE("closing file system\n");
+
+ if (DeviceObject->DeviceExtension)
+ uninit(DeviceObject->DeviceExtension);
+
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ if (FileObject) {
+ fcb = FileObject->FsContext;
+
+ TRACE("cleanup called for FileObject %p\n", FileObject);
+ TRACE("fcb %p (%.*S), refcount = %u, open_count = %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount, fcb->open_count);
+
+ FileObject->Flags |= FO_CLEANUP_COMPLETE;
+
+ if (fcb->open_count == 1 && fcb->delete_on_close && fcb != fcb->Vcb->root_fcb && fcb != fcb->Vcb->volume_fcb) {
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ delete_fcb(fcb, FileObject);
+ // FIXME - change allocation size etc. to zero
+
+ release_tree_lock(fcb->Vcb, TRUE);
+ }
+
+ fcb->open_count--;
+ }
+
+ Status = STATUS_SUCCESS;
+
+exit:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+ ULONG att;
+ char* eaval;
+ UINT16 ealen;
+
+ if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
+ if (ealen > 2) {
+ if (eaval[0] == '0' && eaval[1] == 'x') {
+ int i;
+ ULONG dosnum = 0;
+
+ for (i = 2; i < ealen; i++) {
+ dosnum *= 0x10;
+
+ if (eaval[i] >= '0' && eaval[i] <= '9')
+ dosnum |= eaval[i] - '0';
+ else if (eaval[i] >= 'a' && eaval[i] <= 'f')
+ dosnum |= eaval[i] + 10 - 'a';
+ else if (eaval[i] >= 'A' && eaval[i] <= 'F')
+ dosnum |= eaval[i] + 10 - 'a';
+ }
+
+ TRACE("DOSATTRIB: %08x\n", dosnum);
+
+ ExFreePool(eaval);
+
+ return dosnum;
+ }
+ }
+
+ ExFreePool(eaval);
+ }
+
+ switch (type) {
+ case BTRFS_TYPE_DIRECTORY:
+ att = FILE_ATTRIBUTE_DIRECTORY;
+ break;
+
+ case BTRFS_TYPE_SYMLINK:
+ att = FILE_ATTRIBUTE_REPARSE_POINT;
+ break;
+
+ default:
+ att = 0;
+ break;
+ }
+
+ if (dotfile) {
+ att |= FILE_ATTRIBUTE_HIDDEN;
+ }
+
+ att |= FILE_ATTRIBUTE_ARCHIVE;
+
+ // FIXME - get READONLY from ii->st_mode
+ // FIXME - return SYSTEM for block/char devices?
+
+ if (att == 0)
+ att = FILE_ATTRIBUTE_NORMAL;
+
+ return att;
+}
+
+// static int STDCALL utf8icmp(char* a, char* b) {
+// return strcmp(a, b); // FIXME - don't treat as ASCII
+// }
+
+NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
+ IO_STATUS_BLOCK* IoStatus;
+ LARGE_INTEGER Offset;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ read_context* context;
+
+ num_reads++;
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
+
+ Offset.QuadPart = StartingOffset;
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
+ Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+ if (!Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (DeviceObject->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (DeviceObject->Flags & DO_DIRECT_IO) {
+// TRACE("direct IO\n");
+
+ Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ // IoFreeIrp(Irp);
+ goto exit;
+// } else {
+// TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+// TRACE("neither buffered nor direct IO\n");
+ Irp->UserBuffer = Buffer;
+ }
+
+ IrpSp->Parameters.Read.Length = Length;
+ IrpSp->Parameters.Read.ByteOffset = Offset;
+
+ Irp->UserIosb = IoStatus;
+// Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ Irp->UserEvent = &context->Event;
+
+// IoQueueThreadIrp(Irp);
+
+ IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
+
+// if (Override)
+// {
+// Stack = IoGetNextIrpStackLocation(Irp);
+// Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+// }
+
+// TRACE("Calling IO Driver... with irp %p\n", Irp);
+ Status = IoCallDriver(DeviceObject, Irp);
+
+// TRACE("Waiting for IO Operation for %p\n", Irp);
+ if (Status == STATUS_PENDING) {
+// TRACE("Operation pending\n");
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+// TRACE("Getting IO Status... for %p\n", Irp);
+ Status = context->iosb.Status;
+ }
+
+ if (DeviceObject->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+ ExFreePool(IoStatus);
+ ExFreePool(context);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) {
+ NTSTATUS Status;
+ superblock* sb;
+ unsigned int i, to_read;
+ UINT32 crc32;
+
+ to_read = sector_align(sizeof(superblock), device->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+
+ i = 0;
+
+ while (superblock_addrs[i] > 0) {
+ if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length)
+ break;
+
+ Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("Failed to read superblock %u: %08x\n", i, Status);
+ ExFreePool(sb);
+ return Status;
+ }
+
+ TRACE("got superblock %u!\n", i);
+
+ if (i == 0 || sb->generation > Vcb->superblock.generation)
+ RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
+
+ i++;
+ }
+
+ ExFreePool(sb);
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
+ crc32 = ~crc32;
+ TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
+
+ if (crc32 != *((UINT32*)Vcb->superblock.checksum))
+ return STATUS_INTERNAL_ERROR; // FIXME - correct error?
+
+ TRACE("label is %s\n", Vcb->superblock.label);
+// utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
+ ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override)
+{
+ PIRP Irp;
+ KEVENT Event;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION Stack;
+ IO_STATUS_BLOCK IoStatus;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Irp = IoBuildDeviceIoControlRequest(ControlCode,
+ DeviceObject,
+ InputBuffer,
+ InputBufferSize,
+ OutputBuffer,
+ OutputBufferSize,
+ FALSE,
+ &Event,
+ &IoStatus);
+
+ if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
+
+ if (Override) {
+ Stack = IoGetNextIrpStackLocation(Irp);
+ Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+ }
+
+ Status = IoCallDriver(DeviceObject, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ }
+
+ return Status;
+}
+
+// static void STDCALL find_chunk_root(device_extension* Vcb) {
+// UINT32 i;
+// KEY* key;
+//
+// i = 0;
+// while (i < Vcb->superblock.n) {
+// key = &Vcb->superblock.sys_chunk_array[i];
+// i += sizeof(KEY);
+// }
+//
+// // FIXME
+// }
+
+// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
+// if (!Vcb->log_to_phys) {
+// Vcb->log_to_phys = ltp;
+// ltp->next = NULL;
+// return;
+// }
+//
+// // FIXME - these should be ordered
+// ltp->next = Vcb->log_to_phys;
+// Vcb->log_to_phys = ltp;
+// }
+
+static void STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
+ root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
+
+ r->id = id;
+ r->treeholder.address = addr;
+ r->treeholder.tree = NULL;
+ init_tree_holder(&r->treeholder);
+ r->prev = NULL;
+ r->next = Vcb->roots;
+
+ r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
+ ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
+
+ r->lastinode = 0;
+
+ if (tp) {
+ RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
+ if (tp->item->size < sizeof(ROOT_ITEM))
+ RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
+ }
+
+ if (Vcb->roots)
+ Vcb->roots->prev = r;
+
+ Vcb->roots = r;
+
+ switch (r->id) {
+ case BTRFS_ROOT_ROOT:
+ Vcb->root_root = r;
+ break;
+
+ case BTRFS_ROOT_EXTENT:
+ Vcb->extent_root = r;
+ break;
+
+ case BTRFS_ROOT_CHUNK:
+ Vcb->chunk_root = r;
+ break;
+
+ case BTRFS_ROOT_DEVTREE:
+ Vcb->dev_root = r;
+ break;
+
+ case BTRFS_ROOT_CHECKSUM:
+ Vcb->checksum_root = r;
+ break;
+ }
+}
+
+static void STDCALL look_for_roots(device_extension* Vcb) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ BOOL b;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any root tree entries\n");
+ return;
+ }
+
+ do {
+ TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;;
+
+ TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
+
+ add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+}
+
+static void add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) {
+ disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
+
+ dh->address = address;
+ dh->size = size;
+ dh->provisional = FALSE;
+
+ InsertTailList(disk_holes, &dh->listentry);
+}
+
+static void find_disk_holes(device_extension* Vcb, device* dev) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ UINT64 lastaddr;
+
+ InitializeListHead(&dev->disk_holes);
+
+ searchkey.obj_id = dev->devitem.dev_id;
+ searchkey.obj_type = TYPE_DEV_EXTENT;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any dev tree entries\n");
+ return;
+ }
+
+ lastaddr = 0;
+
+ do {
+ if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
+ DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
+
+ if (tp.item->key.offset > lastaddr)
+ add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr);
+
+ lastaddr = tp.item->key.offset + de->length;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (lastaddr < dev->devitem.num_bytes)
+ add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
+
+ // FIXME - free disk_holes when unmounting
+}
+
+device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
+ UINT64 i;
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
+ Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7],
+ Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]);
+
+ if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ TRACE("returning device %llx\n", i);
+ return &Vcb->devices[i];
+ }
+ }
+
+ WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
+ uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
+
+ return NULL;
+}
+
+static void STDCALL load_chunk_root(device_extension* Vcb) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ BOOL b;
+ chunk* c;
+ UINT64 i;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any chunk tree entries\n");
+ return;
+ }
+
+ do {
+ TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) {
+ // FIXME - this is a hack; make this work with multiple devices!
+ RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+ } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
+ c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+
+ c->size = tp.item->size;
+ c->offset = tp.item->key.offset;
+ c->used = c->oldused = 0;
+ c->space_changed = FALSE;
+
+ c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
+
+ if (c->chunk_item->num_stripes > 0) {
+ CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+
+ c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < c->chunk_item->num_stripes; i++) {
+ c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
+ TRACE("device %llu = %p\n", i, c->devices[i]);
+ }
+ } else
+ c->devices = NULL;
+
+ InitializeListHead(&c->space);
+
+ InsertTailList(&Vcb->chunks, &c->list_entry);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ Vcb->log_to_phys_loaded = TRUE;
+}
+
+static BOOL load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ FREE_SPACE_ITEM* fsi;
+ UINT64 inode, num_sectors, i, generation;
+ INODE_ITEM* ii;
+ UINT8* data;
+ NTSTATUS Status;
+ UINT32 *checksums, crc32;
+#if DEBUG_LEVEL > 2
+ FREE_SPACE_ENTRY* fse;
+ UINT64 num_entries;
+#endif
+
+ TRACE("(%p, %llx)\n", Vcb, c->offset);
+
+ if (Vcb->superblock.generation != Vcb->superblock.cache_generation)
+ return FALSE;
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ fsi = (FREE_SPACE_ITEM*)tp.item->data;
+
+ if (fsi->generation != Vcb->superblock.cache_generation) {
+ WARN("cache had generation %llx, expecting %llx\n", fsi->generation, Vcb->superblock.cache_generation);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ if (fsi->key.obj_type != TYPE_INODE_ITEM) {
+ WARN("cache pointed to something other than an INODE_ITEM\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ if (fsi->num_bitmaps > 0) {
+ WARN("cache had bitmaps, unsure of how to deal with these\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ inode = fsi->key.obj_id;
+
+ searchkey = fsi->key;
+#if DEBUG_LEVEL > 2
+ num_entries = fsi->num_entries;
+#endif
+
+ if (!find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ ii = (INODE_ITEM*)tp2.item->data;
+
+ data = ExAllocatePoolWithTag(PagedPool, ii->st_size, ALLOC_TAG);
+
+ Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ num_sectors = ii->st_size / Vcb->superblock.sector_size;
+
+ generation = *(data + (num_sectors * sizeof(UINT32)));
+
+ if (generation != Vcb->superblock.cache_generation) {
+ ERR("generation was %llx, expected %llx\n", generation, Vcb->superblock.cache_generation);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * num_sectors, ALLOC_TAG); // FIXME - get rid of this
+ RtlCopyMemory(checksums, data, sizeof(UINT32) * num_sectors);
+
+ for (i = 0; i < num_sectors; i++) {
+ if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
+ crc32 = ~calc_crc32c(0xffffffff, &data[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+ else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
+ crc32 = 0; // FIXME - test this
+ else
+ crc32 = ~calc_crc32c(0xffffffff, &data[sizeof(UINT32) * num_sectors], ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
+
+ if (crc32 != checksums[i]) {
+ WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]);
+ ExFreePool(checksums);
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+ return FALSE;
+ }
+ }
+
+ ExFreePool(checksums);
+
+#if DEBUG_LEVEL > 2
+ fse = (FREE_SPACE_ENTRY*)&data[(sizeof(UINT32) * num_sectors) + sizeof(UINT64)];
+
+ for (i = 0; i < num_entries; i++) {
+ TRACE("(%llx,%llx,%x)\n", fse[i].offset, fse[i].size, fse[i].type);
+ }
+#endif
+
+ FIXME("FIXME - read cache\n");
+
+ ExFreePool(data);
+ free_traverse_ptr(&tp2);
+
+ return FALSE;
+}
+
+static void load_free_space_cache(device_extension* Vcb, chunk* c) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT64 lastaddr;
+ BOOL b;
+ space *s, *s2;
+ LIST_ENTRY* le;
+
+ load_stored_free_space_cache(Vcb, c);
+
+ TRACE("generating free space cache for chunk %llx\n", c->offset);
+
+ searchkey.obj_id = c->offset;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return;
+ }
+
+ lastaddr = c->offset;
+
+ do {
+ if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
+ break;
+
+ if (tp.item->key.obj_id >= c->offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
+ if (tp.item->key.obj_id > lastaddr) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = tp.item->key.obj_id - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+
+ if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
+ lastaddr = tp.item->key.obj_id + Vcb->superblock.node_size;
+ else
+ lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ }
+ } while (b);
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+
+ free_traverse_ptr(&tp);
+
+ // add allocated space
+
+ lastaddr = c->offset;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset > lastaddr) {
+ s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s2->offset = lastaddr;
+ s2->size = s->offset - lastaddr;
+ s2->type = SPACE_TYPE_USED;
+
+ InsertTailList(&s->list_entry, &s2->list_entry);
+ }
+
+ lastaddr = s->offset + s->size;
+
+ le = le->Flink;
+ }
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_USED;
+ InsertTailList(&c->space, &s->list_entry);
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+}
+
+void protect_superblocks(device_extension* Vcb, chunk* c) {
+ int i = 0, j;
+ UINT64 addr;
+
+ // FIXME - this will need modifying for RAID
+
+ while (superblock_addrs[i] != 0) {
+ CHUNK_ITEM* ci = c->chunk_item;
+ CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ for (j = 0; j < ci->num_stripes; j++) {
+ if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ addr = (superblock_addrs[i] - cis[j].offset) + c->offset;
+ TRACE("addr %llx\n", addr);
+
+ add_to_space_list(c, addr, sizeof(superblock), SPACE_TYPE_USED);
+ }
+ }
+
+ i++;
+ }
+}
+
+static void STDCALL find_chunk_usage(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ traverse_ptr tp;
+ BLOCK_GROUP_ITEM* bgi;
+
+// c00000,c0,800000
+// block_group_item size=7f0000 chunktreeid=100 flags=1
+
+ searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ searchkey.obj_id = c->offset;
+ searchkey.offset = c->chunk_item->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
+
+ c->used = c->oldused = bgi->used;
+
+ TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
+ }
+
+ free_traverse_ptr(&tp);
+// if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
+// cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+//
+// return (addr - c->offset) + cis->offset;
+// }
+
+ // FIXME - make sure we free occasionally after doing one of these, or we
+ // might use up a lot of memory with a big disk.
+
+ load_free_space_cache(Vcb, c);
+ protect_superblocks(Vcb, c);
+
+ le = le->Flink;
+ }
+}
+
+// static void STDCALL root_test(device_extension* Vcb) {
+// root* r;
+// KEY searchkey;
+// traverse_ptr tp, next_tp;
+// BOOL b;
+//
+// r = Vcb->roots;
+// while (r) {
+// if (r->id == 0x102)
+// break;
+// r = r->next;
+// }
+//
+// if (!r) {
+// ERR("Could not find root tree.\n");
+// return;
+// }
+//
+// searchkey.obj_id = 0x1b6;
+// searchkey.obj_type = 0xb;
+// searchkey.offset = 0;
+//
+// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
+// ERR("Could not find first item.\n");
+// return;
+// }
+//
+// b = TRUE;
+// do {
+// TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+// }
+
+static void load_sys_chunks(device_extension* Vcb) {
+ KEY key;
+ ULONG n = Vcb->superblock.n;
+
+ while (n > 0) {
+ if (n > sizeof(KEY)) {
+ RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
+ n -= sizeof(KEY);
+ } else
+ return;
+
+ TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
+
+ if (key.obj_type == TYPE_CHUNK_ITEM) {
+ CHUNK_ITEM* ci;
+ ULONG cisize;
+ sys_chunk* sc;
+
+ if (n < sizeof(CHUNK_ITEM))
+ return;
+
+ ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
+ cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
+
+ if (n < cisize)
+ return;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
+ sc->key = key;
+ sc->size = cisize;
+ sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
+ RtlCopyMemory(sc->data, ci, sc->size);
+ InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
+
+ n -= cisize;
+ } else {
+ ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
+ return;
+ }
+ }
+}
+
+static root* find_default_subvol(device_extension* Vcb) {
+ root* subvol;
+ UINT64 inode;
+ UINT8 type;
+ UNICODE_STRING filename;
+
+ static WCHAR fn[] = L"default";
+ static UINT32 crc32 = 0x8dbfc2d2;
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
+ filename.Buffer = fn;
+ filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR);
+
+ if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL))
+ WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
+ else
+ return subvol;
+ }
+
+ subvol = Vcb->roots;
+ while (subvol && subvol->id != BTRFS_ROOT_FSTREE)
+ subvol = subvol->next;
+
+ return subvol;
+}
+
+static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION Stack;
+ PDEVICE_OBJECT NewDeviceObject = NULL;
+ PDEVICE_OBJECT DeviceToMount;
+ NTSTATUS Status;
+ device_extension* Vcb = NULL;
+ PARTITION_INFORMATION_EX piex;
+ UINT64 i;
+ LIST_ENTRY* le;
+
+ TRACE("mount_vol called\n");
+
+ if (DeviceObject != devobj)
+ {
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ goto exit;
+ }
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+ DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
+
+// Status = NtfsHasFileSystem(DeviceToMount);
+// if (!NT_SUCCESS(Status))
+// {
+// goto ByeBye;
+// }
+
+ Status = IoCreateDevice(drvobj,
+ sizeof(device_extension),
+ NULL,
+ FILE_DEVICE_DISK_FILE_SYSTEM,
+ 0,
+ FALSE,
+ &NewDeviceObject);
+ if (!NT_SUCCESS(Status))
+ goto exit;
+
+// TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
+
+ NewDeviceObject->Flags |= DO_DIRECT_IO;
+ Vcb = (PVOID)NewDeviceObject->DeviceExtension;
+ RtlZeroMemory(Vcb, sizeof(device_extension));
+
+ InitializeListHead(&Vcb->tree_cache);
+
+ ExInitializeResourceLite(&Vcb->tree_lock);
+ Vcb->tree_lock_counter = 0;
+ Vcb->open_trees = 0;
+ Vcb->write_trees =0;
+
+ ExInitializeResourceLite(&Vcb->load_lock);
+ FsRtlEnterFileSystem();
+ ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
+
+// Vcb->Identifier.Type = NTFS_TYPE_VCB;
+// Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
+//
+// Status = NtfsGetVolumeData(DeviceToMount,
+// Vcb);
+// if (!NT_SUCCESS(Status))
+// goto ByeBye;
+
+// Vcb->device = DeviceToMount;
+ DeviceToMount->Flags |= DO_DIRECT_IO;
+
+// Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+// &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error reading disk geometry: %08x\n", Status);
+// goto exit;
+// } else {
+// TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
+// Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
+// Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
+// }
+
+ Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
+ &piex, sizeof(piex), TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error reading partition information: %08x\n", Status);
+ goto exit;
+ }
+
+ Vcb->length = piex.PartitionLength.QuadPart;
+ TRACE("partition length = %u\n", piex.PartitionLength);
+
+ Status = read_superblock(Vcb, DeviceToMount);
+ if (!NT_SUCCESS(Status))
+ goto exit;
+
+ if (Vcb->superblock.magic != BTRFS_MAGIC) {
+ ERR("not a BTRFS volume\n");
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ } else {
+ TRACE("btrfs magic found\n");
+ }
+
+ if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
+ WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ le = volumes.Flink;
+ while (le != &volumes) {
+ volume* v = CONTAINING_RECORD(le, volume, list_entry);
+
+ if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
+ // skipping over device in RAID which isn't the first one
+ // FIXME - hide this in My Computer
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ le = le->Flink;
+ }
+
+ // FIXME - remove this when we can
+ if (Vcb->superblock.num_devices > 1) {
+ WARN("not mounting - multiple devices not yet implemented\n");
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ Vcb->readonly = FALSE;
+ if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
+ WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
+ Vcb->readonly = TRUE;
+ }
+
+ Vcb->superblock.generation++;
+ Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
+
+ Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
+ Vcb->devices[0].devobj = DeviceToMount;
+ RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
+
+ if (Vcb->superblock.num_devices > 1)
+ RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
+
+ // FIXME - find other devices, if there are any
+
+ TRACE("DeviceToMount = %p\n", DeviceToMount);
+ TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
+
+ NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
+ Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
+ Stack->Parameters.MountVolume.Vpb->RealDevice = DeviceToMount;
+ Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
+ NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
+ NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+
+// find_chunk_root(Vcb);
+// Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
+
+// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
+
+ Vcb->roots = NULL;
+ Vcb->log_to_phys_loaded = FALSE;
+
+ Vcb->max_inline = Vcb->superblock.node_size / 2;
+
+// Vcb->write_trees = NULL;
+
+ add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
+
+ if (!Vcb->chunk_root) {
+ ERR("Could not load chunk root.\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ InitializeListHead(&Vcb->sys_chunks);
+ load_sys_chunks(Vcb);
+
+ InitializeListHead(&Vcb->chunks);
+ InitializeListHead(&Vcb->trees);
+
+ InitializeListHead(&Vcb->DirNotifyList);
+
+ FsRtlNotifyInitializeSync(&Vcb->NotifySync);
+
+ load_chunk_root(Vcb);
+
+ add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
+
+ if (!Vcb->root_root) {
+ ERR("Could not load root of roots.\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ look_for_roots(Vcb);
+
+ find_chunk_usage(Vcb);
+
+ Vcb->volume_fcb = create_fcb();
+ Vcb->volume_fcb->Vcb = Vcb;
+ Vcb->volume_fcb->sd = NULL;
+
+ Vcb->root_fcb = create_fcb();
+ Vcb->root_fcb->Vcb = Vcb;
+ Vcb->root_fcb->inode = SUBVOL_ROOT_INODE;
+ Vcb->root_fcb->type = BTRFS_TYPE_DIRECTORY;
+
+ Vcb->root_fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
+ Vcb->root_fcb->full_filename.Buffer[0] = '\\';
+ Vcb->root_fcb->full_filename.Length = Vcb->root_fcb->full_filename.MaximumLength = sizeof(WCHAR);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("volume FCB = %p\n", Vcb->volume_fcb);
+ WARN("root FCB = %p\n", Vcb->root_fcb);
+#endif
+
+ Vcb->root_fcb->subvol = find_default_subvol(Vcb);
+
+ if (!Vcb->root_fcb->subvol) {
+ ERR("could not find top subvol\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ Vcb->fcbs = Vcb->root_fcb;
+
+ if (!get_item(Vcb, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, TYPE_INODE_ITEM, 0, &Vcb->root_fcb->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find INODE_ITEM for root directory\n");
+ }
+
+ fcb_get_sd(Vcb->root_fcb);
+
+ Vcb->root_fcb->atts = get_file_attributes(Vcb, &Vcb->root_fcb->inode_item, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, Vcb->root_fcb->type,
+ FALSE, FALSE);
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ find_disk_holes(Vcb, &Vcb->devices[i]);
+ }
+
+ ExInitializeResourceLite(&Vcb->fcb_lock);
+
+// root_test(Vcb);
+
+// Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
+// Vcb->StorageDevice);
+//
+// InitializeListHead(&Vcb->FcbListHead);
+//
+// Fcb = NtfsCreateFCB(NULL, Vcb);
+// if (Fcb == NULL)
+// {
+// Status = STATUS_INSUFFICIENT_RESOURCES;
+// goto ByeBye;
+// }
+//
+// Ccb = ExAllocatePoolWithTag(NonPagedPool,
+// sizeof(NTFS_CCB),
+// TAG_CCB);
+// if (Ccb == NULL)
+// {
+// Status = STATUS_INSUFFICIENT_RESOURCES;
+// goto ByeBye;
+// }
+//
+// RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
+//
+// Ccb->Identifier.Type = NTFS_TYPE_CCB;
+// Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
+//
+// Vcb->StreamFileObject->FsContext = Fcb;
+// Vcb->StreamFileObject->FsContext2 = Ccb;
+// Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+// Vcb->StreamFileObject->PrivateCacheMap = NULL;
+// Vcb->StreamFileObject->Vpb = Vcb->Vpb;
+// Ccb->PtrFileObject = Vcb->StreamFileObject;
+// Fcb->FileObject = Vcb->StreamFileObject;
+// Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
+//
+// Fcb->Flags = FCB_IS_VOLUME_STREAM;
+//
+// Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
+// Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
+// Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
+//
+// CcInitializeCacheMap(Vcb->StreamFileObject,
+// (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+// FALSE,
+// &(NtfsGlobalData->CacheMgrCallbacks),
+// Fcb);
+//
+ ExInitializeResourceLite(&Vcb->DirResource);
+// ExInitializeResourceLite(&Vcb->LogToPhysLock);
+
+ KeInitializeSpinLock(&Vcb->FcbListLock);
+//
+// /* Get serial number */
+// NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
+//
+// /* Get volume label */
+// NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
+// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
+// Vcb->NtfsInfo.VolumeLabel,
+// Vcb->NtfsInfo.VolumeLabelLength);
+ NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
+ NewDeviceObject->Vpb->VolumeLabel[0] = '?';
+ NewDeviceObject->Vpb->VolumeLabel[1] = 0;
+
+ Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = STATUS_SUCCESS;
+
+exit:
+// if (!NT_SUCCESS(Status))
+// {
+// /* Cleanup */
+// if (Vcb && Vcb->StreamFileObject)
+// ObDereferenceObject(Vcb->StreamFileObject);
+//
+// if (Fcb)
+// ExFreePool(Fcb);
+//
+// if (Ccb)
+// ExFreePool(Ccb);
+//
+// if (NewDeviceObject)
+// IoDeleteDevice(NewDeviceObject);
+// }
+
+ if (Vcb) {
+ ExReleaseResourceLite(&Vcb->load_lock);
+ FsRtlExitFileSystem();
+ }
+
+ TRACE("mount_vol done (status: %lx)\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS status;
+ BOOL top_level;
+
+ TRACE("file system control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ status = STATUS_NOT_IMPLEMENTED;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Irp->IoStatus.Information = 0;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_MOUNT_VOLUME:
+ TRACE("IRP_MN_MOUNT_VOLUME\n");
+
+// Irp->IoStatus.Status = STATUS_SUCCESS;
+ status = mount_vol(DeviceObject, Irp);
+// IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
+// IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
+
+// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+// return Irp->IoStatus.Status;
+ break;
+
+ case IRP_MN_KERNEL_CALL:
+ TRACE("IRP_MN_KERNEL_CALL\n");
+ break;
+
+ case IRP_MN_LOAD_FILE_SYSTEM:
+ TRACE("IRP_MN_LOAD_FILE_SYSTEM\n");
+ break;
+
+ case IRP_MN_USER_FS_REQUEST:
+ TRACE("IRP_MN_USER_FS_REQUEST\n");
+
+ status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
+ break;
+
+ case IRP_MN_VERIFY_VOLUME:
+ TRACE("IRP_MN_VERIFY_VOLUME\n");
+ break;
+
+ default:
+ WARN("unknown minor %u\n", IrpSp->MinorFunction);
+ break;
+
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return status;
+}
+
+static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ fcb* fcb = IrpSp->FileObject->FsContext;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ TRACE("lock control\n");
+
+ Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb;
+ BOOL top_level;
+
+ FIXME("STUB: device control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ WARN("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+
+ }
+
+ fcb = FileObject->FsContext;
+
+ if (!fcb) {
+ ERR("FCB was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (fcb == Vcb->volume_fcb) {
+ FIXME("FIXME - pass through\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ } else {
+ TRACE("filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+ TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ default:
+ WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xff0000) >> 16,
+ (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xc000) >> 14, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3ffc) >> 2,
+ IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3);
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+end:
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ uid_map* um;
+ BOOL top_level;
+
+ TRACE("shutdown\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ // FIXME - free Vcb->devices
+
+ // FIXME - only do this when last device is unmounted?
+ while ((le = RemoveHeadList(&uid_map_list)) != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ ExFreePool(um->sid);
+
+ ExFreePool(um);
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL drv_pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FIXME("STUB: pnp\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ TRACE(" IRP_MN_CANCEL_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ TRACE(" IRP_MN_QUERY_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ TRACE(" IRP_MN_REMOVE_DEVICE\n");
+ break;
+
+ case IRP_MN_START_DEVICE:
+ TRACE(" IRP_MN_START_DEVICE\n");
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ TRACE(" IRP_MN_SURPRISE_REMOVAL\n");
+ break;
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ TRACE(" IRP_MN_QUERY_DEVICE_RELATIONS\n");
+ break;
+
+ default:
+ WARN("Unrecognized minor function 0x%x\n", IrpSp->MinorFunction);
+ break;
+ }
+
+// Irp->IoStatus.Status = Status;
+// Irp->IoStatus.Information = 0;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+
+ Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+
+// IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+#ifdef DEBUG
+static void STDCALL init_serial() {
+ UNICODE_STRING us;
+ NTSTATUS Status;
+
+ static const WCHAR com1[] = {'\\','D','e','v','i','c','e','\\','S','e','r','i','a','l','0',0};
+
+ RtlInitUnicodeString(&us, com1);
+
+ Status = IoGetDeviceObjectPointer(&us, FILE_WRITE_DATA, &comfo, &comdo);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ }
+}
+#endif
+
+#ifndef __REACTOS__
+static void STDCALL check_cpu() {
+ unsigned int cpuInfo[4];
+#ifndef _MSC_VER
+ __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
+ have_sse42 = cpuInfo[2] & bit_SSE4_2;
+#else
+ __cpuid(cpuInfo, 1);
+ have_sse42 = cpuInfo[2] & (1 << 20);
+#endif
+
+ if (have_sse42)
+ TRACE("SSE4.2 is supported\n");
+ else
+ TRACE("SSE4.2 not supported\n");
+}
+#endif
+
+static void STDCALL read_registry(PUNICODE_STRING regpath) {
+ HANDLE h;
+ UNICODE_STRING us;
+ OBJECT_ATTRIBUTES oa;
+ ULONG dispos;
+ NTSTATUS Status;
+ WCHAR* path;
+ ULONG kvfilen, retlen, i;
+ KEY_VALUE_FULL_INFORMATION* kvfi;
+
+ const WCHAR mappings[] = L"\\Mappings";
+
+ path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG);
+ RtlCopyMemory(path, regpath->Buffer, regpath->Length);
+ RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR));
+
+ us.Buffer = path;
+ us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR));
+
+ InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ // FIXME - keep open and do notify for changes
+ Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwCreateKey returned %08x\n", Status);
+ goto exit;
+ }
+
+// TRACE("dispos = %u\n", dispos);
+
+ if (dispos == REG_OPENED_EXISTING_KEY) {
+ kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
+ kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
+
+ i = 0;
+ do {
+ Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
+
+ if (NT_SUCCESS(Status) && kvfi->DataLength > 0) {
+ UINT32 val = 0;
+
+ RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32)));
+
+ TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val);
+
+ add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
+ }
+
+ i = i + 1;
+ } while (Status != STATUS_NO_MORE_ENTRIES);
+ }
+
+// InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+//
+// Status = ZwOpenKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("ZwOpenKey returned %08x\n", Status);
+// return;
+// }
+
+ ZwClose(h);
+
+exit:
+ ExFreePool(path);
+}
+
+NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
+ NTSTATUS Status;
+ PDEVICE_OBJECT DeviceObject;
+ UNICODE_STRING device_nameW;
+ UNICODE_STRING dosdevice_nameW;
+
+#ifdef DEBUG
+ init_serial();
+#endif
+
+ TRACE("DriverEntry\n");
+
+#ifndef __REACTOS__
+ check_cpu();
+#endif
+
+// TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
+
+ drvobj = DriverObject;
+
+ DriverObject->DriverUnload = DriverUnload;
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close;
+ DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)drv_write;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)drv_query_information;
+ DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)drv_set_information;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)drv_query_ea;
+ DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)drv_set_ea;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)drv_flush_buffers;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
+ DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_set_volume_information;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
+ DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)drv_directory_control;
+ DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_file_system_control;
+ DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)drv_query_security;
+ DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)drv_set_security;
+
+ device_nameW.Buffer = device_name;
+ device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) * sizeof(WCHAR);
+ dosdevice_nameW.Buffer = dosdevice_name;
+ dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+
+ Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &DeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateDevice returned %08x\n", Status);
+ return Status;
+ }
+
+ devobj = DeviceObject;
+
+ InitializeListHead(&uid_map_list);
+ read_registry(RegistryPath);
+
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateSymbolicLink returned %08x\n", Status);
+ return Status;
+ }
+
+ init_cache();
+
+ InitializeListHead(&volumes);
+ look_for_vols(&volumes);
+
+ IoRegisterFileSystem(DeviceObject);
+
+ return STATUS_SUCCESS;
+}
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+/******************************************************************************
+ * RtlUnicodeToUTF8N [NTDLL.@]
+ */
+NTSTATUS WINAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+ ULONG *utf8_bytes_written,
+ const WCHAR *uni_src, ULONG uni_bytes)
+{
+ NTSTATUS status;
+ ULONG i;
+ ULONG written;
+ ULONG ch;
+ BYTE utf8_ch[4];
+ ULONG utf8_ch_len;
+
+ if (!uni_src)
+ return STATUS_INVALID_PARAMETER_4;
+ if (!utf8_bytes_written)
+ return STATUS_INVALID_PARAMETER;
+ if (utf8_dest && uni_bytes % sizeof(WCHAR))
+ return STATUS_INVALID_PARAMETER_5;
+
+ written = 0;
+ status = STATUS_SUCCESS;
+
+ for (i = 0; i < uni_bytes / sizeof(WCHAR); i++)
+ {
+ /* decode UTF-16 into ch */
+ ch = uni_src[i];
+ if (ch >= 0xdc00 && ch <= 0xdfff)
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ if (i + 1 < uni_bytes / sizeof(WCHAR))
+ {
+ ch -= 0xd800;
+ ch <<= 10;
+ if (uni_src[i + 1] >= 0xdc00 && uni_src[i + 1] <= 0xdfff)
+ {
+ ch |= uni_src[i + 1] - 0xdc00;
+ ch += 0x010000;
+ i++;
+ }
+ else
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ }
+ else
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ }
+
+ /* encode ch as UTF-8 */
+#ifndef __REACTOS__
+ assert(ch <= 0x10ffff);
+#endif
+ if (ch < 0x80)
+ {
+ utf8_ch[0] = ch & 0x7f;
+ utf8_ch_len = 1;
+ }
+ else if (ch < 0x800)
+ {
+ utf8_ch[0] = 0xc0 | (ch >> 6 & 0x1f);
+ utf8_ch[1] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 2;
+ }
+ else if (ch < 0x10000)
+ {
+ utf8_ch[0] = 0xe0 | (ch >> 12 & 0x0f);
+ utf8_ch[1] = 0x80 | (ch >> 6 & 0x3f);
+ utf8_ch[2] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 3;
+ }
+ else if (ch < 0x200000)
+ {
+ utf8_ch[0] = 0xf0 | (ch >> 18 & 0x07);
+ utf8_ch[1] = 0x80 | (ch >> 12 & 0x3f);
+ utf8_ch[2] = 0x80 | (ch >> 6 & 0x3f);
+ utf8_ch[3] = 0x80 | (ch >> 0 & 0x3f);
+ utf8_ch_len = 4;
+ }
+
+ if (!utf8_dest)
+ {
+ written += utf8_ch_len;
+ continue;
+ }
+
+ if (utf8_bytes_max >= utf8_ch_len)
+ {
+ memcpy(utf8_dest, utf8_ch, utf8_ch_len);
+ utf8_dest += utf8_ch_len;
+ utf8_bytes_max -= utf8_ch_len;
+ written += utf8_ch_len;
+ }
+ else
+ {
+ utf8_bytes_max = 0;
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ *utf8_bytes_written = written;
+ return status;
+}
+
+
+/******************************************************************************
+ * RtlUTF8ToUnicodeN [NTDLL.@]
+ */
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+ ULONG *uni_bytes_written,
+ const CHAR *utf8_src, ULONG utf8_bytes)
+{
+ NTSTATUS status;
+ ULONG i, j;
+ ULONG written;
+ ULONG ch;
+ ULONG utf8_trail_bytes;
+ WCHAR utf16_ch[3];
+ ULONG utf16_ch_len;
+
+ if (!utf8_src)
+ return STATUS_INVALID_PARAMETER_4;
+ if (!uni_bytes_written)
+ return STATUS_INVALID_PARAMETER;
+
+ written = 0;
+ status = STATUS_SUCCESS;
+
+ for (i = 0; i < utf8_bytes; i++)
+ {
+ /* read UTF-8 lead byte */
+ ch = (BYTE)utf8_src[i];
+ utf8_trail_bytes = 0;
+ if (ch >= 0xf5)
+ {
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0xf0)
+ {
+ ch &= 0x07;
+ utf8_trail_bytes = 3;
+ }
+ else if (ch >= 0xe0)
+ {
+ ch &= 0x0f;
+ utf8_trail_bytes = 2;
+ }
+ else if (ch >= 0xc2)
+ {
+ ch &= 0x1f;
+ utf8_trail_bytes = 1;
+ }
+ else if (ch >= 0x80)
+ {
+ /* overlong or trail byte */
+ ch = 0xfffd;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+
+ /* read UTF-8 trail bytes */
+ if (i + utf8_trail_bytes < utf8_bytes)
+ {
+ for (j = 0; j < utf8_trail_bytes; j++)
+ {
+ if ((utf8_src[i + 1] & 0xc0) == 0x80)
+ {
+ ch <<= 6;
+ ch |= utf8_src[i + 1] & 0x3f;
+ i++;
+ }
+ else
+ {
+ ch = 0xfffd;
+ utf8_trail_bytes = 0;
+ status = STATUS_SOME_NOT_MAPPED;
+ break;
+ }
+ }
+ }
+ else
+ {
+ ch = 0xfffd;
+ utf8_trail_bytes = 0;
+ status = STATUS_SOME_NOT_MAPPED;
+ i = utf8_bytes;
+ }
+
+ /* encode ch as UTF-16 */
+ if ((ch > 0x10ffff) ||
+ (ch >= 0xd800 && ch <= 0xdfff) ||
+ (utf8_trail_bytes == 2 && ch < 0x00800) ||
+ (utf8_trail_bytes == 3 && ch < 0x10000))
+ {
+ /* invalid codepoint or overlong encoding */
+ utf16_ch[0] = 0xfffd;
+ utf16_ch[1] = 0xfffd;
+ utf16_ch[2] = 0xfffd;
+ utf16_ch_len = utf8_trail_bytes;
+ status = STATUS_SOME_NOT_MAPPED;
+ }
+ else if (ch >= 0x10000)
+ {
+ /* surrogate pair */
+ ch -= 0x010000;
+ utf16_ch[0] = 0xd800 + (ch >> 10 & 0x3ff);
+ utf16_ch[1] = 0xdc00 + (ch >> 0 & 0x3ff);
+ utf16_ch_len = 2;
+ }
+ else
+ {
+ /* single unit */
+ utf16_ch[0] = ch;
+ utf16_ch_len = 1;
+ }
+
+ if (!uni_dest)
+ {
+ written += utf16_ch_len;
+ continue;
+ }
+
+ for (j = 0; j < utf16_ch_len; j++)
+ {
+ if (uni_bytes_max >= sizeof(WCHAR))
+ {
+ *uni_dest++ = utf16_ch[j];
+ uni_bytes_max -= sizeof(WCHAR);
+ written++;
+ }
+ else
+ {
+ uni_bytes_max = 0;
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ }
+
+ *uni_bytes_written = written * sizeof(WCHAR);
+ return status;
+}
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
Index: reactos/drivers/filesystems/btrfs/btrfs.h
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.h (working copy)
@@ -0,0 +1,403 @@
+/* btrfs.h
+ * Generic btrfs header file. Thanks to whoever it was who wrote
+ * https://btrfs.wiki.kernel.org/index.php/On-disk_Format - you saved me a lot of time!
+ *
+ * I release this file, and this file only, into the public domain - do whatever
+ * you want with it. You don't have to, but I'd appreciate if you let me know if you
+ * use it anything cool - mark@harmstone.com. */
+
+#ifndef BTRFS_H_DEFINED
+#define BTRFS_H_DEFINED
+
+static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4000000000000, 0 };
+
+#define BTRFS_MAGIC 0x4d5f53665248425f
+#define MAX_LABEL_SIZE 0x100
+#define SUBVOL_ROOT_INODE 0x100
+
+#define TYPE_INODE_ITEM 0x01
+#define TYPE_INODE_REF 0x0C
+#define TYPE_INODE_EXTREF 0x0D
+#define TYPE_XATTR_ITEM 0x18
+#define TYPE_DIR_ITEM 0x54
+#define TYPE_DIR_INDEX 0x60
+#define TYPE_EXTENT_DATA 0x6C
+#define TYPE_EXTENT_CSUM 0x80
+#define TYPE_ROOT_ITEM 0x84
+#define TYPE_ROOT_BACKREF 0x90
+#define TYPE_ROOT_REF 0x9C
+#define TYPE_EXTENT_ITEM 0xA8
+#define TYPE_METADATA_ITEM 0xA9
+#define TYPE_TREE_BLOCK_REF 0xB0
+#define TYPE_EXTENT_DATA_REF 0xB2
+#define TYPE_EXTENT_REF_V0 0xB4
+#define TYPE_SHARED_BLOCK_REF 0xB6
+#define TYPE_SHARED_DATA_REF 0xB8
+#define TYPE_BLOCK_GROUP_ITEM 0xC0
+#define TYPE_DEV_EXTENT 0xCC
+#define TYPE_DEV_ITEM 0xD8
+#define TYPE_CHUNK_ITEM 0xE4
+
+#define BTRFS_ROOT_ROOT 1
+#define BTRFS_ROOT_EXTENT 2
+#define BTRFS_ROOT_CHUNK 3
+#define BTRFS_ROOT_DEVTREE 4
+#define BTRFS_ROOT_FSTREE 5
+#define BTRFS_ROOT_CHECKSUM 7
+
+#define BTRFS_COMPRESSION_NONE 0
+#define BTRFS_COMPRESSION_ZLIB 1
+#define BTRFS_COMPRESSION_LZO 2
+
+#define BTRFS_ENCRYPTION_NONE 0
+
+#define BTRFS_ENCODING_NONE 0
+
+#define EXTENT_TYPE_INLINE 0
+#define EXTENT_TYPE_REGULAR 1
+#define EXTENT_TYPE_PREALLOC 2
+
+#define BLOCK_FLAG_DATA 0x001
+#define BLOCK_FLAG_SYSTEM 0x002
+#define BLOCK_FLAG_METADATA 0x004
+#define BLOCK_FLAG_RAID0 0x008
+#define BLOCK_FLAG_RAID1 0x010
+#define BLOCK_FLAG_DUPLICATE 0x020
+#define BLOCK_FLAG_RAID10 0x040
+#define BLOCK_FLAG_RAID5 0x080
+#define BLOCK_FLAG_RAID6 0x100
+
+#define FREE_SPACE_CACHE_ID 0xFFFFFFFFFFFFFFF5
+#define EXTENT_CSUM_ID 0xFFFFFFFFFFFFFFF6
+
+#define BTRFS_INODE_NODATASUM 0x001
+#define BTRFS_INODE_NODATACOW 0x002
+#define BTRFS_INODE_READONLY 0x004
+#define BTRFS_INODE_NOCOMPRESS 0x008
+#define BTRFS_INODE_PREALLOC 0x010
+#define BTRFS_INODE_SYNC 0x020
+#define BTRFS_INODE_IMMUTABLE 0x040
+#define BTRFS_INODE_APPEND 0x080
+#define BTRFS_INODE_NODUMP 0x100
+#define BTRFS_INODE_NOATIME 0x200
+#define BTRFS_INODE_DIRSYNC 0x400
+#define BTRFS_INODE_COMPRESS 0x800
+
+#define BTRFS_SUBVOL_READONLY 0x1
+
+#define BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE 0x1
+
+#define BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF 0x0001
+#define BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL 0x0002
+#define BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS 0x0004
+#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO 0x0008
+#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZOV2 0x0010
+#define BTRFS_INCOMPAT_FLAGS_BIG_METADATA 0x0020
+#define BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF 0x0040
+#define BTRFS_INCOMPAT_FLAGS_RAID56 0x0080
+#define BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA 0x0100
+#define BTRFS_INCOMPAT_FLAGS_NO_HOLES 0x0200
+
+#pragma pack(push, 1)
+
+typedef struct {
+ UINT8 uuid[16];
+} BTRFS_UUID;
+
+typedef struct {
+ UINT64 obj_id;
+ UINT8 obj_type;
+ UINT64 offset;
+} KEY;
+
+#define HEADER_FLAG_MIXED_BACKREF 0x100000000000000
+#define HEADER_FLAG_SHARED_BACKREF 0x000000000000002
+
+typedef struct {
+ UINT8 csum[32];
+ BTRFS_UUID fs_uuid;
+ UINT64 address;
+ UINT64 flags;
+ BTRFS_UUID chunk_tree_uuid;
+ UINT64 generation;
+ UINT64 tree_id;
+ UINT32 num_items;
+ UINT8 level;
+} tree_header;
+
+typedef struct {
+ KEY key;
+ UINT32 offset;
+ UINT32 size;
+} leaf_node;
+
+typedef struct {
+ KEY key;
+ UINT64 address;
+ UINT64 generation;
+} internal_node;
+
+typedef struct {
+ UINT64 dev_id;
+ UINT64 num_bytes;
+ UINT64 bytes_used;
+ UINT32 optimal_io_align;
+ UINT32 optimal_io_width;
+ UINT32 minimal_io_size;
+ UINT64 type;
+ UINT64 generation;
+ UINT64 start_offset;
+ UINT32 dev_group;
+ UINT8 seek_speed;
+ UINT8 bandwidth;
+ BTRFS_UUID device_uuid;
+ BTRFS_UUID fs_uuid;
+} DEV_ITEM;
+
+#define SYS_CHUNK_ARRAY_SIZE 0x800
+
+typedef struct {
+ UINT8 checksum[32];
+ BTRFS_UUID uuid;
+ UINT64 sb_phys_addr;
+ UINT64 flags;
+ UINT64 magic;
+ UINT64 generation;
+ UINT64 root_tree_addr;
+ UINT64 chunk_tree_addr;
+ UINT64 log_tree_addr;
+ UINT64 log_root_transid;
+ UINT64 total_bytes;
+ UINT64 bytes_used;
+ UINT64 root_dir_objectid;
+ UINT64 num_devices;
+ UINT32 sector_size;
+ UINT32 node_size;
+ UINT32 leaf_size;
+ UINT32 stripe_size;
+ UINT32 n;
+ UINT64 chunk_root_generation;
+ UINT64 compat_flags;
+ UINT64 compat_ro_flags;
+ UINT64 incompat_flags;
+ UINT16 csum_type;
+ UINT8 root_level;
+ UINT8 chunk_root_level;
+ UINT8 log_root_level;
+ DEV_ITEM dev_item;
+ char label[MAX_LABEL_SIZE];
+ UINT64 cache_generation;
+ UINT64 uuid_tree_generation;
+ UINT64 reserved[30];
+ UINT8 sys_chunk_array[SYS_CHUNK_ARRAY_SIZE];
+// struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
+ UINT8 reserved2[1237];
+} superblock;
+
+#define BTRFS_TYPE_UNKNOWN 0
+#define BTRFS_TYPE_FILE 1
+#define BTRFS_TYPE_DIRECTORY 2
+#define BTRFS_TYPE_CHARDEV 3
+#define BTRFS_TYPE_BLOCKDEV 4
+#define BTRFS_TYPE_FIFO 5
+#define BTRFS_TYPE_SOCKET 6
+#define BTRFS_TYPE_SYMLINK 7
+#define BTRFS_TYPE_EA 8
+
+typedef struct {
+ KEY key;
+ UINT64 transid;
+ UINT16 m;
+ UINT16 n;
+ UINT8 type;
+ char name[1];
+} DIR_ITEM;
+
+typedef struct {
+ UINT64 seconds;
+ UINT32 nanoseconds;
+} BTRFS_TIME;
+
+typedef struct {
+ UINT64 generation;
+ UINT64 transid;
+ UINT64 st_size;
+ UINT64 st_blocks;
+ UINT64 block_group;
+ UINT32 st_nlink;
+ UINT32 st_uid;
+ UINT32 st_gid;
+ UINT32 st_mode;
+ UINT64 st_rdev;
+ UINT64 flags;
+ UINT64 sequence;
+ UINT8 reserved[32];
+ BTRFS_TIME st_atime;
+ BTRFS_TIME st_ctime;
+ BTRFS_TIME st_mtime;
+ BTRFS_TIME otime;
+} INODE_ITEM;
+
+typedef struct {
+ INODE_ITEM inode;
+ UINT64 generation;
+ UINT64 objid;
+ UINT64 block_number;
+ UINT64 byte_limit;
+ UINT64 bytes_used;
+ UINT64 last_snapshot_generation;
+ UINT64 flags;
+ UINT32 num_references;
+ KEY drop_progress;
+ UINT8 drop_level;
+ UINT8 root_level;
+ UINT64 generation2;
+ BTRFS_UUID uuid;
+ BTRFS_UUID parent_uuid;
+ BTRFS_UUID received_uuid;
+ UINT64 ctransid;
+ UINT64 otransid;
+ UINT64 stransid;
+ UINT64 rtransid;
+ BTRFS_TIME ctime;
+ BTRFS_TIME otime;
+ BTRFS_TIME stime;
+ BTRFS_TIME rtime;
+ UINT64 reserved[8];
+} ROOT_ITEM;
+
+typedef struct {
+ UINT64 size;
+ UINT64 root_id;
+ UINT64 stripe_length;
+ UINT64 type;
+ UINT32 opt_io_alignment;
+ UINT32 opt_io_width;
+ UINT32 sector_size;
+ UINT16 num_stripes;
+ UINT16 sub_stripes;
+} CHUNK_ITEM;
+
+typedef struct {
+ UINT64 dev_id;
+ UINT64 offset;
+ BTRFS_UUID dev_uuid;
+} CHUNK_ITEM_STRIPE;
+
+typedef struct {
+ UINT64 generation;
+ UINT64 decoded_size;
+ UINT8 compression;
+ UINT8 encryption;
+ UINT16 encoding;
+ UINT8 type;
+ UINT8 data[1];
+} EXTENT_DATA;
+
+typedef struct {
+ UINT64 address;
+ UINT64 size;
+ UINT64 offset;
+ UINT64 num_bytes;
+} EXTENT_DATA2;
+
+typedef struct {
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} INODE_REF;
+
+typedef struct {
+ UINT64 dir;
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} INODE_EXTREF;
+
+#define EXTENT_ITEM_DATA 0x001
+#define EXTENT_ITEM_TREE_BLOCK 0x002
+#define EXTENT_ITEM_SHARED_BACKREFS 0x100
+
+typedef struct {
+ UINT64 refcount;
+ UINT64 generation;
+ UINT64 flags;
+} EXTENT_ITEM;
+
+typedef struct {
+ UINT32 refcount;
+} EXTENT_ITEM_V0;
+
+typedef struct {
+ EXTENT_ITEM extent_item;
+ KEY firstitem;
+ UINT8 level;
+} EXTENT_ITEM_TREE;
+
+typedef struct {
+ UINT64 offset;
+} TREE_BLOCK_REF;
+
+typedef struct {
+ UINT64 root;
+ UINT64 objid;
+ UINT64 offset;
+ UINT32 count;
+} EXTENT_DATA_REF;
+
+typedef struct {
+ UINT64 used;
+ UINT64 chunk_tree;
+ UINT64 flags;
+} BLOCK_GROUP_ITEM;
+
+typedef struct {
+ UINT64 root;
+ UINT64 gen;
+ UINT64 objid;
+ UINT64 count;
+} EXTENT_REF_V0;
+
+typedef struct {
+ UINT64 offset;
+} SHARED_BLOCK_REF;
+
+typedef struct {
+ UINT64 offset;
+ UINT32 count;
+} SHARED_DATA_REF;
+
+#define FREE_SPACE_EXTENT 1
+#define FREE_SPACE_BITMAP 2
+
+typedef struct {
+ UINT64 offset;
+ UINT64 size;
+ UINT8 type;
+} FREE_SPACE_ENTRY;
+
+typedef struct {
+ KEY key;
+ UINT64 generation;
+ UINT64 num_entries;
+ UINT64 num_bitmaps;
+} FREE_SPACE_ITEM;
+
+typedef struct {
+ UINT64 dir;
+ UINT64 index;
+ UINT16 n;
+ char name[1];
+} ROOT_REF;
+
+typedef struct {
+ UINT64 chunktree;
+ UINT64 objid;
+ UINT64 address;
+ UINT64 length;
+ BTRFS_UUID chunktree_uuid;
+} DEV_EXTENT;
+
+#pragma pack(pop)
+
+#endif
Index: reactos/drivers/filesystems/btrfs/btrfs.rc
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs.rc (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs.rc (working copy)
@@ -0,0 +1,101 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904b0"
+ BEGIN
+ VALUE "FileDescription", "WinBtrfs"
+ VALUE "FileVersion", "0.1"
+ VALUE "InternalName", "btrfs"
+ VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
+ VALUE "OriginalFilename", "btrfs.sys"
+ VALUE "ProductName", "WinBtrfs"
+ VALUE "ProductVersion", "0.1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
Index: reactos/drivers/filesystems/btrfs/btrfs_drv.h
===================================================================
--- reactos/drivers/filesystems/btrfs/btrfs_drv.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/btrfs_drv.h (working copy)
@@ -0,0 +1,644 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifndef BTRFS_DRV_H_DEFINED
+#define BTRFS_DRV_H_DEFINED
+
+#ifndef __REACTOS__
+#undef _WIN32_WINNT
+#undef NTDDI_VERSION
+
+#define _WIN32_WINNT 0x0600
+#define NTDDI_VERSION 0x06010000 // Win 7
+#define _CRT_SECURE_NO_WARNINGS
+#endif /* __REACTOS__ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+#include
+#include
+#include
+#include
+#include
+#include "btrfs.h"
+
+#ifdef _DEBUG
+// #define DEBUG_TREE_REFCOUNTS
+// #define DEBUG_FCB_REFCOUNTS
+// #define DEBUG_LONG_MESSAGES
+#define DEBUG_PARANOID
+#define DEBUG_LEVEL 2 // 0 = nothing, 1 = ERR and FIXME, 2 = WARN, 3 = TRACE
+#else
+#define DEBUG_LEVEL 1
+#endif
+
+#define BTRFS_NODE_TYPE_CCB 0x2295
+#define BTRFS_NODE_TYPE_FCB 0x2296
+
+#define ALLOC_TAG 0x4D484274 //'MHBt'
+
+#define STDCALL __stdcall
+
+#define UID_NOBODY 65534
+#define GID_NOBODY 65534
+
+#define EA_NTACL "security.NTACL"
+#define EA_NTACL_HASH 0x45922146
+
+#define EA_DOSATTRIB "user.DOSATTRIB"
+#define EA_DOSATTRIB_HASH 0x914f9939
+
+#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
+
+#ifdef _MSC_VER
+#define try __try
+#define except __except
+#define finally __finally
+#else
+#define try if (1)
+#define except(x) if (0 && (x))
+#define finally if (1)
+#endif
+
+// #pragma pack(push, 1)
+
+struct device_extension;
+
+typedef struct {
+ PDEVICE_OBJECT devobj;
+ BTRFS_UUID fsuuid;
+ BTRFS_UUID devuuid;
+ UINT64 devnum;
+ LIST_ENTRY list_entry;
+} volume;
+
+typedef struct _fcb_nonpaged {
+ FAST_MUTEX HeaderMutex;
+ SECTION_OBJECT_POINTERS segment_object;
+ ERESOURCE resource;
+} fcb_nonpaged;
+
+struct _root;
+
+typedef struct _fcb {
+ FSRTL_ADVANCED_FCB_HEADER Header;
+ struct _fcb_nonpaged* nonpaged;
+ LONG refcount;
+ ULONG open_count;
+ UNICODE_STRING filepart;
+ ANSI_STRING utf8;
+ struct _device_extension* Vcb;
+ struct _fcb* par;
+ struct _fcb* prev;
+ struct _fcb* next;
+ struct _root* subvol;
+ LIST_ENTRY children;
+ UINT64 inode;
+ UINT8 type;
+ BOOL delete_on_close;
+ INODE_ITEM inode_item;
+ UNICODE_STRING full_filename;
+ ULONG name_offset;
+ SECURITY_DESCRIPTOR* sd;
+ FILE_LOCK lock;
+ BOOL deleted;
+ PKTHREAD lazy_writer_thread;
+ ULONG atts;
+
+ BOOL ads;
+ UINT32 adssize;
+ UINT32 adshash;
+ ANSI_STRING adsxattr;
+
+ LIST_ENTRY list_entry;
+} fcb;
+
+typedef struct _ccb {
+ USHORT NodeType;
+ CSHORT NodeSize;
+ ULONG disposition;
+ ULONG options;
+ UINT64 query_dir_offset;
+// char* query_string;
+ UNICODE_STRING query_string;
+ BOOL has_wildcard;
+ BOOL specific_file;
+} ccb;
+
+// typedef struct _log_to_phys {
+// UINT64 address;
+// UINT64 size;
+// UINT64 physaddr;
+// UINT32 sector_size;
+// struct _log_to_phys* next;
+// } log_to_phys;
+
+struct _device_extension;
+
+// enum tree_holder_status {
+// tree_holder_unloaded,
+// tree_holder_loading,
+// tree_holder_loaded,
+// tree_holder_unloading
+// };
+
+// typedef struct {
+// enum tree_holder_status status;
+// KSPIN_LOCK spin_lock;
+// ERESOURCE lock;
+// } tree_holder_nonpaged;
+
+typedef struct {
+ UINT64 address;
+ UINT64 generation;
+ struct _tree* tree;
+// tree_holder_nonpaged* nonpaged;
+} tree_holder;
+
+typedef struct _tree_data {
+ KEY key;
+ LIST_ENTRY list_entry;
+ BOOL ignore;
+ BOOL inserted;
+
+ union {
+ tree_holder treeholder;
+
+ struct {
+ UINT32 size;
+ UINT8* data;
+ };
+ };
+} tree_data;
+
+// typedef struct _tree_nonpaged {
+// ERESOURCE load_tree_lock;
+// } tree_nonpaged;
+
+typedef struct _tree {
+// UINT64 address;
+// UINT8 level;
+ tree_header header;
+ LONG refcount;
+ UINT32 size;
+ struct _device_extension* Vcb;
+ struct _tree* parent;
+ tree_data* paritem;
+ struct _root* root;
+// tree_nonpaged* nonpaged;
+ LIST_ENTRY itemlist;
+ LIST_ENTRY list_entry;
+ UINT64 new_address;
+ UINT64 flags;
+} tree;
+
+typedef struct {
+// KSPIN_LOCK load_tree_lock;
+ ERESOURCE load_tree_lock;
+} root_nonpaged;
+
+typedef struct _root {
+ UINT64 id;
+ tree_holder treeholder;
+ root_nonpaged* nonpaged;
+ UINT64 lastinode;
+ ROOT_ITEM root_item;
+
+ struct _root* prev;
+ struct _root* next;
+} root;
+
+typedef struct {
+ tree* tree;
+ tree_data* item;
+} traverse_ptr;
+
+typedef struct _tree_cache {
+ tree* tree;
+ BOOL write;
+ LIST_ENTRY list_entry;
+} tree_cache;
+
+typedef struct _root_cache {
+ root* root;
+ struct _root_cache* next;
+} root_cache;
+
+#define SPACE_TYPE_FREE 0
+#define SPACE_TYPE_USED 1
+#define SPACE_TYPE_DELETING 2
+#define SPACE_TYPE_WRITING 3
+
+typedef struct {
+ UINT64 offset;
+ UINT64 size;
+ UINT8 type;
+ LIST_ENTRY list_entry;
+} space;
+
+typedef struct {
+ UINT64 address;
+ UINT64 size;
+ BOOL provisional;
+ LIST_ENTRY listentry;
+} disk_hole;
+
+typedef struct {
+ PDEVICE_OBJECT devobj;
+ DEV_ITEM devitem;
+ LIST_ENTRY disk_holes;
+} device;
+
+typedef struct {
+ CHUNK_ITEM* chunk_item;
+ UINT32 size;
+ UINT64 offset;
+ UINT64 used;
+ UINT32 oldused;
+ BOOL space_changed;
+ device** devices;
+ LIST_ENTRY space;
+ LIST_ENTRY list_entry;
+} chunk;
+
+typedef struct {
+ KEY key;
+ void* data;
+ USHORT size;
+ LIST_ENTRY list_entry;
+} sys_chunk;
+
+typedef struct _device_extension {
+ device* devices;
+// DISK_GEOMETRY geometry;
+ UINT64 length;
+ superblock superblock;
+// WCHAR label[MAX_LABEL_SIZE];
+ BOOL readonly;
+ fcb* fcbs;
+ fcb* volume_fcb;
+ fcb* root_fcb;
+ ERESOURCE DirResource;
+ KSPIN_LOCK FcbListLock;
+ ERESOURCE fcb_lock;
+ ERESOURCE load_lock;
+ ERESOURCE tree_lock;
+ PNOTIFY_SYNC NotifySync;
+ LIST_ENTRY DirNotifyList;
+ LONG tree_lock_counter;
+ LONG open_trees;
+ ULONG write_trees;
+// ERESOURCE LogToPhysLock;
+// UINT64 chunk_root_phys_addr;
+ UINT64 root_tree_phys_addr;
+// log_to_phys* log_to_phys;
+ root* roots;
+ root* chunk_root;
+ root* root_root;
+ root* extent_root;
+ root* checksum_root;
+ root* dev_root;
+ BOOL log_to_phys_loaded;
+ UINT32 max_inline;
+ LIST_ENTRY sys_chunks;
+ LIST_ENTRY chunks;
+ LIST_ENTRY trees;
+ LIST_ENTRY tree_cache;
+ HANDLE flush_thread_handle;
+ KTIMER flush_thread_timer;
+} device_extension;
+
+typedef struct {
+ LIST_ENTRY listentry;
+ PSID sid;
+ UINT32 uid;
+} uid_map;
+
+// #pragma pack(pop)
+
+static __inline void init_tree_holder(tree_holder* th) {
+// th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG);
+// KeInitializeSpinLock(&th->nonpaged->spin_lock);
+// ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later
+}
+
+static __inline void* map_user_buffer(PIRP Irp) {
+ if (!Irp->MdlAddress) {
+ return Irp->UserBuffer;
+ } else {
+ return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+ }
+}
+
+static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
+ return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
+}
+
+static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) {
+ ULONGLONG l = t.QuadPart - 116444736000000000;
+
+ out->seconds = l / 10000000;
+ out->nanoseconds = (l % 10000000) * 100;
+}
+
+// in btrfs.c
+device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
+ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
+int keycmp(const KEY* key1, const KEY* key2);
+NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
+void STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen);
+BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32);
+void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize);
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8);
+UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index);
+void delete_fcb(fcb* fcb, PFILE_OBJECT FileObject);
+fcb* create_fcb();
+void protect_superblocks(device_extension* Vcb, chunk* c);
+BOOL is_top_level(PIRP Irp);
+
+#ifdef _MSC_VER
+#define funcname __FUNCTION__
+#else
+#define funcname __func__
+#endif
+
+// FIXME - we probably shouldn't be moving funcname etc. around if we're not printing debug messages
+#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
+
+#ifdef DEBUG_LONG_MESSAGES
+
+#if DEBUG_LEVEL >= 3
+#define TRACE(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define TRACE(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 2
+#define WARN(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define WARN(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 1
+#define FIXME(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#define ERR(s, ...) _debug_message(funcname, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#else
+#define FIXME(s, ...)
+#define ERR(s, ...)
+#endif
+
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...);
+
+#else
+
+#if DEBUG_LEVEL >= 3
+#define TRACE(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define TRACE(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 2
+#define WARN(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define WARN(s, ...)
+#endif
+
+#if DEBUG_LEVEL >= 1
+#define FIXME(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#define ERR(s, ...) _debug_message(funcname, s, ##__VA_ARGS__)
+#else
+#define FIXME(s, ...)
+#define ERR(s, ...)
+#endif
+
+void STDCALL _debug_message(const char* func, char* s, ...);
+
+#endif
+
+// in fastio.c
+void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
+
+// in crc32c.c
+UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
+
+// in treefuncs.c
+BOOL STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _get_item(device_extension* Vcb, root* r, UINT64 objid, UINT8 objtype, UINT64 offset, void* ptr, UINT32 size, const char* func, const char* file, unsigned int line);
+void STDCALL free_tree_cache(LIST_ENTRY* tc);
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp);
+void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp);
+void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write);
+tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
+tree* STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, const char* func, const char* file, unsigned int line);
+BOOL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, const char* func, const char* file, unsigned int line);
+
+#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
+#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
+#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
+#define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
+#define load_tree(t, addr, r) _load_tree(t, addr, r, funcname, __FILE__, __LINE__)
+#define free_traverse_ptr(tp) _free_traverse_ptr(tp, funcname, __FILE__, __LINE__)
+#define get_item(Vcb, r, objid, objtype, offset, ptr, size) _get_item(Vcb, r, objid, objtype, offset, ptr, size, funcname, __FILE__, __LINE__)
+#define do_load_tree(Vcb, th, r, t, td) _do_load_tree(Vcb, th, r, t, td, funcname, __FILE__, __LINE__)
+
+// in search.c
+void STDCALL look_for_vols(LIST_ENTRY* volumes);
+
+// in cache.c
+void STDCALL init_cache();
+void STDCALL free_cache();
+extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
+
+// in write.c
+NTSTATUS STDCALL do_write(device_extension* Vcb);
+NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache);
+NTSTATUS truncate_file(fcb* fcb, UINT64 end);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list);
+void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
+NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length);
+NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset);
+NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list);
+void print_trees(LIST_ENTRY* tc);
+chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
+void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type);
+NTSTATUS consider_write(device_extension* Vcb);
+
+// in dirctrl.c
+NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+
+// in security.c
+NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+void fcb_get_sd(fcb* fcb);
+// UINT32 STDCALL get_uid();
+void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
+NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as);
+
+// in fileinfo.c
+NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8);
+
+// in reparse.c
+BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject);
+NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+
+// in create.c
+NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent);
+BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol,
+ UINT64* inode, UINT8* type, PANSI_STRING utf8);
+
+// in fsctl.c
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
+
+// in flushthread.c
+void STDCALL flush_thread(void* context);
+
+static __inline void print_open_trees(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
+ tree_data* td = CONTAINING_RECORD(t->itemlist.Flink, tree_data, list_entry);
+ ERR("tree %p: root %llx, level %u, refcount %u, first key (%llx,%x,%llx)\n",
+ t, t->root->id, t->header.level, t->refcount, td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ le = le->Flink;
+ }
+}
+
+static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* before) {
+ item->Flink = before->Flink;
+ before->Flink = item;
+ item->Blink = before;
+
+ if (item->Flink != head)
+ item->Flink->Blink = item;
+ else
+ head->Blink = item;
+}
+
+#ifdef _MSC_VER
+// #define int3 __asm { int 3 }
+#define int3 __debugbreak()
+#else
+#define int3 asm("int3;")
+#endif
+
+#define acquire_tree_lock(Vcb, exclusive) {\
+ LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \
+ ref = ref; \
+ if (exclusive) { \
+ TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \
+ TRACE("open tree count = %i\n", Vcb->open_trees); \
+ } else { \
+ TRACE("getting tree_lock %u->%u\n", ref-1, ref); \
+ ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \
+ } \
+}
+
+// if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; }
+// else TRACE("open tree count = %i\n", Vcb->open_trees);
+
+// FIXME - find a way to catch unfreed trees again
+
+#define release_tree_lock(Vcb, exclusive) {\
+ LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \
+ ref = ref; \
+ TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \
+ if (exclusive) {\
+ TRACE("open tree count = %i\n", Vcb->open_trees); \
+ } \
+ ExReleaseResourceLite(&Vcb->tree_lock); \
+}
+
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+#define _increase_tree_rc(t, func, file, line) { \
+ LONG rc = InterlockedIncrement(&t->refcount); \
+ _debug_message(func, file, line, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
+}
+#else
+#define _increase_tree_rc(t, func, file, line) { \
+ LONG rc = InterlockedIncrement(&t->refcount); \
+ _debug_message(func, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
+}
+#endif
+#define increase_tree_rc(t) _increase_tree_rc(t, funcname, __FILE__, __LINE__)
+#else
+#define increase_tree_rc(t) InterlockedIncrement(&t->refcount);
+#define _increase_tree_rc(t, func, file, line) increase_tree_rc(t)
+#endif
+
+// from sys/stat.h
+#define __S_IFMT 0170000 /* These bits determine file type. */
+#define __S_IFDIR 0040000 /* Directory. */
+#define __S_IFCHR 0020000 /* Character device. */
+#define __S_IFBLK 0060000 /* Block device. */
+#define __S_IFREG 0100000 /* Regular file. */
+#define __S_IFIFO 0010000 /* FIFO. */
+#define __S_IFLNK 0120000 /* Symbolic link. */
+#define __S_IFSOCK 0140000 /* Socket. */
+#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 0000100
+#endif
+
+#ifdef __REACTOS__
+#define S_IFDIR __S_IFDIR
+#define S_IFREG __S_IFREG
+#endif /* __REACTOS__ */
+
+#ifndef S_IXGRP
+#define S_IXGRP (S_IXUSR >> 3)
+#endif
+
+#ifndef S_IXOTH
+#define S_IXOTH (S_IXGRP >> 3)
+#endif
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+NTSTATUS WINAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+ ULONG *utf8_bytes_written,
+ const WCHAR *uni_src, ULONG uni_bytes);
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+ ULONG *uni_bytes_written,
+ const CHAR *utf8_src, ULONG utf8_bytes);
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
+
+#endif
Index: reactos/drivers/filesystems/btrfs/cache.c
===================================================================
--- reactos/drivers/filesystems/btrfs/cache.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/cache.c (working copy)
@@ -0,0 +1,62 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+#include
+
+CACHE_MANAGER_CALLBACKS* cache_callbacks;
+
+static BOOLEAN STDCALL acquire_for_lazy_write(PVOID Context, BOOLEAN Wait) {
+ fcb* fcb = Context;
+
+ TRACE("(%p, %u)\n", Context, Wait);
+
+ fcb->lazy_writer_thread = KeGetCurrentThread();
+
+ return TRUE;
+}
+
+static void STDCALL release_from_lazy_write(PVOID Context) {
+ fcb* fcb = Context;
+
+ TRACE("(%p)\n", Context);
+
+ fcb->lazy_writer_thread = NULL;
+}
+
+static BOOLEAN STDCALL acquire_for_read_ahead(PVOID Context, BOOLEAN Wait) {
+ TRACE("(%p, %u)\n", Context, Wait);
+
+ return TRUE;
+}
+
+static void STDCALL release_from_read_ahead(PVOID Context) {
+ TRACE("(%p)\n", Context);
+}
+
+void STDCALL init_cache() {
+ cache_callbacks = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_MANAGER_CALLBACKS), ALLOC_TAG);
+
+ 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;
+}
+
+void STDCALL free_cache() {
+ ExFreePool(cache_callbacks);
+}
Index: reactos/drivers/filesystems/btrfs/CMakeLists.txt
===================================================================
--- reactos/drivers/filesystems/btrfs/CMakeLists.txt (revision 0)
+++ reactos/drivers/filesystems/btrfs/CMakeLists.txt (working copy)
@@ -0,0 +1,40 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/include/reactos/drivers
+ inc)
+
+list(APPEND SOURCE
+ btrfs.c
+ cache.c
+ crc32c.c
+ create.c
+ dirctrl.c
+ fastio.c
+ fileinfo.c
+ flushthread.c
+ fsctl.c
+ reparse.c
+ search.c
+ security.c
+ treefuncs.c
+ write.c
+ btrfs_drv.h)
+
+add_library(btrfs SHARED ${SOURCE} btrfs.rc)
+
+if(NOT MSVC)
+# add_target_compile_flags(btrfs "-Wno-pointer-sign -Wno-unused-function")
+ replace_compile_flags("-Werror" " ")
+# if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+# add_target_compile_flags(btrfs "-Wno-unused-but-set-variable -Wno-unused-variable -Wno-missing-braces")
+# endif()
+else()
+ #disable warnings: "unreferenced local variable", "initialized, but not used variable", "benign include"
+ replace_compile_flags("/we\"4189\"" " ")
+# add_target_compile_flags(btrfs "/wd\"4189\" /wd\"4142\" /wd\"4101\"")
+endif()
+
+add_definitions(-D__KERNEL__)
+set_module_type(btrfs kernelmodedriver)
+add_importlibs(btrfs ntoskrnl hal)
+add_pch(btrfs btrfs_drv.h SOURCE)
+add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)
Index: reactos/drivers/filesystems/btrfs/crc32c.c
===================================================================
--- reactos/drivers/filesystems/btrfs/crc32c.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/crc32c.c (working copy)
@@ -0,0 +1,105 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include
+#ifndef __REACTOS__
+#include
+
+extern BOOL have_sse42;
+#endif
+static const UINT32 crctable[] = {
+ 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
+ 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
+ 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
+ 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
+ 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
+ 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
+ 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
+ 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
+ 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
+ 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
+ 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
+ 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
+ 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
+ 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
+ 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
+ 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
+ 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
+ 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
+ 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
+ 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
+ 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
+ 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
+ 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
+ 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
+ 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
+ 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
+ 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
+ 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
+ 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
+ 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
+ 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
+ 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
+};
+
+// HW code taken from https://github.com/rurban/smhasher/blob/master/crc32_hw.c
+#ifndef __REACTOS__
+#define ALIGN_SIZE 0x08UL
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+#define CALC_CRC(op, crc, type, buf, len) \
+ do { \
+ for (; (len) >= sizeof (type); (len) -= sizeof(type), buf += sizeof (type)) { \
+ (crc) = op((crc), *(type *) (buf)); \
+ } \
+ } while(0)
+
+static UINT32 crc32c_hw(const void *input, int len, UINT32 crc) {
+ const char* buf = (const char*)input;
+
+ for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) {
+ crc = _mm_crc32_u8(crc, *buf);
+ }
+
+#ifdef __x86_64__
+ CALC_CRC(_mm_crc32_u64, crc, UINT64, buf, len);
+#endif
+ CALC_CRC(_mm_crc32_u32, crc, UINT32, buf, len);
+ CALC_CRC(_mm_crc32_u16, crc, UINT16, buf, len);
+ CALC_CRC(_mm_crc32_u8, crc, UINT8, buf, len);
+
+ return crc;
+}
+#endif
+UINT32 __stdcall calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen) {
+ UINT32 rem;
+ ULONG i;
+
+#ifndef __REACTOS__
+ if (have_sse42) {
+ return crc32c_hw(msg, msglen, seed);
+ } else {
+#endif
+ rem = seed;
+
+ for (i = 0; i < msglen; i++) {
+ rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
+ }
+#ifndef __REACTOS__
+ }
+#endif
+ return rem;
+}
Index: reactos/drivers/filesystems/btrfs/create.c
===================================================================
--- reactos/drivers/filesystems/btrfs/create.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/create.c (working copy)
@@ -0,0 +1,2007 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#ifndef __REACTOS__
+#include
+#endif
+#include "btrfs_drv.h"
+
+extern PDEVICE_OBJECT devobj;
+
+BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r,
+ UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
+ DIR_ITEM* di;
+ KEY searchkey;
+ traverse_ptr tp, tp2, next_tp;
+ BOOL b;
+ NTSTATUS Status;
+ ULONG stringlen;
+
+ TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ return FALSE;
+ }
+
+ TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ UINT32 size = tp.item->size;
+
+ // found by hash
+
+ di = (DIR_ITEM*)tp.item->data;
+
+ while (size > 0) {
+ size -= sizeof(DIR_ITEM) - sizeof(char);
+ size -= di->n;
+ size -= di->m;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+ UNICODE_STRING us;
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)stringlen;
+
+ if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
+ if (di->key.obj_type == TYPE_ROOT_ITEM) {
+ root* fcbroot = Vcb->roots;
+ while (fcbroot && fcbroot->id != di->key.obj_id)
+ fcbroot = fcbroot->next;
+
+ *subvol = fcbroot;
+ *inode = SUBVOL_ROOT_INODE;
+ *type = BTRFS_TYPE_DIRECTORY;
+ } else {
+ *subvol = r;
+ *inode = di->key.obj_id;
+ *type = di->type;
+ }
+
+ if (utf8) {
+ utf8->MaximumLength = di->n;
+ utf8->Length = utf8->MaximumLength;
+ utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(utf8->Buffer, di->name, di->n);
+ }
+
+ free_traverse_ptr(&tp);
+ ExFreePool(utf16);
+
+ TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+
+ return TRUE;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ di = (DIR_ITEM*)&di->name[di->n + di->m];
+ }
+ }
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 2;
+
+ if (!find_item(Vcb, r, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", r->id);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = tp2;
+
+ TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (keycmp(&tp.item->key, &searchkey) == -1) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ b = TRUE;
+ do {
+ TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ di = (DIR_ITEM*)tp.item->data;
+
+ TRACE("%.*s\n", di->n, di->name);
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+ UNICODE_STRING us;
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)stringlen;
+
+ if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
+ if (di->key.obj_type == TYPE_ROOT_ITEM) {
+ root* fcbroot = Vcb->roots;
+ while (fcbroot && fcbroot->id != di->key.obj_id)
+ fcbroot = fcbroot->next;
+
+ *subvol = fcbroot;
+ *inode = SUBVOL_ROOT_INODE;
+ *type = BTRFS_TYPE_DIRECTORY;
+ } else {
+ *subvol = r;
+ *inode = di->key.obj_id;
+ *type = di->type;
+ }
+ TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+
+ if (utf8) {
+ utf8->MaximumLength = di->n;
+ utf8->Length = utf8->MaximumLength;
+ utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(utf8->Buffer, di->name, di->n);
+ }
+
+ free_traverse_ptr(&tp);
+ ExFreePool(utf16);
+
+ return TRUE;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return FALSE;
+}
+
+fcb* create_fcb() {
+ fcb* fcb;
+
+ fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("allocating fcb %p\n", fcb);
+#endif
+ RtlZeroMemory(fcb, sizeof(struct _fcb));
+
+ fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
+ fcb->Header.NodeByteSize = sizeof(struct _fcb);
+
+ fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
+ RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
+
+ ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
+ FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
+
+ fcb->refcount = 1;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
+#endif
+
+ ExInitializeResourceLite(&fcb->nonpaged->resource);
+ fcb->Header.Resource = &fcb->nonpaged->resource;
+
+ FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
+
+ InitializeListHead(&fcb->children);
+
+ return fcb;
+}
+
+static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
+ UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
+ char* fn;
+ UINT32 crc32;
+ BOOL ret;
+ ULONG utf8len;
+ NTSTATUS Status;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
+ if (!NT_SUCCESS(Status)) {
+ ExFreePool(fn);
+ ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ TRACE("%.*s\n", utf8len, fn);
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
+ TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
+
+ ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8);
+
+ return ret;
+}
+
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, UINT32* hash, PANSI_STRING xattr) {
+ NTSTATUS Status;
+ ULONG utf8len;
+ char* utf8;
+ UINT32 crc32;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL success = FALSE, b;
+
+ static char xapref[] = "user.";
+ ULONG xapreflen = strlen(xapref);
+
+ TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ TRACE("utf8len = %u\n", utf8len);
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
+ RtlCopyMemory(utf8, xapref, xapreflen);
+
+ Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+ goto end;
+ }
+
+ utf8len += xapreflen;
+ utf8[utf8len] = 0;
+
+ TRACE("utf8 = %s\n", utf8);
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
+ TRACE("crc32 = %08x\n", crc32);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto end;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ ULONG len = tp.item->size, xasize;
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+
+ TRACE("found match on hash\n");
+
+ while (len > 0) {
+ if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
+ TRACE("found exact match for %s\n", utf8);
+
+ *size = di->m;
+ *hash = tp.item->key.offset;
+
+ xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
+ xattr->Length = xattr->MaximumLength = di->n;
+ RtlCopyMemory(xattr->Buffer, di->name, di->n);
+ xattr->Buffer[di->n] = 0;
+
+ free_traverse_ptr(&tp);
+
+ success = TRUE;
+ goto end;
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
+
+ if (len > xasize) {
+ len -= xasize;
+ di = (DIR_ITEM*)&di->name[di->m + di->n];
+ } else
+ break;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.offset = 0;
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
+ ULONG len = tp.item->size, xasize;
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ ULONG utf16len;
+
+ TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
+
+ while (len > 0) {
+ if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
+ TRACE("found potential xattr %.*s\n", di->n, di->name);
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ } else {
+ UNICODE_STRING us;
+
+ us.Buffer = utf16;
+ us.Length = us.MaximumLength = (USHORT)utf16len;
+
+ if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
+ TRACE("found case-insensitive match for %s\n", utf8);
+
+ *newstreamname = us;
+ *size = di->m;
+ *hash = tp.item->key.offset;
+
+ xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
+ xattr->Length = xattr->MaximumLength = di->n;
+ RtlCopyMemory(xattr->Buffer, di->name, di->n);
+ xattr->Buffer[di->n] = 0;
+
+ free_traverse_ptr(&tp);
+
+ success = TRUE;
+ goto end;
+ }
+ }
+
+ ExFreePool(utf16);
+ }
+
+ xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
+
+ if (len > xasize) {
+ len -= xasize;
+ di = (DIR_ITEM*)&di->name[di->m + di->n];
+ } else
+ break;
+ }
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+end:
+ ExFreePool(utf8);
+
+ return success;
+}
+
+static void split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
+ ULONG len, i, j, np;
+ BOOL has_stream;
+ UNICODE_STRING* ps;
+ WCHAR* buf;
+
+ np = 1;
+
+ len = path->Length / sizeof(WCHAR);
+ if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
+ len--;
+
+ has_stream = FALSE;
+ for (i = 0; i < len; i++) {
+ if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+ np++;
+ has_stream = FALSE;
+ } else if (path->Buffer[i] == ':') {
+ has_stream = TRUE;
+ }
+ }
+
+ if (has_stream)
+ np++;
+
+ ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
+ RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
+
+ buf = path->Buffer;
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+ ps[j].Buffer = buf;
+ ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ buf = &path->Buffer[i+1];
+ j++;
+ }
+ }
+
+ ps[j].Buffer = buf;
+ ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ if (has_stream) {
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ UNICODE_STRING dsus;
+
+ dsus.Buffer = datasuf;
+ dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+
+ for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
+ if (ps[j].Buffer[i] == ':') {
+ ps[j+1].Buffer = &ps[j].Buffer[i+1];
+ ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
+
+ ps[j].Length = i * sizeof(WCHAR);
+ ps[j].MaximumLength = ps[j].Length;
+
+ j++;
+
+ break;
+ }
+ }
+
+ // FIXME - should comparison be case-insensitive?
+ // remove :$DATA suffix
+ if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
+ ps[j].Length -= dsus.Length;
+
+ if (ps[j].Length == 0) {
+ np--;
+ has_stream = FALSE;
+ }
+ }
+
+ // if path is just stream name, remove first empty item
+ if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
+ ps[0] = ps[1];
+ np--;
+ }
+
+// for (i = 0; i < np; i++) {
+// ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
+// }
+
+ *num_parts = np;
+ *parts = ps;
+ *stream = has_stream;
+}
+
+static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
+ LIST_ENTRY* le;
+ fcb *c, *deleted = NULL;
+
+ le = dir->children.Flink;
+ while (le != &dir->children) {
+ c = CONTAINING_RECORD(le, fcb, list_entry);
+
+ if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
+ if (c->deleted) {
+ deleted = c;
+ } else {
+ c->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+#endif
+ return c;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return deleted;
+}
+
+#ifdef DEBUG_FCB_REFCOUNTS
+static void print_fcbs(device_extension* Vcb) {
+ fcb* fcb = Vcb->fcbs;
+
+ while (fcb) {
+ ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
+
+ fcb = fcb->next;
+ }
+}
+#endif
+
+NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
+ fcb *dir, *sf, *sf2;
+ ULONG i, num_parts;
+ UNICODE_STRING fnus2;
+ UNICODE_STRING* parts = NULL;
+ BOOL has_stream;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ print_fcbs(Vcb);
+#endif
+
+ fnus2 = *fnus;
+
+ if (fnus2.Length < sizeof(WCHAR) && !relatedfcb) {
+ ERR("error - fnus was too short\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (relatedfcb) {
+ dir = relatedfcb;
+ } else {
+ if (fnus2.Buffer[0] != '\\') {
+ ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (fnus2.Length == sizeof(WCHAR)) {
+ *pfcb = Vcb->root_fcb;
+ Vcb->root_fcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, Vcb->root_fcb->refcount);
+#endif
+ return STATUS_SUCCESS;
+ }
+
+ dir = Vcb->root_fcb;
+
+ fnus2.Buffer++;
+ fnus2.Length -= sizeof(WCHAR);
+ fnus2.MaximumLength -= sizeof(WCHAR);
+ }
+
+ if (dir->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
+ WARN("passed relatedfcb which isn't a directory (%.*S) (fnus = %.*S)\n",
+ relatedfcb->full_filename.Length / sizeof(WCHAR), relatedfcb->full_filename.Buffer, fnus->Length / sizeof(WCHAR), fnus->Buffer);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ if (fnus->Length == 0) {
+ num_parts = 0;
+ } else {
+ split_path(&fnus2, &parts, &num_parts, &has_stream);
+ }
+
+ // FIXME - handle refcounts(?)
+ sf = dir;
+ dir->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", dir, dir->refcount, dir->full_filename.Length / sizeof(WCHAR), dir->full_filename.Buffer);
+#endif
+
+ if (parent) {
+ num_parts--;
+
+ if (has_stream) {
+ num_parts--;
+ has_stream = FALSE;
+ }
+ }
+
+ if (num_parts == 0) {
+ Status = STATUS_SUCCESS;
+ *pfcb = dir;
+ goto end2;
+ }
+
+ for (i = 0; i < num_parts; i++) {
+ BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
+
+ sf2 = search_fcb_children(sf, &parts[i]);
+
+ if (sf2 && sf2->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
+ WARN("passed path including file as subdirectory\n");
+
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ }
+
+ if (!sf2) {
+ if (has_stream && i == num_parts - 1) {
+ UNICODE_STRING streamname;
+ ANSI_STRING xattr;
+ UINT32 streamsize, streamhash;
+
+ streamname.Buffer = NULL;
+ streamname.Length = streamname.MaximumLength = 0;
+ xattr.Buffer = NULL;
+ xattr.Length = xattr.MaximumLength = 0;
+
+ if (!find_stream(Vcb, sf, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
+ WARN("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ } else {
+ ULONG fnlen;
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ if (streamname.Buffer) // case has changed
+ sf2->filepart = streamname;
+ else {
+ sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
+ }
+
+ sf2->par = sf;
+
+ sf->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
+#endif
+
+ sf2->subvol = sf->subvol;
+ sf2->inode = sf->inode;
+ sf2->type = sf->type;
+ sf2->ads = TRUE;
+ sf2->adssize = streamsize;
+ sf2->adshash = streamhash;
+ sf2->adsxattr = xattr;
+
+ TRACE("stream found: size = %x, hash = %08x\n", sf2->adssize, sf2->adshash);
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
+
+ if (sf != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
+
+ sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
+
+ sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':';
+
+ RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
+
+ // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+
+ TRACE("found stream %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, sf->subvol);
+
+ InsertTailList(&sf->children, &sf2->list_entry);
+ }
+ } else {
+ root* subvol;
+ UINT64 inode;
+ UINT8 type;
+ ANSI_STRING utf8;
+
+ if (!find_file_in_dir(Vcb, &parts[i], sf->subvol, sf->inode, &subvol, &inode, &type, &utf8)) {
+ WARN("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
+
+ Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ } else if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
+ WARN("passed path including file as subdirectory\n");
+
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ } else {
+ ULONG fnlen, strlen;
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ } else {
+ sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ }
+ }
+
+ sf2->par = sf;
+
+ sf->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
+#endif
+
+ sf2->subvol = subvol;
+ sf2->inode = inode;
+ sf2->type = type;
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
+
+ if (sf != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
+
+ sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
+
+ if (sf != Vcb->root_fcb)
+ sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
+
+ sf2->utf8 = utf8;
+
+ if (!get_item(sf2->Vcb, sf2->subvol, sf2->inode, TYPE_INODE_ITEM, 0, &sf2->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find (%llx,%x,0) in subvol %llx\n", sf2->inode, TYPE_INODE_ITEM, sf2->subvol->id);
+ }
+
+ sf2->atts = get_file_attributes(Vcb, &sf2->inode_item, sf2->subvol, sf2->inode, sf2->type, sf2->filepart.Buffer[0] == '.', FALSE);
+
+ fcb_get_sd(sf2);
+
+ TRACE("found %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, subvol);
+
+ InsertTailList(&sf->children, &sf2->list_entry);
+ }
+ }
+ }
+
+ if (i == num_parts - 1)
+ break;
+
+ free_fcb(sf);
+ sf = sf2;
+ }
+
+ Status = STATUS_SUCCESS;
+ *pfcb = sf2;
+
+end:
+ free_fcb(sf);
+
+end2:
+ if (parts)
+ ExFreePool(parts);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ print_fcbs(Vcb);
+#endif
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL attach_fcb_to_fileobject(device_extension* Vcb, fcb* fcb, PFILE_OBJECT FileObject) {
+ FileObject->FsContext = fcb;
+// FileObject->FsContext2 = 0x0badc0de;//NULL;
+
+ // FIXME - cache stuff
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, fcb* parfcb, ULONG options, fcb** pfcb) {
+ NTSTATUS Status;
+ fcb* fcb;
+ ULONG utf8len;
+ char* utf8 = NULL;
+ UINT32 crc32;
+ UINT64 dirpos, inode;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM *dirii, *ii;
+ UINT8 type;
+ ULONG disize;
+ DIR_ITEM *di, *di2;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ANSI_STRING utf8as;
+ ULONG defda;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
+ if (!NT_SUCCESS(Status)) {
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ utf8[utf8len] = 0;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
+
+ dirpos = find_next_dir_index(Vcb, parfcb->subvol, parfcb->inode);
+ if (dirpos == 0) {
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfcb->inode_item.st_size += utf8len * 2;
+ TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfcb->inode_item.transid = Vcb->superblock.generation;
+ parfcb->inode_item.sequence++;
+ parfcb->inode_item.st_ctime = now;
+ parfcb->inode_item.st_mtime = now;
+
+ searchkey.obj_id = parfcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parfcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfcb->inode, parfcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ free_traverse_ptr(&tp);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(dirii, &parfcb->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(Vcb, &tp);
+
+ insert_tree_item(Vcb, parfcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ if (parfcb->subvol->lastinode == 0)
+ get_last_inode(Vcb, parfcb->subvol);
+
+ inode = parfcb->subvol->lastinode + 1;
+
+ type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
+
+ disize = sizeof(DIR_ITEM) - 1 + utf8len;
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = (UINT16)utf8len;
+ di->type = type;
+ RtlCopyMemory(di->name, utf8, utf8len);
+
+ insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL);
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(Vcb, parfcb->subvol, parfcb->inode, crc32, di2, disize);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ // FIXME - handle Irp->Overlay.AllocationSize
+
+ utf8as.Buffer = utf8;
+ utf8as.Length = utf8as.MaximumLength = utf8len;
+
+ Status = add_inode_ref(Vcb, parfcb->subvol, inode, parfcb->inode, dirpos, &utf8as);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
+
+ TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
+
+ IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
+
+ defda = 0;
+
+ if (utf8[0] == '.')
+ defda |= FILE_ATTRIBUTE_HIDDEN;
+
+ if (options & FILE_DIRECTORY_FILE) {
+ defda |= FILE_ATTRIBUTE_DIRECTORY;
+ IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ TRACE("defda = %x\n", defda);
+
+ if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
+ IrpSp->Parameters.Create.FileAttributes = defda;
+
+ if (IrpSp->Parameters.Create.FileAttributes != defda) {
+ char val[64];
+
+ sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
+
+ set_xattr(Vcb, parfcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ }
+
+ parfcb->subvol->lastinode++;
+
+ fcb = create_fcb();
+ fcb->Vcb = Vcb;
+
+ RtlZeroMemory(&fcb->inode_item, sizeof(INODE_ITEM));
+ fcb->inode_item.generation = Vcb->superblock.generation;
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_size = 0;
+ fcb->inode_item.st_blocks = 0;
+ fcb->inode_item.block_group = 0;
+ fcb->inode_item.st_nlink = 1;
+// fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
+ fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
+ fcb->inode_item.st_mode = parfcb ? (parfcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
+ fcb->inode_item.st_rdev = 0;
+ fcb->inode_item.flags = 0;
+ fcb->inode_item.sequence = 1;
+ fcb->inode_item.st_atime = now;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+ fcb->inode_item.otime = now;
+
+ if (type == BTRFS_TYPE_DIRECTORY)
+ fcb->inode_item.st_mode |= S_IFDIR;
+ else {
+ fcb->inode_item.st_mode |= S_IFREG;
+ fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
+ }
+
+ // inherit nodatacow flag from parent directory
+ if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+ fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+
+ if (type != BTRFS_TYPE_DIRECTORY)
+ fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+ }
+
+// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+
+ fcb->atts = IrpSp->Parameters.Create.FileAttributes;
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ fcb->par = parfcb;
+ parfcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+#endif
+ fcb->subvol = parfcb->subvol;
+ fcb->inode = inode;
+ fcb->type = type;
+
+ fcb->utf8.MaximumLength = fcb->utf8.Length = utf8len;
+ fcb->utf8.Buffer = utf8;
+
+ Status = fcb_get_new_sd(fcb, IrpSp->Parameters.Create.SecurityContext->AccessState);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("fcb_get_new_sd returned %08x\n", Status);
+ ExFreePool(utf8);
+ return Status;
+ }
+
+ fcb->filepart = *fpus;
+
+ set_xattr(Vcb, parfcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd));
+
+ fcb->full_filename.Length = parfcb->full_filename.Length + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fcb->filepart.Length;
+ fcb->full_filename.MaximumLength = fcb->full_filename.Length;
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.Length, ALLOC_TAG);
+ RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
+
+ if (parfcb->full_filename.Length > sizeof(WCHAR))
+ fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[(parfcb->full_filename.Length / sizeof(WCHAR)) + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], fcb->filepart.Buffer, fcb->filepart.Length);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, parfcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ *pfcb = fcb;
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+ TRACE("created new file %.*S in subvol %llx, inode %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->subvol->id, fcb->inode);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options) {
+ NTSTATUS Status;
+ fcb *fcb, *parfcb = NULL;
+ ULONG i, j;
+ ULONG utf8len;
+ ccb* ccb;
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ UNICODE_STRING dsus, fpus, stream;
+
+ TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ dsus.Buffer = datasuf;
+ dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+ fpus.Buffer = NULL;
+
+ // FIXME - apparently you can open streams using RelatedFileObject. How can we test this?
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &parfcb, fnus, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, TRUE);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ }
+
+ if (parfcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ i = (fnus->Length / sizeof(WCHAR))-1;
+ while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
+
+ j = i;
+
+ while (fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/' && i > 0) { i--; }
+
+ fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
+ fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
+ fpus.Length = (j - i + 1) * sizeof(WCHAR);
+
+ RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
+ fpus.Buffer[j - i + 1] = 0;
+
+ if (fpus.Length > dsus.Length) { // check for :$DATA suffix
+ UNICODE_STRING lb;
+
+ lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
+ lb.Length = lb.MaximumLength = dsus.Length;
+
+ TRACE("lb = %.*S\n", lb.Length/sizeof(WCHAR), lb.Buffer);
+
+ if (FsRtlAreNamesEqual(&dsus, &lb, TRUE, NULL)) {
+ TRACE("ignoring :$DATA suffix\n");
+
+ fpus.Length -= lb.Length;
+
+ if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
+ fpus.Length -= sizeof(WCHAR);
+
+ TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
+ }
+ }
+
+ stream.Length = 0;
+
+ for (i = 0; i < fpus.Length/sizeof(WCHAR); i++) {
+ if (fpus.Buffer[i] == ':') {
+ stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
+ stream.Buffer = &fpus.Buffer[i+1];
+ fpus.Buffer[i] = 0;
+ fpus.Length = i * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if (stream.Length > 0) {
+ struct _fcb* newpar;
+ static char xapref[] = "user.";
+ ULONG xapreflen = strlen(xapref), fnlen;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
+ TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &newpar, &fpus, parfcb, FALSE);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &newpar);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("file_create2 returned %08x\n", Status);
+ goto end;
+ }
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("get_fcb returned %08x\n", Status);
+ goto end;
+ }
+
+ free_fcb(parfcb);
+ parfcb = newpar;
+
+ if (newpar->type != BTRFS_TYPE_FILE && newpar->type != BTRFS_TYPE_SYMLINK) {
+ WARN("parent not file or symlink\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (options & FILE_DIRECTORY_FILE) {
+ WARN("tried to create directory as stream\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ fcb = create_fcb();
+ fcb->Vcb = Vcb;
+
+// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ fcb->par = parfcb;
+ parfcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+#endif
+ fcb->subvol = parfcb->subvol;
+ fcb->inode = parfcb->inode;
+ fcb->type = parfcb->type;
+
+ fcb->ads = TRUE;
+ fcb->adssize = 0;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ fcb->adsxattr.Length = utf8len + xapreflen;
+ fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
+ fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+
+ Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
+ if (!NT_SUCCESS(Status)) {
+ free_fcb(fcb);
+ goto end;
+ }
+
+ fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
+
+ TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
+
+ fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+ TRACE("adshash = %08x\n", fcb->adshash);
+
+ fcb->name_offset = parfcb->full_filename.Length / sizeof(WCHAR);
+ if (parfcb != Vcb->root_fcb)
+ fcb->name_offset++;
+
+ fcb->filepart.MaximumLength = fcb->filepart.Length = stream.Length;
+ fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->filepart.Buffer, stream.Buffer, stream.Length);
+
+ fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
+
+ fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = ':';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
+ TRACE("full_filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+ set_xattr(Vcb, parfcb->subvol, parfcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ parfcb->inode_item.transid = Vcb->superblock.generation;
+ parfcb->inode_item.sequence++;
+ parfcb->inode_item.st_ctime = now;
+
+ searchkey.obj_id = parfcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parfcb->subvol->id);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfcb->subvol->id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &parfcb->inode_item, sizeof(INODE_ITEM));
+
+ insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ parfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ parfcb->subvol->root_item.ctime = now;
+
+ ExFreePool(fpus.Buffer);
+ fpus.Buffer = NULL;
+ } else {
+ Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &fcb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("file_create2 returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = fcb;
+
+ fcb->next = Vcb->fcbs;
+ Vcb->fcbs = fcb;
+
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
+
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ RtlZeroMemory(ccb, sizeof(*ccb));
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(ccb);
+ ccb->disposition = disposition;
+ ccb->options = options;
+ ccb->query_dir_offset = 0;
+ RtlInitUnicodeString(&ccb->query_string, NULL);
+ ccb->has_wildcard = FALSE;
+ ccb->specific_file = FALSE;
+
+ fcb->open_count++;
+
+ FileObject->FsContext2 = ccb;
+
+ FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+
+ TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
+
+ Status = consider_write(Vcb);
+
+ if (NT_SUCCESS(Status)) {
+ ULONG fnlen;
+
+ fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
+
+ if (fcb->par != Vcb->root_fcb)
+ fcb->name_offset++;
+
+ fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+
+ if (fcb->par != Vcb->root_fcb)
+ fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
+
+ RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
+
+ FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ FILE_ACTION_ADDED, NULL);
+
+ goto end2;
+ }
+
+end:
+ if (fpus.Buffer)
+ ExFreePool(fpus.Buffer);
+
+end2:
+ if (parfcb)
+ free_fcb(parfcb);
+
+ return Status;
+}
+
+static __inline void debug_create_options(ULONG RequestedOptions) {
+ if (RequestedOptions != 0) {
+ ULONG options = RequestedOptions;
+
+ TRACE("requested options:\n");
+
+ if (options & FILE_DIRECTORY_FILE) {
+ TRACE(" FILE_DIRECTORY_FILE\n");
+ options &= ~FILE_DIRECTORY_FILE;
+ }
+
+ if (options & FILE_WRITE_THROUGH) {
+ TRACE(" FILE_WRITE_THROUGH\n");
+ options &= ~FILE_WRITE_THROUGH;
+ }
+
+ if (options & FILE_SEQUENTIAL_ONLY) {
+ TRACE(" FILE_SEQUENTIAL_ONLY\n");
+ options &= ~FILE_SEQUENTIAL_ONLY;
+ }
+
+ if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
+ TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
+ options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
+ }
+
+ if (options & FILE_SYNCHRONOUS_IO_ALERT) {
+ TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
+ options &= ~FILE_SYNCHRONOUS_IO_ALERT;
+ }
+
+ if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
+ TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
+ options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
+ }
+
+ if (options & FILE_NON_DIRECTORY_FILE) {
+ TRACE(" FILE_NON_DIRECTORY_FILE\n");
+ options &= ~FILE_NON_DIRECTORY_FILE;
+ }
+
+ if (options & FILE_CREATE_TREE_CONNECTION) {
+ TRACE(" FILE_CREATE_TREE_CONNECTION\n");
+ options &= ~FILE_CREATE_TREE_CONNECTION;
+ }
+
+ if (options & FILE_COMPLETE_IF_OPLOCKED) {
+ TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
+ options &= ~FILE_COMPLETE_IF_OPLOCKED;
+ }
+
+ if (options & FILE_NO_EA_KNOWLEDGE) {
+ TRACE(" FILE_NO_EA_KNOWLEDGE\n");
+ options &= ~FILE_NO_EA_KNOWLEDGE;
+ }
+
+ if (options & FILE_OPEN_REMOTE_INSTANCE) {
+ TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
+ options &= ~FILE_OPEN_REMOTE_INSTANCE;
+ }
+
+ if (options & FILE_RANDOM_ACCESS) {
+ TRACE(" FILE_RANDOM_ACCESS\n");
+ options &= ~FILE_RANDOM_ACCESS;
+ }
+
+ if (options & FILE_DELETE_ON_CLOSE) {
+ TRACE(" FILE_DELETE_ON_CLOSE\n");
+ options &= ~FILE_DELETE_ON_CLOSE;
+ }
+
+ if (options & FILE_OPEN_BY_FILE_ID) {
+ TRACE(" FILE_OPEN_BY_FILE_ID\n");
+ options &= ~FILE_OPEN_BY_FILE_ID;
+ }
+
+ if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
+ TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
+ options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ if (options & FILE_NO_COMPRESSION) {
+ TRACE(" FILE_NO_COMPRESSION\n");
+ options &= ~FILE_NO_COMPRESSION;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WIN7)
+ if (options & FILE_OPEN_REQUIRING_OPLOCK) {
+ TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
+ options &= ~FILE_OPEN_REQUIRING_OPLOCK;
+ }
+
+ if (options & FILE_DISALLOW_EXCLUSIVE) {
+ TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
+ options &= ~FILE_DISALLOW_EXCLUSIVE;
+ }
+#endif
+ if (options & FILE_RESERVE_OPFILTER) {
+ TRACE(" FILE_RESERVE_OPFILTER\n");
+ options &= ~FILE_RESERVE_OPFILTER;
+ }
+
+ if (options & FILE_OPEN_REPARSE_POINT) {
+ TRACE(" FILE_OPEN_REPARSE_POINT\n");
+ options &= ~FILE_OPEN_REPARSE_POINT;
+ }
+
+ if (options & FILE_OPEN_NO_RECALL) {
+ TRACE(" FILE_OPEN_NO_RECALL\n");
+ options &= ~FILE_OPEN_NO_RECALL;
+ }
+
+ if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
+ TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
+ options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
+ }
+
+ if (options)
+ TRACE(" unknown options: %x\n", options);
+ } else {
+ TRACE("requested options: (none)\n");
+ }
+}
+
+static NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* newii;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find %llx,%x,%llx in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, subvol->id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
+
+ insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, 0, newii, sizeof(INODE_ITEM), NULL);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION Stack;
+ PFILE_OBJECT FileObject;
+ ULONG RequestedDisposition;
+ ULONG options;
+ NTSTATUS Status;
+ fcb* fcb;
+ ccb* ccb;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
+ options = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+ if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
+ WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ FileObject = Stack->FileObject;
+
+ debug_create_options(options);
+
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ TRACE("requested disposition: FILE_SUPERSEDE\n");
+ break;
+
+ case FILE_CREATE:
+ TRACE("requested disposition: FILE_CREATE\n");
+ break;
+
+ case FILE_OPEN:
+ TRACE("requested disposition: FILE_OPEN\n");
+ break;
+
+ case FILE_OPEN_IF:
+ TRACE("requested disposition: FILE_OPEN_IF\n");
+ break;
+
+ case FILE_OVERWRITE:
+ TRACE("requested disposition: FILE_OVERWRITE\n");
+ break;
+
+ case FILE_OVERWRITE_IF:
+ TRACE("requested disposition: FILE_OVERWRITE_IF\n");
+ break;
+
+ default:
+ ERR("unknown disposition: %x\n", RequestedDisposition);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+ TRACE("FileObject = %p\n", FileObject);
+
+ if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto exit;
+ }
+
+ // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = get_fcb(Vcb, &fcb, &FileObject->FileName, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, Stack->Flags & SL_OPEN_TARGET_DIRECTORY);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ if (NT_SUCCESS(Status) && fcb->deleted) {
+ free_fcb(fcb);
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (NT_SUCCESS(Status)) {
+ if (RequestedDisposition == FILE_CREATE) {
+ WARN("file %.*S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ free_fcb(fcb);
+ goto exit;
+ }
+ } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
+ WARN("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
+ goto exit;
+ }
+ } else {
+ TRACE("get_fcb returned %08x\n", Status);
+ goto exit;
+ }
+
+ if (NT_SUCCESS(Status)) { // file already exists
+ struct _fcb* sf;
+
+ if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
+ RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
+ Status = STATUS_ACCESS_DENIED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ TRACE("deleted = %s\n", fcb->deleted ? "TRUE" : "FALSE");
+
+// if (!fcb->ads && !get_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, &fcb->inode_item, sizeof(INODE_ITEM), NULL)) { // FIXME - don't do this if already loaded
+// ERR("couldn't find (%llx,%x,0) in subvol %llx\n", fcb->inode, TYPE_INODE_ITEM, fcb->subvol->id);
+// Status = STATUS_INTERNAL_ERROR;
+// free_fcb(fcb);
+// goto exit;
+// }
+
+ sf = fcb;
+ while (sf) {
+ if (sf->delete_on_close) {
+ WARN("could not open as deletion pending\n");
+ Status = STATUS_DELETE_PENDING;
+ free_fcb(fcb);
+ goto exit;
+ }
+ sf = sf->par;
+ }
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK && !(options & FILE_OPEN_REPARSE_POINT)) {
+ if (!follow_symlink(fcb, FileObject)) {
+ ERR("follow_symlink failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ Status = STATUS_REPARSE;
+ Irp->IoStatus.Information = IO_REPARSE;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ // FIXME - supersede on FILE_SUPERSEDE
+
+ if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
+ ULONG defda;
+ LIST_ENTRY changed_sector_list;
+
+ if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fcb->atts & FILE_ATTRIBUTE_READONLY) {
+ WARN("cannot overwrite readonly file\n");
+ Status = STATUS_ACCESS_DENIED;
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ // FIXME - where did we get this from?
+// if (fcb->refcount > 1) {
+// WARN("cannot overwrite open file (fcb = %p, refcount = %u)\n", fcb, fcb->refcount);
+// Status = STATUS_ACCESS_DENIED;
+// free_fcb(fcb);
+// goto exit;
+// }
+ InitializeListHead(&changed_sector_list);
+
+ // FIXME - make sure not ADS!
+ Status = truncate_file(fcb, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ Status = update_inode_item(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_inode_item returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+
+ if (RequestedDisposition == FILE_SUPERSEDE)
+ fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ else
+ fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+
+ if (Stack->Parameters.Create.FileAttributes != defda) {
+ char val[64];
+
+ sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ } else
+ delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH);
+
+ // FIXME - truncate streams
+ // FIXME - do we need to alter parent directory's times?
+ // FIXME - send notifications
+
+ Status = consider_write(fcb->Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("consider_write returned %08x\n", Status);
+ free_fcb(fcb);
+ goto exit;
+ }
+ }
+
+// fcb->Header.IsFastIoPossible = TRUE;
+
+ if (fcb->ads) {
+ fcb->Header.AllocationSize.QuadPart = fcb->adssize;
+ fcb->Header.FileSize.QuadPart = fcb->adssize;
+ fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
+ } else {
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ }
+
+ if (options & FILE_NON_DIRECTORY_FILE && fcb->type == BTRFS_TYPE_DIRECTORY) {
+ free_fcb(fcb);
+ Status = STATUS_FILE_IS_A_DIRECTORY;
+ goto exit;
+ } else if (options & FILE_DIRECTORY_FILE && fcb->type != BTRFS_TYPE_DIRECTORY) {
+ TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, path = %.*S)\n", fcb->type, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ free_fcb(fcb);
+ Status = STATUS_NOT_A_DIRECTORY;
+ goto exit;
+ }
+
+ Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
+
+ if (options & FILE_DELETE_ON_CLOSE)
+ fcb->delete_on_close = TRUE;
+
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ RtlZeroMemory(ccb, sizeof(*ccb));
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(ccb);
+ ccb->disposition = RequestedDisposition;
+ ccb->options = options;
+ ccb->query_dir_offset = 0;
+ RtlInitUnicodeString(&ccb->query_string, NULL);
+ ccb->has_wildcard = FALSE;
+ ccb->specific_file = FALSE;
+
+ FileObject->FsContext2 = ccb;
+
+ FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+
+ if (NT_SUCCESS(Status)) {
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ Irp->IoStatus.Information = FILE_SUPERSEDED;
+ break;
+
+ case FILE_OPEN:
+ case FILE_OPEN_IF:
+ Irp->IoStatus.Information = FILE_OPENED;
+ break;
+
+ case FILE_OVERWRITE:
+ case FILE_OVERWRITE_IF:
+ Irp->IoStatus.Information = FILE_OVERWRITTEN;
+ break;
+ }
+ }
+
+ fcb->open_count++;
+ } else {
+ Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options);
+ Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
+
+// if (!NT_SUCCESS(Status))
+// free_fcb(fcb);
+ }
+
+exit:
+ if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ device_extension* Vcb = NULL;
+ BOOL top_level;
+
+ TRACE("create (flags = %x)\n", Irp->Flags);
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ /* return success if just called for FS device object */
+ if (DeviceObject == devobj) {
+ TRACE("create called for FS device object\n");
+
+ Irp->IoStatus.Information = FILE_OPENED;
+ Status = STATUS_SUCCESS;
+
+ goto exit;
+ }
+
+ Vcb = DeviceObject->DeviceExtension;
+ ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (IrpSp->Flags != 0) {
+ UINT32 flags = IrpSp->Flags;
+
+ TRACE("flags:\n");
+
+ if (flags & SL_CASE_SENSITIVE) {
+ TRACE("SL_CASE_SENSITIVE\n");
+ flags &= ~SL_CASE_SENSITIVE;
+ }
+
+ if (flags & SL_FORCE_ACCESS_CHECK) {
+ TRACE("SL_FORCE_ACCESS_CHECK\n");
+ flags &= ~SL_FORCE_ACCESS_CHECK;
+ }
+
+ if (flags & SL_OPEN_PAGING_FILE) {
+ TRACE("SL_OPEN_PAGING_FILE\n");
+ flags &= ~SL_OPEN_PAGING_FILE;
+ }
+
+ if (flags & SL_OPEN_TARGET_DIRECTORY) {
+ TRACE("SL_OPEN_TARGET_DIRECTORY\n");
+ flags &= ~SL_OPEN_TARGET_DIRECTORY;
+ }
+
+ if (flags & SL_STOP_ON_SYMLINK) {
+ TRACE("SL_STOP_ON_SYMLINK\n");
+ flags &= ~SL_STOP_ON_SYMLINK;
+ }
+
+ if (flags)
+ WARN("unknown flags: %x\n", flags);
+ } else {
+ TRACE("flags: (none)\n");
+ }
+
+// Vpb = DeviceObject->DeviceExtension;
+
+// TRACE("create called for something other than FS device object\n");
+
+ // opening volume
+ // FIXME - also check if RelatedFileObject is Vcb
+ if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
+ ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+ ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+ TRACE("open operation for volume\n");
+
+ if (RequestedDisposition != FILE_OPEN &&
+ RequestedDisposition != FILE_OPEN_IF)
+ {
+ Status = STATUS_ACCESS_DENIED;
+ goto exit;
+ }
+
+ if (RequestedOptions & FILE_DIRECTORY_FILE)
+ {
+ Status = STATUS_NOT_A_DIRECTORY;
+ goto exit;
+ }
+
+ Vcb->volume_fcb->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, Vcb->volume_fcb->refcount);
+#endif
+ attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
+// // NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
+// // DeviceExt->VolumeFcb->RefCount++;
+//
+ Irp->IoStatus.Information = FILE_OPENED;
+ Status = STATUS_SUCCESS;
+ } else {
+ BOOL exclusive;
+ ULONG disposition;
+
+ TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
+
+ if (IrpSp->FileObject->RelatedFileObject) {
+ fcb* relfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
+
+ if (relfcb)
+ TRACE("related file name = %.*S\n", relfcb->full_filename.Length / sizeof(WCHAR), relfcb->full_filename.Buffer);
+ }
+
+ disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+
+ // We acquire the lock exclusively if there's the possibility we might be writing
+ exclusive = disposition != FILE_OPEN;
+
+ acquire_tree_lock(Vcb, exclusive);
+
+// ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
+ // Status = NtfsCreateFile(DeviceObject,
+ // Irp);
+ Status = create_file(DeviceObject, Irp);
+// ExReleaseResourceLite(&Vpb->DirResource);
+
+ release_tree_lock(Vcb, exclusive);
+
+// Status = STATUS_ACCESS_DENIED;
+ }
+
+exit:
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest( Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT );
+// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+
+ TRACE("create returning %08x\n", Status);
+
+ ExReleaseResourceLite(&Vcb->load_lock);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/dirctrl.c
===================================================================
--- reactos/drivers/filesystems/btrfs/dirctrl.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/dirctrl.c (working copy)
@@ -0,0 +1,791 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+enum DirEntryType {
+ DirEntryType_File,
+ DirEntryType_Self,
+ DirEntryType_Parent
+};
+
+typedef struct {
+ KEY key;
+ char* name;
+ ULONG namelen;
+ UINT8 type;
+ enum DirEntryType dir_entry_type;
+} dir_entry;
+
+static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
+ PIO_STACK_LOCATION IrpSp;
+ UINT32 needed;
+ UINT64 inode;
+ INODE_ITEM ii;
+ NTSTATUS Status;
+ ULONG stringlen;
+ BOOL dotfile;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol
+ r = fcb->Vcb->roots;
+ while (r && r->id != de->key.obj_id)
+ r = r->next;
+
+ if (!r) {
+ ERR("could not find root %llx\n", de->key.obj_id);
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ inode = SUBVOL_ROOT_INODE;
+ } else {
+ inode = de->key.obj_id;
+ }
+
+ if (IrpSp->Parameters.QueryDirectory.FileInformationClass != FileNamesInformation) { // FIXME - object ID and reparse point classes too?
+ switch (de->dir_entry_type) {
+ case DirEntryType_File:
+ if (!get_item(fcb->Vcb, r, inode, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM))) { // FIXME - used cached version
+ ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+ break;
+
+ case DirEntryType_Self:
+ ii = fcb->inode_item;
+ r = fcb->subvol;
+ inode = fcb->inode;
+ break;
+
+ case DirEntryType_Parent:
+ ii = fcb->par->inode_item;
+ r = fcb->par->subvol;
+ inode = fcb->par->inode;
+ break;
+ }
+ }
+
+ // FICs which return the filename
+ if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
+ IrpSp->Parameters.QueryDirectory.FileInformationClass == FileNamesInformation) {
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de->name, de->namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0);
+
+ switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
+ case FileBothDirectoryInformation:
+ {
+ FILE_BOTH_DIR_INFORMATION* fbdi = buf;
+
+ TRACE("FileBothDirectoryInformation\n");
+
+ needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fbdi->NextEntryOffset = 0;
+ fbdi->FileIndex = 0;
+ fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fbdi->ChangeTime.QuadPart = 0;
+ fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fbdi->FileNameLength = stringlen;
+ fbdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fbdi->ShortNameLength = 0;
+// fibdi->ShortName[12];
+
+ Status = RtlUTF8ToUnicodeN(fbdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileDirectoryInformation:
+ {
+ FILE_DIRECTORY_INFORMATION* fdi = buf;
+
+ TRACE("FileDirectoryInformation\n");
+
+ needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fdi->NextEntryOffset = 0;
+ fdi->FileIndex = 0;
+ fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fdi->ChangeTime.QuadPart = 0;
+ fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fdi->FileNameLength = stringlen;
+
+ Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileFullDirectoryInformation:
+ {
+ FILE_FULL_DIR_INFORMATION* ffdi = buf;
+
+ TRACE("FileFullDirectoryInformation\n");
+
+ needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ ffdi->NextEntryOffset = 0;
+ ffdi->FileIndex = 0;
+ ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ ffdi->ChangeTime.QuadPart = 0;
+ ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ ffdi->FileNameLength = stringlen;
+ ffdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+
+ Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileIdBothDirectoryInformation:
+ {
+ FILE_ID_BOTH_DIR_INFORMATION* fibdi = buf;
+
+ TRACE("FileIdBothDirectoryInformation\n");
+
+ needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+// if (!buf)
+// return STATUS_INVALID_POINTER;
+
+ fibdi->NextEntryOffset = 0;
+ fibdi->FileIndex = 0;
+ fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+ fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+ fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+ fibdi->ChangeTime.QuadPart = 0;
+ fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+ fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
+ fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ fibdi->FileNameLength = stringlen;
+ fibdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fibdi->ShortNameLength = 0;
+// fibdi->ShortName[12];
+ fibdi->FileId.QuadPart = inode;
+
+ Status = RtlUTF8ToUnicodeN(fibdi->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileIdFullDirectoryInformation:
+ FIXME("STUB: FileIdFullDirectoryInformation\n");
+ break;
+
+ case FileNamesInformation:
+ {
+ FILE_NAMES_INFORMATION* fni = buf;
+
+ TRACE("FileNamesInformation\n");
+
+ needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen;
+
+ if (needed > *len) {
+ WARN("buffer overflow - %u > %u\n", needed, *len);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ fni->NextEntryOffset = 0;
+ fni->FileIndex = 0;
+ fni->FileNameLength = stringlen;
+
+ Status = RtlUTF8ToUnicodeN(fni->FileName, stringlen, &stringlen, de->name, de->namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ return Status;
+ }
+
+ *len -= needed;
+
+ return STATUS_SUCCESS;
+ }
+
+ case FileObjectIdInformation:
+ FIXME("STUB: FileObjectIdInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ case FileQuotaInformation:
+ FIXME("STUB: FileQuotaInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ case FileReparsePointInformation:
+ FIXME("STUB: FileReparsePointInformation\n");
+ return STATUS_NOT_IMPLEMENTED;
+
+ default:
+ WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ return STATUS_NO_MORE_FILES;
+}
+
+static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
+ KEY searchkey;
+ traverse_ptr next_tp;
+ DIR_ITEM* di;
+
+ if (fcb->par) { // don't return . and .. if root directory
+ if (*offset == 0) {
+ de->key.obj_id = fcb->inode;
+ de->key.obj_type = TYPE_INODE_ITEM;
+ de->key.offset = 0;
+ de->dir_entry_type = DirEntryType_Self;
+ de->name = ".";
+ de->namelen = 1;
+ de->type = BTRFS_TYPE_DIRECTORY;
+
+ *offset = 1;
+
+ return STATUS_SUCCESS;
+ } else if (*offset == 1) {
+ de->key.obj_id = fcb->par->inode;
+ de->key.obj_type = TYPE_INODE_ITEM;
+ de->key.offset = 0;
+ de->dir_entry_type = DirEntryType_Parent;
+ de->name = "..";
+ de->namelen = 2;
+ de->type = BTRFS_TYPE_DIRECTORY;
+
+ *offset = 2;
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ if (!tp->tree) {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = *offset;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ free_traverse_ptr(tp);
+ tp->tree = NULL;
+ return STATUS_NO_MORE_FILES;
+ }
+
+ TRACE("found item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+
+ if (keycmp(&tp->item->key, &searchkey) == -1) {
+ if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
+ free_traverse_ptr(tp);
+ *tp = next_tp;
+
+ TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+ }
+ }
+
+ if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) {
+ free_traverse_ptr(tp);
+ tp->tree = NULL;
+ return STATUS_NO_MORE_FILES;
+ }
+
+ *offset = tp->item->key.offset + 1;
+
+ di = (DIR_ITEM*)tp->item->data;
+ de->key = di->key;
+ de->name = di->name;
+ de->namelen = di->n;
+ de->type = di->type;
+ de->dir_entry_type = DirEntryType_File;
+
+ return STATUS_SUCCESS;
+ } else {
+ if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
+ if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) {
+ free_traverse_ptr(tp);
+ *tp = next_tp;
+
+ *offset = tp->item->key.offset + 1;
+
+ di = (DIR_ITEM*)tp->item->data;
+ de->key = di->key;
+ de->name = di->name;
+ de->namelen = di->n;
+ de->type = di->type;
+ de->dir_entry_type = DirEntryType_File;
+
+ return STATUS_SUCCESS;
+ } else {
+ free_traverse_ptr(&next_tp);
+ return STATUS_NO_MORE_FILES;
+ }
+ } else
+ return STATUS_NO_MORE_FILES;
+ }
+}
+
+static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status, status2;
+ fcb* fcb;
+ ccb* ccb;
+ void* buf;
+ UINT8 *curitem, *lastitem;
+ LONG length;
+ ULONG count;
+ BOOL has_wildcard = FALSE, specific_file = FALSE;
+// UINT64 num_reads_orig;
+ traverse_ptr tp;
+ dir_entry de;
+
+ TRACE("query directory\n");
+
+// get_uid(); // TESTING
+
+// num_reads_orig = num_reads;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ fcb = IrpSp->FileObject->FsContext;
+ ccb = IrpSp->FileObject->FsContext2;
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ if (IrpSp->Flags == 0) {
+ TRACE("QD flags: (none)\n");
+ } else {
+ ULONG flags = IrpSp->Flags;
+
+ TRACE("QD flags:\n");
+
+ if (flags & SL_INDEX_SPECIFIED) {
+ TRACE(" SL_INDEX_SPECIFIED\n");
+ flags &= ~SL_INDEX_SPECIFIED;
+ }
+
+ if (flags & SL_RESTART_SCAN) {
+ TRACE(" SL_RESTART_SCAN\n");
+ flags &= ~SL_RESTART_SCAN;
+ }
+
+ if (flags & SL_RETURN_SINGLE_ENTRY) {
+ TRACE(" SL_RETURN_SINGLE_ENTRY\n");
+ flags &= ~SL_RETURN_SINGLE_ENTRY;
+ }
+
+ if (flags != 0)
+ TRACE(" unknown flags: %u\n", flags);
+ }
+
+ if (IrpSp->Flags & SL_RESTART_SCAN) {
+ ccb->query_dir_offset = 0;
+
+ if (ccb->query_string.Buffer) {
+ RtlFreeUnicodeString(&ccb->query_string);
+ ccb->query_string.Buffer = NULL;
+ }
+ }
+
+ if (IrpSp->Parameters.QueryDirectory.FileName) {
+// int i;
+// WCHAR* us;
+
+ TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
+
+// if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
+// specific_file = TRUE;
+// for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) {
+// if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') {
+// has_wildcard = TRUE;
+// specific_file = FALSE;
+// }
+// }
+// }
+ has_wildcard = TRUE;
+
+ if (ccb->query_string.Buffer)
+ RtlFreeUnicodeString(&ccb->query_string);
+
+// us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
+// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
+// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
+
+// ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG);
+// utf16_to_utf8(us, ccb->query_string);
+
+// ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
+// RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer,
+// IrpSp->Parameters.QueryDirectory.FileName->Length);
+// ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length;
+// ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
+ RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
+
+ ccb->has_wildcard = has_wildcard;
+ ccb->specific_file = specific_file;
+
+// ExFreePool(us);
+ } else {
+ has_wildcard = ccb->has_wildcard;
+ specific_file = ccb->specific_file;
+ }
+
+ if (ccb->query_string.Buffer) {
+ TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
+ }
+
+ tp.tree = NULL;
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ Status = STATUS_NO_SUCH_FILE;
+ goto end;
+ }
+
+ // FIXME - make this work
+// if (specific_file) {
+// UINT64 filesubvol, fileinode;
+// WCHAR* us;
+//
+// us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
+// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
+// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
+//
+// if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) {
+// ExFreePool(us);
+// return STATUS_NO_MORE_FILES;
+// }
+//
+// ExFreePool(us);
+// }
+
+ buf = map_user_buffer(Irp);
+ length = IrpSp->Parameters.QueryDirectory.Length;
+
+// if (specific_file) {
+ if (has_wildcard) {
+ WCHAR* uni_fn;
+ ULONG stringlen;
+ UNICODE_STRING di_uni_fn;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+
+ while (!FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+
+ ExFreePool(uni_fn);
+ if (NT_SUCCESS(Status)) {
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExFreePool(uni_fn);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+ } else {
+ if (tp.tree) free_traverse_ptr(&tp);
+
+ if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ Status = STATUS_NO_SUCH_FILE;
+
+ goto end;
+ }
+ }
+
+ ExFreePool(uni_fn);
+ }
+
+ TRACE("file(0) = %.*s\n", de.namelen, de.name);
+ TRACE("offset = %u\n", ccb->query_dir_offset - 1);
+
+ Status = query_dir_item(fcb, buf, &length, Irp, &de, fcb->subvol);
+
+ count = 0;
+ if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
+ lastitem = (UINT8*)buf;
+
+ while (length > 0) {
+ switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
+ case FileBothDirectoryInformation:
+ case FileIdBothDirectoryInformation:
+ case FileFullDirectoryInformation:
+ length -= length % 8;
+ break;
+
+ case FileNamesInformation:
+ length -= length % 4;
+ break;
+
+ default:
+ WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
+ break;
+ }
+
+ if (length > 0) {
+ WCHAR* uni_fn = NULL;
+ UNICODE_STRING di_uni_fn;
+
+ Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+ if (NT_SUCCESS(Status)) {
+ if (has_wildcard) {
+ ULONG stringlen;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExFreePool(uni_fn);
+ if (tp.tree) free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
+ di_uni_fn.Buffer = uni_fn;
+ }
+
+ if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
+ curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
+ count++;
+
+ TRACE("file(%u) %u = %.*s\n", count, curitem - (UINT8*)buf, de.namelen, de.name);
+ TRACE("offset = %u\n", ccb->query_dir_offset - 1);
+
+ status2 = query_dir_item(fcb, curitem, &length, Irp, &de, fcb->subvol);
+
+ if (NT_SUCCESS(status2)) {
+ ULONG* lastoffset = (ULONG*)lastitem;
+
+ *lastoffset = (ULONG)(curitem - lastitem);
+
+ lastitem = curitem;
+ } else {
+ if (uni_fn) ExFreePool(uni_fn);
+
+ break;
+ }
+ }
+
+ if (uni_fn) {
+ ExFreePool(uni_fn);
+ uni_fn = NULL;
+ }
+ } else {
+ if (Status == STATUS_NO_MORE_FILES)
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+ } else
+ break;
+ }
+ }
+
+ Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
+
+ if (tp.tree) free_traverse_ptr(&tp);
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+// TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig));
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ NTSTATUS Status;
+// WCHAR fn[MAX_PATH];
+
+ TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ // FIXME - raise exception if FCB marked for deletion?
+
+ TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fcb->full_filename,
+ IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
+
+ Status = STATUS_PENDING;
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ ULONG func;
+ BOOL top_level;
+
+ TRACE("directory control\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ func = IrpSp->MinorFunction;
+
+ switch (func) {
+ case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
+ Status = notify_change_directory(DeviceObject->DeviceExtension, Irp);
+ break;
+
+ case IRP_MN_QUERY_DIRECTORY:
+ Status = query_directory(DeviceObject, Irp);
+ break;
+
+ default:
+ WARN("unknown minor %u\n", func);
+ Status = STATUS_NOT_IMPLEMENTED;
+ Irp->IoStatus.Status = Status;
+ break;
+ }
+
+ if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) {
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest( Irp, IO_DISK_INCREMENT );
+ }
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/fastio.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fastio.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fastio.c (working copy)
@@ -0,0 +1,182 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include
+#include "btrfs_drv.h"
+
+FAST_IO_DISPATCH FastIoDispatch;
+
+static void STDCALL acquire_file_for_create_section(PFILE_OBJECT FileObject) {
+ TRACE("STUB: acquire_file_for_create_section\n");
+}
+
+static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) {
+ TRACE("STUB: release_file_for_create_section\n");
+}
+
+static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION buf,
+ PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+
+ TRACE("STUB: fast_query_basic_info\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION buf,
+ PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+
+ TRACE("STUB: fast_query_standard_info\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, PDEVICE_OBJECT DeviceObject) {
+ TRACE("STUB: fast_io_query_open\n");
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_check_if_possible(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
+ ULONG LockKey, BOOLEAN CheckForReadOperation, PIO_STATUS_BLOCK IoStatus,
+ PDEVICE_OBJECT DeviceObject) {
+ fcb* fcb = FileObject->FsContext;
+ LARGE_INTEGER len2;
+
+ TRACE("(%p, %llx, %x, %x, %x, %x, %p, %p)\n", FileObject, FileOffset->QuadPart, Length, Wait, LockKey, CheckForReadOperation, IoStatus, DeviceObject);
+
+ len2.QuadPart = Length;
+
+ if (CheckForReadOperation) {
+ if (FsRtlFastCheckLockForRead(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
+ return TRUE;
+ } else {
+ if (!fcb->Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) && FsRtlFastCheckLockForWrite(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_lock(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_lock\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_single(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_single\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_all(PFILE_OBJECT FileObject, PEPROCESS ProcessId, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_all\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_unlock_all_by_key(PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_unlock_all_by_key\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_device_control(PFILE_OBJECT FileObject, BOOLEAN Wait, PVOID InputBuffer OPTIONAL, ULONG InputBufferLength, PVOID OutputBuffer OPTIONAL, ULONG OutputBufferLength, ULONG IoControlCode, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_device_control\n");
+ return FALSE;
+}
+
+static VOID STDCALL fast_io_detach_device(PDEVICE_OBJECT SourceDevice, PDEVICE_OBJECT TargetDevice){
+ TRACE("STUB: fast_io_detach_device\n");
+}
+
+static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, struct _FILE_NETWORK_OPEN_INFORMATION *Buffer, struct _IO_STATUS_BLOCK *IoStatus, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_query_network_open_info\n");
+ return FALSE;
+}
+
+static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_acquire_for_mod_write\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_read_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_write_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_write_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_mdl_read_complete_compressed(PFILE_OBJECT FileObject, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_mdl_read_complete_compressed\n");
+ return FALSE;
+}
+
+static BOOLEAN STDCALL fast_io_mdl_write_complete_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_mdl_write_complete_compressed\n");
+ return FALSE;
+}
+
+static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_release_for_mod_write\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_acquire_for_ccflush\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+ TRACE("STUB: fast_io_release_for_ccflush\n");
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
+ RtlZeroMemory(&FastIoDispatch, sizeof(FastIoDispatch));
+
+ FastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
+
+ FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible;
+ FastIoDispatch.FastIoRead = FsRtlCopyRead;
+ FastIoDispatch.FastIoWrite = FsRtlCopyWrite;
+ FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info;
+ FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info;
+ FastIoDispatch.FastIoLock = fast_io_lock;
+ FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single;
+ FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all;
+ FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key;
+ FastIoDispatch.FastIoDeviceControl = fast_io_device_control;
+ FastIoDispatch.AcquireFileForNtCreateSection = acquire_file_for_create_section;
+ FastIoDispatch.ReleaseFileForNtCreateSection = release_file_for_create_section;
+ FastIoDispatch.FastIoDetachDevice = fast_io_detach_device;
+ FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info;
+ FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write;
+ FastIoDispatch.MdlRead = FsRtlMdlReadDev;
+ FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
+ FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
+ FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
+ FastIoDispatch.FastIoReadCompressed = fast_io_read_compressed;
+ FastIoDispatch.FastIoWriteCompressed = fast_io_write_compressed;
+ FastIoDispatch.MdlReadCompleteCompressed = fast_io_mdl_read_complete_compressed;
+ FastIoDispatch.MdlWriteCompleteCompressed = fast_io_mdl_write_complete_compressed;
+ FastIoDispatch.FastIoQueryOpen = fast_io_query_open;
+ FastIoDispatch.ReleaseForModWrite = fast_io_release_for_mod_write;
+ FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush;
+ FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush;
+
+ *fiod = &FastIoDispatch;
+}
\ No newline at end of file
Index: reactos/drivers/filesystems/btrfs/fileinfo.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fileinfo.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fileinfo.c (working copy)
@@ -0,0 +1,2598 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ ULONG defda;
+ BOOL inode_item_changed = FALSE;
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+ TRACE("file = %.*S, attributes = %x\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fbi->FileAttributes);
+
+ if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
+ WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // FIXME - what if FCB is volume or root?
+ // FIXME - what about subvol roots?
+
+ // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
+ // FIXME - handle times == -1
+
+ // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
+ if (fbi->FileAttributes != 0) {
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY)
+ fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (fcb->type == BTRFS_TYPE_SYMLINK)
+ fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ // create new xattr
+ if (defda != fbi->FileAttributes) {
+ char val[64];
+
+ TRACE("inserting new DOSATTRIB xattr\n");
+ sprintf(val, "0x%lx", fbi->FileAttributes);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val));
+ } else
+ delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH);
+
+ fcb->atts = fbi->FileAttributes;
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.st_ctime = now;
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ inode_item_changed = TRUE;
+ }
+
+// FIXME - CreationTime
+// FIXME - LastAccessTime
+// FIXME - LastWriteTime
+// FIXME - ChangeTime
+
+ if (inode_item_changed) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find old INODE_ITEM\n");
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ii);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ ULONG atts;
+
+ TRACE("changing delete_on_close to %s for %.*S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+
+ atts = fcb->ads ? fcb->par->atts : fcb->atts;
+ TRACE("atts = %x\n", atts);
+
+ if (atts & FILE_ATTRIBUTE_READONLY)
+ return STATUS_CANNOT_DELETE;
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
+ return STATUS_DIRECTORY_NOT_EMPTY;
+
+ if (fcb->inode == SUBVOL_ROOT_INODE) {
+ FIXME("FIXME - subvol deletion not yet supported\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ fcb->delete_on_close = fdi->DeleteFile;
+ // FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
+ FileObject->DeletePending = fdi->DeleteFile;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_EXTREF* ier;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG iersize = tp.item->size + sizeof(INODE_EXTREF) - 1 + utf8->Length;
+ UINT8* ier2;
+ UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (iersize > maxlen) {
+ ERR("item would be too long (%u > %u)\n", iersize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ier2 = ExAllocatePoolWithTag(PagedPool, iersize, ALLOC_TAG);
+ RtlCopyMemory(ier2, tp.item->data, tp.item->size);
+
+ ier = (INODE_EXTREF*)&ier2[tp.item->size];
+ ier->dir = parinode;
+ ier->index = index;
+ ier->n = utf8->Length;
+ RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ier = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_EXTREF) - 1 + utf8->Length, ALLOC_TAG);
+
+ ier->dir = parinode;
+ ier->index = index;
+ ier->n = utf8->Length;
+ RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8) {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_REF* ir;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = parinode;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG irsize = tp.item->size + sizeof(INODE_REF) - 1 + utf8->Length;
+ UINT8* ir2;
+ UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ if (irsize > maxlen) {
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+ TRACE("INODE_REF too long, creating INODE_EXTREF\n");
+ free_traverse_ptr(&tp);
+ return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8);
+ } else {
+ ERR("item would be too long (%u > %u)\n", irsize, maxlen);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ ir2 = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
+ RtlCopyMemory(ir2, tp.item->data, tp.item->size);
+
+ ir = (INODE_REF*)&ir2[tp.item->size];
+ ir->index = index;
+ ir->n = utf8->Length;
+ RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + utf8->Length, ALLOC_TAG);
+
+ ir->index = index;
+ ir->n = utf8->Length;
+ RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS get_fcb_from_dir_item(device_extension* Vcb, fcb** pfcb, fcb* parent, root* subvol, DIR_ITEM* di) {
+ LIST_ENTRY* le;
+ fcb* sf2;
+ struct _fcb* c;
+
+ le = parent->children.Flink;
+
+ while (le != &parent->children) {
+ c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (c->refcount > 0 && c->inode == di->key.obj_id && c->subvol == subvol) {
+ c->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+#endif
+ *pfcb = c;
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ sf2 = create_fcb();
+ sf2->Vcb = Vcb;
+
+ sf2->utf8.Length = sf2->utf8.MaximumLength = di->n;
+ sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
+ RtlCopyMemory(sf2->utf8.Buffer, di->name, di->n);
+
+ sf2->par = parent;
+
+ parent->refcount++;
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", parent, parent->refcount, parent->full_filename.Length / sizeof(WCHAR), parent->full_filename.Buffer);
+#endif
+
+ sf2->subvol = subvol;
+ sf2->inode = di->key.obj_id;
+ sf2->type = di->type;
+
+ if (Vcb->fcbs)
+ Vcb->fcbs->prev = sf2;
+
+ sf2->next = Vcb->fcbs;
+ Vcb->fcbs = sf2;
+
+ sf2->name_offset = parent->full_filename.Length / sizeof(WCHAR);
+
+ if (parent != Vcb->root_fcb)
+ sf2->name_offset++;
+
+ InsertTailList(&parent->children, &sf2->list_entry);
+
+ if (!get_item(sf2->Vcb, sf2->subvol, sf2->inode, TYPE_INODE_ITEM, 0, &sf2->inode_item, sizeof(INODE_ITEM))) {
+ ERR("couldn't find (%llx,%x,0) in subvol %llx\n", sf2->inode, TYPE_INODE_ITEM, sf2->subvol->id);
+ free_fcb(sf2);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // This is just a quick function for the sake of move_across_subvols. As such, we don't bother
+ // with sf2->atts, sf2->sd, or sf2->full_filename.
+
+ *pfcb = sf2;
+
+ return STATUS_SUCCESS;
+}
+
+// static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) {
+// LONG rc = 0;
+// LIST_ENTRY* le = Vcb->trees.Flink;
+//
+// while (le != &Vcb->trees) {
+// tree* t = CONTAINING_RECORD(le, tree, list_entry);
+//
+// rc += t->refcount;
+//
+// le = le->Flink;
+// }
+//
+// le = tc->Flink;
+// while (le != tc) {
+// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+// tree* t;
+//
+// rc--;
+//
+// t = tc2->tree->parent;
+// while (t) {
+// rc--;
+// t = t->parent;
+// }
+//
+// le = le->Flink;
+// }
+//
+// return rc;
+// }
+
+static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, UINT64 inode, UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now) {
+ UINT64 oldindex, index;
+ UINT32 oldcrc32;
+ INODE_ITEM* ii;
+ BOOL has_hardlink = FALSE;
+ DIR_ITEM* di;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+
+ // move INODE_ITEM
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = *now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(Vcb, &tp);
+
+ if (fcb->inode_item.st_nlink > 1) {
+ fcb->inode_item.st_nlink--;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ has_hardlink = TRUE;
+ }
+ } else {
+ WARN("couldn't find old INODE_ITEM\n");
+ }
+
+ free_traverse_ptr(&tp);
+
+ fcb->inode_item.st_nlink = 1;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+
+ // delete old DIR_ITEM
+
+ Status = delete_dir_item(Vcb, fcb->subvol, oldparinode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // create new DIR_ITEM
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, oldparinode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete DIR_INDEX
+
+ if (oldindex == 0) {
+ WARN("couldn't find old INODE_REF\n");
+ } else {
+ searchkey.obj_id = oldparinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find old DIR_INDEX\n");
+
+ free_traverse_ptr(&tp);
+ }
+
+ // get new index
+
+ searchkey.obj_id = destinode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, destsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ index = tp.item->key.offset + 1;
+ } else
+ index = 2;
+
+ free_traverse_ptr(&tp);
+
+ // create INODE_REF
+
+ Status = add_inode_ref(Vcb, destsubvol, inode, destinode, index, utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // create DIR_INDEX
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // move XATTR_ITEMs
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(di, tp.item->data, tp.item->size);
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_XATTR_ITEM, tp.item->key.offset, di, tp.item->size, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!has_hardlink)
+ delete_tree_item(Vcb, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ // do extents
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", destsubvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(ed, tp.item->data, tp.item->size);
+
+ // FIXME - update ed's generation
+
+ if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ed, tp.item->size, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ if (!has_hardlink) {
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+ }
+ }
+
+ if (!has_hardlink)
+ delete_tree_item(Vcb, &tp);
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ fcb* fcb;
+ UINT8 level;
+ UINT32 crc32;
+ UINT64 newinode;
+ UINT64 newparinode;
+ ANSI_STRING utf8;
+ LIST_ENTRY list_entry;
+} dir_list;
+
+static NTSTATUS add_to_dir_list(fcb* fcb, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ NTSTATUS Status;
+
+ *empty = TRUE;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 2;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ struct _fcb* child;
+ dir_list* dl2;
+
+ // FIXME - what about subvols?
+ if (di->key.obj_type == TYPE_INODE_ITEM) {
+ TRACE("moving inode %llx\n", di->key.obj_id);
+
+ *empty = FALSE;
+
+ Status = get_fcb_from_dir_item(fcb->Vcb, &child, fcb, fcb->subvol, di);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_fcb_from_dir_item returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+
+ dl2 = ExAllocatePoolWithTag(PagedPool, sizeof(dir_list), ALLOC_TAG);
+ dl2->fcb = child;
+ dl2->level = level;
+ dl2->newparinode = newparinode;
+
+ dl2->utf8.Length = dl2->utf8.MaximumLength = di->n;
+ dl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dl2->utf8.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(dl2->utf8.Buffer, di->name, dl2->utf8.Length);
+ dl2->crc32 = calc_crc32c(0xfffffffe, (UINT8*)dl2->utf8.Buffer, (ULONG)dl2->utf8.Length);
+
+ InsertTailList(dl, &dl2->list_entry);
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now) {
+ UINT64 inode, oldparinode;
+ NTSTATUS Status;
+ LIST_ENTRY dl;
+
+ if (destsubvol->lastinode == 0)
+ get_last_inode(Vcb, destsubvol);
+
+ inode = destsubvol->lastinode + 1;
+ destsubvol->lastinode++;
+
+ oldparinode = fcb->subvol == fcb->par->subvol ? fcb->par->inode : SUBVOL_ROOT_INODE;
+
+ Status = move_inode_across_subvols(Vcb, fcb, destsubvol, destinode, inode, oldparinode, utf8, crc32, now);
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_inode_across_subvols returned %08x\n", Status);
+ return Status;
+ }
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
+ BOOL b, empty;
+ UINT8 level, max_level;
+ LIST_ENTRY* le;
+
+ InitializeListHead(&dl);
+
+ add_to_dir_list(fcb, 0, &dl, inode, &b);
+
+ level = 0;
+ do {
+ empty = TRUE;
+
+ le = dl.Flink;
+ while (le != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ if (dl2->level == level) {
+ inode++;
+ destsubvol->lastinode++;
+
+ dl2->newinode = inode;
+
+ add_to_dir_list(dl2->fcb, level+1, &dl, dl2->newinode, &b);
+ if (!b) empty = FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!empty) level++;
+ } while (!empty);
+
+ max_level = level;
+
+ for (level = 0; level <= max_level; level++) {
+ TRACE("level %u\n", level);
+
+ le = dl.Flink;
+ while (le != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ if (dl2->level == level) {
+ TRACE("inode %llx\n", dl2->fcb->inode);
+
+ Status = move_inode_across_subvols(Vcb, dl2->fcb, destsubvol, dl2->newparinode, dl2->newinode, dl2->fcb->par->inode, &dl2->utf8, dl2->crc32, now);
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_inode_across_subvols returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ while ((le = RemoveHeadList(&dl)) != &dl) {
+ dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
+
+ ExFreePool(dl2->utf8.Buffer);
+ free_fcb(dl2->fcb);
+
+ ExFreePool(dl2);
+ }
+ }
+
+ fcb->inode = inode;
+ fcb->subvol = destsubvol;
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = *now;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - find_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ROOT_REF* rr;
+ ULONG len;
+
+ rr = (ROOT_REF*)tp.item->data;
+ len = tp.item->size;
+
+ do {
+ ULONG itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n;
+
+ if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) {
+ ULONG newlen = tp.item->size - itemlen;
+
+ delete_tree_item(Vcb, &tp);
+
+ if (newlen == 0) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else {
+ UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff;
+
+ TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if ((UINT8*)rr > tp.item->data) {
+ RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data);
+ rroff = newrr + ((UINT8*)rr - tp.item->data);
+ } else {
+ rroff = newrr;
+ }
+
+ if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size)
+ RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data));
+
+ insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL);
+ }
+
+ if (index)
+ *index = rr->index;
+
+ break;
+ }
+
+ if (len > itemlen) {
+ len -= itemlen;
+ rr = (ROOT_REF*)&rr->name[rr->n];
+ } else
+ break;
+ } while (len > 0);
+ } else {
+ WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n;
+ UINT8* rr2;
+
+ rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ RtlCopyMemory(rr2, tp.item->data, tp.item->size);
+ RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n);
+ ExFreePool(rr);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL)) {
+ ERR("error - failed to insert item\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid) {
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT8* data;
+ ULONG datalen;
+
+ searchkey.obj_id = parsubvolid;
+ searchkey.obj_type = TYPE_ROOT_REF;
+ searchkey.offset = subvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ datalen = tp.item->size;
+
+ data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
+ RtlCopyMemory(data, tp.item->data, datalen);
+ } else {
+ datalen = 0;
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = subvolid;
+ searchkey.obj_type = TYPE_ROOT_BACKREF;
+ searchkey.offset = parsubvolid;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ if (datalen > 0) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, struct _fcb* destfcb, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists) {
+ DIR_ITEM* di;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT64 oldindex, index;
+ ROOT_REF* rr;
+
+ // delete old DIR_ITEM
+
+ Status = delete_dir_item(Vcb, fcb->par->subvol, fcb->par->inode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // create new DIR_ITEM
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = fcb->subvol->id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ Status = add_dir_item(Vcb, destfcb->subvol, destfcb->inode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete old ROOT_REF
+
+ oldindex = 0;
+
+ Status = delete_root_ref(Vcb, fcb->subvol->id, fcb->par->subvol->id, fcb->par->inode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_root_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ TRACE("root index = %llx\n", oldindex);
+
+ // delete old DIR_INDEX
+
+ if (oldindex != 0) {
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %lx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ delete_tree_item(Vcb, &tp);
+ } else {
+ WARN("could not find old DIR_INDEX entry\n");
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ // create new DIR_INDEX
+
+ if (fcb->par->subvol == destfcb->subvol && fcb->par->inode == destfcb->inode) {
+ index = oldindex;
+ } else {
+ index = find_next_dir_index(Vcb, destfcb->subvol, destfcb->inode);
+ }
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
+ di->key.obj_id = fcb->subvol->id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, destfcb->subvol, destfcb->inode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // create new ROOT_REF
+
+ rr = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_REF) - 1 + utf8->Length, ALLOC_TAG);
+ rr->dir = destfcb->inode;
+ rr->index = index;
+ rr->n = utf8->Length;
+ RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
+
+ Status = add_root_ref(Vcb, fcb->subvol->id, destfcb->subvol->id, rr);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_root_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = update_root_backref(Vcb, fcb->subvol->id, fcb->par->subvol->id);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_backref 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ if (fcb->par->subvol != destfcb->subvol) {
+ Status = update_root_backref(Vcb, fcb->subvol->id, destfcb->subvol->id);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_backref 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = *now;
+ }
+
+ destfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ destfcb->subvol->root_item.ctime = *now;
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL has_open_children(fcb* fcb) {
+ LIST_ENTRY* le = fcb->children.Flink;
+ struct _fcb* c;
+
+ while (le != &fcb->children) {
+ c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (c->refcount > 0) {
+ if (c->open_count > 0)
+ return TRUE;
+
+ if (has_open_children(c))
+ return TRUE;
+ }
+
+ le = le->Flink;
+ }
+
+ return FALSE;
+}
+
+static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) {
+ FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
+ fcb *fcb = FileObject->FsContext, *tfofcb, *oldparfcb, *oldfcb;
+ root* parsubvol;
+ UINT64 parinode, dirpos;
+ WCHAR* fn;
+ UNICODE_STRING fnus;
+ ULONG fnlen, utf8len, disize;
+ NTSTATUS Status;
+ ANSI_STRING utf8;
+ UINT32 crc32, oldcrc32;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ DIR_ITEM* di;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ BOOL across_directories;
+ INODE_ITEM* ii;
+
+ // FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
+
+ TRACE(" tfo = %p\n", tfo);
+ TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists);
+ TRACE(" RootDirectory = %p\n", fri->RootDirectory);
+ TRACE(" FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ utf8.Buffer = NULL;
+
+ if (!fcb->par) {
+ ERR("error - tried to rename file with no parent\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ fn = fri->FileName;
+ fnlen = fri->FileNameLength / sizeof(WCHAR);
+
+ if (!tfo) {
+ parsubvol = fcb->par->subvol;
+ parinode = fcb->par->inode;
+ tfofcb = NULL;
+
+ across_directories = FALSE;
+ } else {
+ LONG i;
+
+ tfofcb = tfo->FsContext;
+ parsubvol = tfofcb->subvol;
+ parinode = tfofcb->inode;
+
+ for (i = fnlen - 1; i >= 0; i--) {
+ if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
+ fn = &fri->FileName[i+1];
+ fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
+ break;
+ }
+ }
+
+ across_directories = parsubvol != fcb->par->subvol || parinode != fcb->par->inode;
+ }
+
+ fnus.Buffer = fn;
+ fnus.Length = fnus.MaximumLength = fnlen * sizeof(WCHAR);
+
+ TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ utf8.MaximumLength = utf8.Length = utf8len;
+ utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length);
+
+ // FIXME - set to crc32 if utf8 and oldutf8 are identical
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+
+// TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
+
+ oldfcb = NULL;
+
+ Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE);
+
+ if (NT_SUCCESS(Status)) {
+ WARN("destination file %.*S already exists\n", oldfcb->full_filename.Length / sizeof(WCHAR), oldfcb->full_filename.Buffer);
+
+ if (fcb != oldfcb && !(oldfcb->open_count == 0 && oldfcb->deleted)) {
+ if (!ReplaceIfExists) {
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end;
+ } else if (oldfcb->open_count >= 1 && !oldfcb->deleted) {
+ WARN("trying to overwrite open file\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (oldfcb->type == BTRFS_TYPE_DIRECTORY) {
+ WARN("trying to overwrite directory\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+ }
+ }
+
+ if (has_open_children(fcb)) {
+ WARN("trying to rename file with open children\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (oldfcb)
+ delete_fcb(oldfcb, NULL); // FIXME - make sure this doesn't cause problems if operation later fails
+
+ if (fcb->inode == SUBVOL_ROOT_INODE) {
+ UNICODE_STRING filename;
+
+ filename.Buffer = fn;
+ filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
+
+ Status = move_subvol(Vcb, fcb, tfofcb, &utf8, crc32, oldcrc32, &now, ReplaceIfExists);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_subvol returned %08x\n", Status);
+ goto end;
+ }
+ } else if (parsubvol != fcb->subvol) {
+ UNICODE_STRING filename;
+
+ filename.Buffer = fn;
+ filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
+
+ Status = move_across_subvols(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("move_across_subvols returned %08x\n", Status);
+ goto end;
+ }
+ } else {
+ UINT64 oldindex;
+ INODE_ITEM* ii;
+
+ // delete old DIR_ITEM entry
+
+ Status = delete_dir_item(Vcb, fcb->subvol, fcb->par->inode, oldcrc32, &fcb->utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // FIXME - make sure fcb's filepart matches the case on disk
+
+ // create new DIR_ITEM entry
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8.Length, ALLOC_TAG);
+ di->key.obj_id = fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8.Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
+
+ Status = add_dir_item(Vcb, parsubvol, parinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ oldindex = 0;
+
+ Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fcb->par->inode, &fcb->utf8, &oldindex);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete old DIR_INDEX entry
+
+ if (oldindex != 0) {
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = oldindex;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else {
+ WARN("couldn't find DIR_INDEX\n");
+ }
+
+ free_traverse_ptr(&tp);
+ } else {
+ WARN("couldn't get index from INODE_REF\n");
+ }
+
+ // create new DIR_INDEX entry
+
+ if (parsubvol != fcb->par->subvol || parinode != fcb->par->inode) {
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX + 1;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, parsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parsubvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ dirpos = 2;
+
+ do {
+ TRACE("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ dirpos = tp.item->key.offset + 1;
+ break;
+ }
+
+ if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ } else
+ break;
+ } while (tp.item->key.obj_id >= parinode && tp.item->key.obj_type >= TYPE_DIR_INDEX);
+
+ free_traverse_ptr(&tp);
+ } else
+ dirpos = oldindex;
+
+ disize = (ULONG)(sizeof(DIR_ITEM) - 1 + utf8.Length);
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ di->key.obj_id = fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = (UINT16)utf8.Length;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
+
+ if (!insert_tree_item(Vcb, parsubvol, parinode, TYPE_DIR_INDEX, dirpos, di, disize, NULL))
+ ERR("error - failed to insert item\n");
+
+ // create new INODE_REF entry
+
+ Status = add_inode_ref(Vcb, parsubvol, fcb->inode, parinode, dirpos, &utf8);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, parsubvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", parsubvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, parsubvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ WARN("insert_tree_item failed\n");
+ }
+ }
+
+ // update directory INODE_ITEMs
+
+ fcb->par->inode_item.transid = Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+ fcb->par->inode_item.st_mtime = now;
+
+ TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
+ if (!tfofcb || (fcb->par->inode == tfofcb->inode && fcb->par->subvol == tfofcb->subvol)) {
+ fcb->par->inode_item.st_size += 2 * (utf8.Length - fcb->utf8.Length);
+ } else {
+ fcb->par->inode_item.st_size -= 2 * fcb->utf8.Length;
+ TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb->inode_item.st_size);
+ tfofcb->inode_item.st_size += 2 * utf8.Length;
+ TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb->inode_item.st_size);
+ tfofcb->inode_item.transid = Vcb->superblock.generation;
+ tfofcb->inode_item.sequence++;
+ tfofcb->inode_item.st_ctime = now;
+ tfofcb->inode_item.st_mtime = now;
+ }
+ TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
+
+ if (oldfcb && oldfcb->par != fcb->par) {
+ TRACE("oldfcb->par->inode_item.st_size was %llx\n", oldfcb->par->inode_item.st_size);
+ oldfcb->par->inode_item.st_size -= 2 * oldfcb->utf8.Length;
+ TRACE("oldfcb->par->inode_item.st_size now %llx\n", oldfcb->par->inode_item.st_size);
+ }
+
+ searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->par->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->par->subvol, fcb->par->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL))
+ WARN("insert_tree_item failed\n");
+
+ free_traverse_ptr(&tp);
+
+ if (tfofcb && (fcb->par->inode != tfofcb->inode || fcb->par->subvol != tfofcb->subvol)) {
+ searchkey.obj_id = tfofcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, tfofcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", tfofcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp);
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &tfofcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, tfofcb->subvol, tfofcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL))
+ WARN("insert_tree_item failed\n");
+
+ free_traverse_ptr(&tp);
+ }
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ // FIXME - handle overwrite by rename here
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME, NULL);
+
+ // FIXME - change full_filename and name_offset of open children
+
+ if (fnlen != fcb->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fcb->filepart.Buffer, fcb->filepart.Length) != fcb->filepart.Length) {
+ RtlFreeUnicodeString(&fcb->filepart);
+ fcb->filepart.Length = fcb->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR));
+ fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.Length, ALLOC_TAG);
+ RtlCopyMemory(fcb->filepart.Buffer, fn, fcb->filepart.Length);
+ }
+
+ if (tfo && tfofcb != fcb->par) {
+ oldparfcb = fcb->par;
+ fcb->par = tfofcb;
+
+ fcb->par->refcount++;
+
+ RemoveEntryList(&fcb->list_entry);
+ InsertTailList(&fcb->par->children, &fcb->list_entry);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fcb %p: refcount now %i (%.*S)\n", fcb->par, fcb->par->refcount, fcb->par->full_filename.Length / sizeof(WCHAR), fcb->par->full_filename.Buffer);
+#endif
+ free_fcb(oldparfcb);
+ }
+
+ ExFreePool(fcb->utf8.Buffer);
+ fcb->utf8 = utf8;
+ utf8.Buffer = NULL;
+
+ // change fcb->full_filename
+
+ fcb->full_filename.MaximumLength = fcb->par->full_filename.Length + fcb->filepart.Length;
+ if (fcb->par->par) fcb->full_filename.MaximumLength += sizeof(WCHAR);
+ ExFreePool(fcb->full_filename.Buffer);
+ fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+ fcb->full_filename.Length = fcb->par->full_filename.Length;
+
+ if (fcb->par->par) {
+ fcb->full_filename.Buffer[fcb->full_filename.Length / sizeof(WCHAR)] = '\\';
+ fcb->full_filename.Length += sizeof(WCHAR);
+ }
+ fcb->name_offset = fcb->full_filename.Length / sizeof(WCHAR);
+
+ RtlAppendUnicodeStringToString(&fcb->full_filename, &fcb->filepart);
+
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
+ fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME, NULL);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (utf8.Buffer)
+ ExFreePool(utf8.Buffer);
+
+ if (oldfcb)
+ free_fcb(oldfcb);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only) {
+ FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+ CC_FILE_SIZES ccfs;
+ UINT8* data = NULL;
+ UINT16 datalen;
+
+ TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->adssize);
+
+ if (feofi->EndOfFile.QuadPart < fcb->adssize) {
+ if (advance_only)
+ return STATUS_SUCCESS;
+
+ TRACE("truncating stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ if (feofi->EndOfFile.QuadPart > 0) {
+ if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, feofi->EndOfFile.QuadPart);
+
+ fcb->adssize = feofi->EndOfFile.QuadPart;
+
+ if (data)
+ ExFreePool(data);
+ } else if (feofi->EndOfFile.QuadPart > fcb->adssize) {
+ UINT16 maxlen;
+ UINT8* data2;
+
+ TRACE("extending stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ // find maximum length of xattr
+ maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = fcb->adshash;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find key for xattr\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
+
+ free_traverse_ptr(&tp);
+
+ if (feofi->EndOfFile.QuadPart > maxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", feofi->EndOfFile.QuadPart, maxlen);
+ return STATUS_DISK_FULL;
+ }
+
+ if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ data2 = ExAllocatePoolWithTag(PagedPool, feofi->EndOfFile.QuadPart, ALLOC_TAG);
+ RtlCopyMemory(data2, data, datalen);
+ ExFreePool(data);
+
+ RtlZeroMemory(&data2[datalen], feofi->EndOfFile.QuadPart - datalen);
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, feofi->EndOfFile.QuadPart);
+
+ fcb->adssize = feofi->EndOfFile.QuadPart;
+
+ ExFreePool(data2);
+ }
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->par->inode_item.transid = Vcb->superblock.generation;
+ fcb->par->inode_item.sequence++;
+ fcb->par->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->par->subvol->root_item.ctime = now;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only) {
+ FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
+ fcb* fcb = FileObject->FsContext;
+ NTSTATUS Status;
+ LARGE_INTEGER time;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+ CC_FILE_SIZES ccfs;
+
+ if (fcb->deleted)
+ return STATUS_FILE_CLOSED;
+
+ if (fcb->ads)
+ return stream_set_end_of_file_information(Vcb, Irp, FileObject, advance_only);
+
+ TRACE("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
+ TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
+ fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+
+// int3;
+ TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
+
+// if (feofi->EndOfFile.QuadPart==0x36c000)
+// int3;
+
+ if (feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
+ if (advance_only)
+ return STATUS_SUCCESS;
+
+ TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ Status = truncate_file(fcb, feofi->EndOfFile.QuadPart);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - truncate_file failed\n");
+ return Status;
+ }
+ } else if (feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
+ UINT64 oldalloc, newalloc;
+
+ if (Irp->Flags & IRP_PAGING_IO) {
+ WARN("paging IO tried to extend file size\n");
+ return STATUS_SUCCESS;
+ }
+
+ TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
+
+ newalloc = sector_align(feofi->EndOfFile.QuadPart, Vcb->superblock.sector_size);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ oldalloc = 0;
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ oldalloc = tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size;
+ }
+
+ // FIXME - handle inline extents here
+ if (newalloc > oldalloc) {
+ // FIXME - we should be doing prealloc rather than inserting a sparse extent here
+
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, oldalloc, newalloc - oldalloc);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+
+ fcb->inode_item.st_size = feofi->EndOfFile.QuadPart;
+ TRACE("setting st_size to %llx\n", feofi->EndOfFile.QuadPart);
+
+ TRACE("newalloc = %llx\n", newalloc);
+
+ fcb->Header.AllocationSize.QuadPart = newalloc;
+ fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+
+ free_traverse_ptr(&tp);
+ }
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ TRACE("setting FileSize for %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, ccfs.FileSize);
+
+ KeQuerySystemTime(&time);
+
+ win_time_to_unix(time, &fcb->inode_item.st_mtime);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+// static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+// FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+// fcb* fcb = FileObject->FsContext;
+//
+// FIXME("FIXME\n");
+// ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+// ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
+//
+// return STATUS_NOT_IMPLEMENTED;
+// }
+
+static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+ FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+#if DEBUG_LEVEL >= 3
+ fcb* fcb = FileObject->FsContext;
+#endif
+
+ TRACE("setting the position on %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fpi->CurrentByteOffset.QuadPart);
+
+ // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
+
+ FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb = IrpSp->FileObject->FsContext;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
+ }
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ TRACE("set information\n");
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ switch (IrpSp->Parameters.SetFile.FileInformationClass) {
+ case FileAllocationInformation:
+ TRACE("FileAllocationInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE);
+ break;
+
+ case FileBasicInformation:
+ TRACE("FileBasicInformation\n");
+ Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileDispositionInformation:
+ TRACE("FileDispositionInformation\n");
+ Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileEndOfFileInformation:
+ TRACE("FileEndOfFileInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly);
+ break;
+
+ case FileLinkInformation:
+ FIXME("STUB: FileLinkInformation\n");
+ break;
+
+ case FilePositionInformation:
+ TRACE("FilePositionInformation\n");
+ Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileRenameInformation:
+ TRACE("FileRenameInformation\n");
+ // FIXME - make this work with streams
+ Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists);
+ break;
+
+ case FileValidDataLengthInformation:
+ FIXME("STUB: FileValidDataLengthInformation\n");
+ break;
+#ifndef __REACTOS__
+ case FileNormalizedNameInformation:
+ FIXME("STUB: FileNormalizedNameInformation\n");
+ break;
+
+ case FileStandardLinkInformation:
+ FIXME("STUB: FileStandardLinkInformation\n");
+ break;
+#endif
+ default:
+ WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
+ }
+
+ if (NT_SUCCESS(Status))
+ Status = consider_write(Vcb);
+
+ release_tree_lock(Vcb, TRUE);
+
+end:
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb) {
+ RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
+
+ *length -= sizeof(FILE_BASIC_INFORMATION);
+
+ fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+ fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+ fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+ fbi->ChangeTime.QuadPart = 0;
+ fbi->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, LONG* length) {
+ INODE_ITEM* ii;
+
+ if (*length < sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
+ WARN("overflow\n");
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
+
+ *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+
+ fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+ fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+ fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+ fnoi->ChangeTime.QuadPart = 0;
+
+ if (fcb->ads) {
+ fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adssize;
+ fnoi->FileAttributes = fcb->par->atts;
+ } else {
+ fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+ fnoi->FileAttributes = fcb->atts;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, LONG* length) {
+ RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
+
+ *length -= sizeof(FILE_STANDARD_INFORMATION);
+
+ if (fcb->ads) {
+ fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adssize;
+ fsi->NumberOfLinks = fcb->par->inode_item.st_nlink;
+ fsi->Directory = S_ISDIR(fcb->par->inode_item.st_mode);
+ } else {
+ fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+ fsi->NumberOfLinks = fcb->inode_item.st_nlink;
+ fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
+ }
+
+ TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
+
+ fsi->DeletePending = fcb->delete_on_close;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, UINT64 inode, LONG* length) {
+ *length -= sizeof(FILE_INTERNAL_INFORMATION);
+
+ fii->IndexNumber.QuadPart = inode;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_ea_information(FILE_EA_INFORMATION* eai, LONG* length) {
+ *length -= sizeof(FILE_EA_INFORMATION);
+
+ eai->EaSize = 0;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_access_information(FILE_ACCESS_INFORMATION* fai, LONG* length) {
+ *length -= sizeof(FILE_ACCESS_INFORMATION);
+
+ fai->AccessFlags = GENERIC_READ;
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS STDCALL fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
+ RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
+
+ *length -= sizeof(FILE_POSITION_INFORMATION);
+
+ fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_mode_information(FILE_MODE_INFORMATION* fmi, ccb* ccb, LONG* length) {
+ RtlZeroMemory(fmi, sizeof(FILE_MODE_INFORMATION));
+
+ *length -= sizeof(FILE_MODE_INFORMATION);
+
+ if (ccb->options & FILE_WRITE_THROUGH)
+ fmi->Mode |= FILE_WRITE_THROUGH;
+
+ if (ccb->options & FILE_SEQUENTIAL_ONLY)
+ fmi->Mode |= FILE_SEQUENTIAL_ONLY;
+
+ if (ccb->options & FILE_NO_INTERMEDIATE_BUFFERING)
+ fmi->Mode |= FILE_NO_INTERMEDIATE_BUFFERING;
+
+ if (ccb->options & FILE_SYNCHRONOUS_IO_ALERT)
+ fmi->Mode |= FILE_SYNCHRONOUS_IO_ALERT;
+
+ if (ccb->options & FILE_SYNCHRONOUS_IO_NONALERT)
+ fmi->Mode |= FILE_SYNCHRONOUS_IO_NONALERT;
+
+ if (ccb->options & FILE_DELETE_ON_CLOSE)
+ fmi->Mode |= FILE_DELETE_ON_CLOSE;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_alignment_information(FILE_ALIGNMENT_INFORMATION* fai, device_extension* Vcb, LONG* length) {
+ RtlZeroMemory(fai, sizeof(FILE_ALIGNMENT_INFORMATION));
+
+ *length -= sizeof(FILE_ALIGNMENT_INFORMATION);
+
+ fai->AlignmentRequirement = Vcb->devices[0].devobj->AlignmentRequirement;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, LONG* length) {
+#if DEBUG_LEVEL > 2
+ ULONG retlen = 0;
+#endif
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ ULONG datasuflen = wcslen(datasuf) * sizeof(WCHAR);
+
+ RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION));
+
+ *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
+
+ TRACE("maximum length is %u\n", *length);
+ fni->FileNameLength = 0;
+
+ fni->FileName[0] = 0;
+
+ if (*length >= (LONG)fcb->full_filename.Length) {
+ RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, fcb->full_filename.Length);
+#if DEBUG_LEVEL > 2
+ retlen = fcb->full_filename.Length;
+#endif
+ *length -= fcb->full_filename.Length;
+ } else {
+ if (*length > 0) {
+ RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, *length);
+#if DEBUG_LEVEL > 2
+ retlen = *length;
+#endif
+ }
+ *length = -1;
+ }
+
+ fni->FileNameLength = fcb->full_filename.Length;
+
+ if (fcb->ads) {
+ if (*length >= (LONG)datasuflen) {
+ RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen);
+#if DEBUG_LEVEL > 2
+ retlen += datasuflen;
+#endif
+ *length -= datasuflen;
+ } else {
+ if (*length > 0) {
+ RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, *length);
+#if DEBUG_LEVEL > 2
+ retlen += *length;
+#endif
+ }
+ *length = -1;
+ }
+ }
+
+ TRACE("%.*S\n", retlen / sizeof(WCHAR), fni->FileName);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, LONG* length) {
+ *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
+
+ ati->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
+ ati->ReparseTag = 0;
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ UNICODE_STRING name;
+ UINT64 size;
+} stream_info;
+
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fcb* fcb, LONG* length) {
+ ULONG reqsize;
+ UINT64 i, num_streams;
+ stream_info* streams;
+ FILE_STREAM_INFORMATION* entry;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+
+ static WCHAR datasuf[] = {':','$','D','A','T','A',0};
+ static char xapref[] = "user.";
+ UNICODE_STRING suf;
+
+ suf.Buffer = datasuf;
+ suf.Length = suf.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+
+ num_streams = 1;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ ULONG len = tp.item->size;
+ DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
+
+ do {
+ if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref))
+ num_streams++;
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
+ xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
+ } while (len > 0);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ streams = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info) * num_streams, ALLOC_TAG);
+
+ reqsize = 0;
+
+ streams[0].name.Length = streams[0].name.MaximumLength = 0;
+ streams[0].name.Buffer = NULL;
+ streams[0].size = fcb->inode_item.st_size;
+ reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[0].name.Length;
+
+ i = 1;
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ ULONG len = tp.item->size;
+ DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
+ ULONG stringlen;
+
+ do {
+ if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref)) {
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
+ if (!NT_SUCCESS(Status)) {
+ UINT64 j;
+
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+
+ for (j = i; j < num_streams; j++)
+ streams[j].name.Buffer = NULL;
+
+ goto end;
+ }
+
+ streams[i].name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(streams[i].name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
+
+ if (!NT_SUCCESS(Status)) {
+ UINT64 j;
+
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+
+ ExFreePool(streams[i].name.Buffer);
+ for (j = i; j < num_streams; j++)
+ streams[j].name.Buffer = NULL;
+
+ goto end;
+ }
+
+ streams[i].name.Length = streams[i].name.MaximumLength = stringlen;
+
+ streams[i].size = xa->m;
+ reqsize = sector_align(reqsize, sizeof(LONGLONG));
+ reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length;
+
+ TRACE("streams[%llu].name = %.*S (length = %u)\n", i, streams[i].name.Length / sizeof(WCHAR), streams[i].name.Buffer, streams[i].name.Length / sizeof(WCHAR));
+
+ i++;
+ }
+
+ len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
+ xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
+ } while (len > 0);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ TRACE("length = %i, reqsize = %u\n", *length, reqsize);
+
+ if (reqsize > *length) {
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ entry = fsi;
+ for (i = 0; i < num_streams; i++) {
+ entry->StreamNameLength = streams[i].name.Length + suf.Length + sizeof(WCHAR);
+ entry->StreamSize.QuadPart = streams[i].size;
+
+ if (i == 0)
+ entry->StreamAllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ else
+ entry->StreamAllocationSize.QuadPart = streams[i].size;
+
+ entry->StreamName[0] = ':';
+
+ if (streams[i].name.Length > 0)
+ RtlCopyMemory(&entry->StreamName[1], streams[i].name.Buffer, streams[i].name.Length);
+
+ RtlCopyMemory(&entry->StreamName[1 + (streams[i].name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
+
+ if (i == num_streams - 1)
+ entry->NextEntryOffset = 0;
+ else {
+ entry->NextEntryOffset = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length, sizeof(LONGLONG));
+
+ entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + entry->NextEntryOffset);
+ }
+ }
+
+ *length -= reqsize;
+
+ Status = STATUS_SUCCESS;
+
+end:
+ for (i = 0; i < num_streams; i++) {
+ if (streams[i].name.Buffer)
+ ExFreePool(streams[i].name.Buffer);
+ }
+
+ ExFreePool(streams);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, LONG* length) {
+ TRACE("FileStandardLinkInformation\n");
+
+ // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
+
+ fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
+ fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
+ fsli->DeletePending = fcb->delete_on_close;
+ fsli->Directory = fcb->type == BTRFS_TYPE_DIRECTORY ? TRUE : FALSE;
+
+ *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ LONG length = IrpSp->Parameters.QueryFile.Length;
+ fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
+ TRACE("fcb = %p\n", fcb);
+
+ if (fcb == Vcb->volume_fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
+ case FileAllInformation:
+ {
+ FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
+ INODE_ITEM* ii;
+
+ TRACE("FileAllInformation\n");
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+ if (length > 0)
+ fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb);
+
+ if (length > 0)
+ fill_in_file_standard_information(&fai->StandardInformation, fcb, &length);
+
+ if (length > 0)
+ fill_in_file_internal_information(&fai->InternalInformation, fcb->inode, &length);
+
+ if (length > 0)
+ fill_in_file_ea_information(&fai->EaInformation, &length);
+
+ if (length > 0)
+ fill_in_file_access_information(&fai->AccessInformation, &length);
+
+ if (length > 0)
+ fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
+
+ if (length > 0)
+ fill_in_file_mode_information(&fai->ModeInformation, ccb, &length);
+
+ if (length > 0)
+ fill_in_file_alignment_information(&fai->AlignmentInformation, Vcb, &length);
+
+ if (length > 0)
+ fill_in_file_name_information(&fai->NameInformation, fcb, &length);
+
+ Status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ case FileAttributeTagInformation:
+ {
+ FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileAttributeTagInformation\n");
+
+ Status = fill_in_file_attribute_information(ati, fcb, &length);
+
+ break;
+ }
+
+ case FileBasicInformation:
+ {
+ FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
+ INODE_ITEM* ii;
+
+ TRACE("FileBasicInformation\n");
+
+ if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
+ WARN("overflow\n");
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+
+ if (fcb->ads)
+ ii = &fcb->par->inode_item;
+ else
+ ii = &fcb->inode_item;
+
+ Status = fill_in_file_basic_information(fbi, ii, &length, fcb);
+ break;
+ }
+
+ case FileCompressionInformation:
+ FIXME("STUB: FileCompressionInformation\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+
+ case FileEaInformation:
+ {
+ FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileEaInformation\n");
+
+ Status = fill_in_file_ea_information(eai, &length);
+
+ break;
+ }
+
+ case FileInternalInformation:
+ {
+ FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileInternalInformation\n");
+
+ Status = fill_in_file_internal_information(fii, fcb->inode, &length);
+
+ break;
+ }
+
+ case FileNameInformation:
+ {
+ FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNameInformation\n");
+
+ Status = fill_in_file_name_information(fni, fcb, &length);
+
+ break;
+ }
+
+ case FileNetworkOpenInformation:
+ {
+ FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNetworkOpenInformation\n");
+
+ Status = fill_in_file_network_open_information(fnoi, fcb, &length);
+
+ break;
+ }
+
+ case FilePositionInformation:
+ {
+ FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FilePositionInformation\n");
+
+ Status = fill_in_file_position_information(fpi, FileObject, &length);
+
+ break;
+ }
+
+ case FileStandardInformation:
+ {
+ FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStandardInformation\n");
+
+ if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
+ WARN("overflow\n");
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto exit;
+ }
+
+ Status = fill_in_file_standard_information(fsi, fcb, &length);
+
+ break;
+ }
+
+ case FileStreamInformation:
+ {
+ FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStreamInformation\n");
+
+ Status = fill_in_file_stream_information(fsi, fcb, &length);
+
+ break;
+ }
+#ifndef __REACTOS__
+ case FileHardLinkInformation:
+ FIXME("STUB: FileHardLinkInformation\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+
+ case FileNormalizedNameInformation:
+ {
+ FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileNormalizedNameInformation\n");
+
+ Status = fill_in_file_name_information(fni, fcb, &length);
+
+ break;
+ }
+
+ case FileStandardLinkInformation:
+ {
+ FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileStandardLinkInformation\n");
+
+ Status = fill_in_file_standard_link_information(fsli, fcb, &length);
+
+ break;
+ }
+#endif
+ default:
+ WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ if (length < 0) {
+ length = 0;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
+
+exit:
+ TRACE("query_info returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ fcb* fcb;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Irp->IoStatus.Information = 0;
+
+ TRACE("query information\n");
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ acquire_tree_lock(Vcb, FALSE);
+
+ fcb = IrpSp->FileObject->FsContext;
+ TRACE("fcb = %p\n", fcb);
+ TRACE("fcb->subvol = %p\n", fcb->subvol);
+
+ Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
+
+ TRACE("returning %08x\n", Status);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ release_tree_lock(Vcb, FALSE);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/flushthread.c
===================================================================
--- reactos/drivers/filesystems/btrfs/flushthread.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/flushthread.c (working copy)
@@ -0,0 +1,57 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#define INTERVAL 15000 // in milliseconds
+
+static void do_flush(device_extension* Vcb) {
+ FsRtlEnterFileSystem();
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ if (Vcb->write_trees > 0)
+ do_write(Vcb);
+
+ free_tree_cache(&Vcb->tree_cache);
+
+ release_tree_lock(Vcb, TRUE);
+
+ FsRtlExitFileSystem();
+}
+
+void STDCALL flush_thread(void* context) {
+ device_extension* Vcb = context;
+ LARGE_INTEGER due_time;
+
+ KeInitializeTimer(&Vcb->flush_thread_timer);
+
+ due_time.QuadPart = -INTERVAL * 10000;
+
+ KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+
+ while (TRUE) {
+ KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
+
+ do_flush(Vcb);
+
+ KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+ }
+
+ KeCancelTimer(&Vcb->flush_thread_timer);
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
Index: reactos/drivers/filesystems/btrfs/fsctl.c
===================================================================
--- reactos/drivers/filesystems/btrfs/fsctl.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/fsctl.c (working copy)
@@ -0,0 +1,509 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#ifndef FSCTL_CSV_CONTROL
+#define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS Status;
+
+ switch (type) {
+ case FSCTL_REQUEST_OPLOCK_LEVEL_1:
+ WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_OPLOCK_LEVEL_2:
+ WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_BATCH_OPLOCK:
+ WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
+ WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_NOTIFY:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_LOCK_VOLUME:
+ WARN("STUB: FSCTL_LOCK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_UNLOCK_VOLUME:
+ WARN("STUB: FSCTL_UNLOCK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DISMOUNT_VOLUME:
+ WARN("STUB: FSCTL_DISMOUNT_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_VOLUME_MOUNTED:
+ WARN("STUB: FSCTL_IS_VOLUME_MOUNTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_PATHNAME_VALID:
+ WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MARK_VOLUME_DIRTY:
+ WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_RETRIEVAL_POINTERS:
+ WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_COMPRESSION:
+ WARN("STUB: FSCTL_GET_COMPRESSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_COMPRESSION:
+ WARN("STUB: FSCTL_SET_COMPRESSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_BOOTLOADER_ACCESSED:
+ WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_OPLOCK_BREAK_ACK_NO_2:
+ WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_INVALIDATE_VOLUMES:
+ WARN("STUB: FSCTL_INVALIDATE_VOLUMES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_FAT_BPB:
+ WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_REQUEST_FILTER_OPLOCK:
+ WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FILESYSTEM_GET_STATISTICS:
+ WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_NTFS_VOLUME_DATA:
+ WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_NTFS_FILE_RECORD:
+ WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_VOLUME_BITMAP:
+ WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_RETRIEVAL_POINTERS:
+ WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MOVE_FILE:
+ WARN("STUB: FSCTL_MOVE_FILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_IS_VOLUME_DIRTY:
+ WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ALLOW_EXTENDED_DASD_IO:
+ WARN("STUB: FSCTL_ALLOW_EXTENDED_DASD_IO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FIND_FILES_BY_SID:
+ WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_OBJECT_ID:
+ WARN("STUB: FSCTL_SET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_OBJECT_ID:
+ WARN("STUB: FSCTL_GET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DELETE_OBJECT_ID:
+ WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_REPARSE_POINT:
+ Status = set_reparse_point(DeviceObject, Irp);
+ break;
+
+ case FSCTL_GET_REPARSE_POINT:
+ Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
+ IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
+ break;
+
+ case FSCTL_DELETE_REPARSE_POINT:
+ WARN("STUB: FSCTL_DELETE_REPARSE_POINT\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENUM_USN_DATA:
+ WARN("STUB: FSCTL_ENUM_USN_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SECURITY_ID_CHECK:
+ WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_USN_JOURNAL:
+ WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_OBJECT_ID_EXTENDED:
+ WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CREATE_OR_GET_OBJECT_ID:
+ WARN("STUB: FSCTL_CREATE_OR_GET_OBJECT_ID\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_SPARSE:
+ WARN("STUB: FSCTL_SET_SPARSE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ZERO_DATA:
+ WARN("STUB: FSCTL_SET_ZERO_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_ALLOCATED_RANGES:
+ WARN("STUB: FSCTL_QUERY_ALLOCATED_RANGES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENABLE_UPGRADE:
+ WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ENCRYPTION:
+ WARN("STUB: FSCTL_SET_ENCRYPTION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_ENCRYPTION_FSCTL_IO:
+ WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WRITE_RAW_ENCRYPTED:
+ WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_RAW_ENCRYPTED:
+ WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CREATE_USN_JOURNAL:
+ WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_FILE_USN_DATA:
+ WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WRITE_USN_CLOSE_RECORD:
+ WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_EXTEND_VOLUME:
+ WARN("STUB: FSCTL_EXTEND_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_USN_JOURNAL:
+ WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DELETE_USN_JOURNAL:
+ WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_MARK_HANDLE:
+ WARN("STUB: FSCTL_MARK_HANDLE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SIS_COPYFILE:
+ WARN("STUB: FSCTL_SIS_COPYFILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SIS_LINK_FILES:
+ WARN("STUB: FSCTL_SIS_LINK_FILES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_RECALL_FILE:
+ WARN("STUB: FSCTL_RECALL_FILE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_READ_FROM_PLEX:
+ WARN("STUB: FSCTL_READ_FROM_PLEX\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_FILE_PREFETCH:
+ WARN("STUB: FSCTL_FILE_PREFETCH\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+#if (_WIN32_WINNT >= 0x0600)
+ case FSCTL_MAKE_MEDIA_COMPATIBLE:
+ WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_DEFECT_MANAGEMENT:
+ WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_SPARING_INFO:
+ WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
+ WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_VOLUME_COMPRESSION_STATE:
+ WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_MODIFY_RM:
+ WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_QUERY_RM_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_ROLLFORWARD_REDO:
+ WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_ROLLFORWARD_UNDO:
+ WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_START_RM:
+ WARN("STUB: FSCTL_TXFS_START_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_SHUTDOWN_RM:
+ WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_READ_BACKUP_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_CREATE_SECONDARY_RM:
+ WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_GET_METADATA_INFO:
+ WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_GET_TRANSACTED_VERSION:
+ WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_SAVEPOINT_INFORMATION:
+ WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_CREATE_MINIVERSION:
+ WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_TRANSACTION_ACTIVE:
+ WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_ZERO_ON_DEALLOCATION:
+ WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_REPAIR:
+ WARN("STUB: FSCTL_SET_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_GET_REPAIR:
+ WARN("STUB: FSCTL_GET_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_WAIT_FOR_REPAIR:
+ WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_INITIATE_REPAIR:
+ WARN("STUB: FSCTL_INITIATE_REPAIR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CSC_INTERNAL:
+ WARN("STUB: FSCTL_CSC_INTERNAL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SHRINK_VOLUME:
+ WARN("STUB: FSCTL_SHRINK_VOLUME\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_SET_SHORT_NAME_BEHAVIOR:
+ WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
+ WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
+ WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_LIST_TRANSACTIONS:
+ WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
+ WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
+ WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
+ WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ case FSCTL_CSV_CONTROL:
+ WARN("STUB: FSCTL_CSV_CONTROL\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+#endif
+ default:
+ WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
+ IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
+ (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
+ IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
+ Status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/reparse.c
===================================================================
--- reactos/drivers/filesystems/btrfs/reparse.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/reparse.c (working copy)
@@ -0,0 +1,521 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject) {
+ NTSTATUS Status;
+ ULONG len, stringlen;
+#if defined(__REACTOS__) && (NTDDI_VERSION >= NTDDI_VISTA)
+ USHORT newlen;
+#endif
+ OBJECT_NAME_INFORMATION* oni;
+ UINT8* data;
+ UINT32 i;
+ BOOL success = FALSE;
+ WCHAR* utf16;
+ UNICODE_STRING abspath;
+
+ if (fcb->inode_item.st_size == 0) {
+ WARN("can't follow symlink with no data\n");
+ return FALSE;
+ }
+
+ Status = ObQueryNameString(FileObject->DeviceObject, NULL, 0, &len);
+
+ if (Status != STATUS_INFO_LENGTH_MISMATCH) {
+ ERR("ObQueryNameString 1 returned %08x\n", Status);
+ return FALSE;
+ }
+
+ oni = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+
+ Status = ObQueryNameString(FileObject->DeviceObject, oni, len, &len);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ObQueryNameString 2 returned %08x\n", Status);
+ ExFreePool(oni);
+ return FALSE;
+ }
+
+ data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
+
+ Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, 0, fcb->inode_item.st_size, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ goto end;
+ }
+
+ for (i = 0; i < fcb->inode_item.st_size; i++) {
+ if (data[i] == '/')
+ data[i] = '\\';
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)data, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ goto end;
+ }
+
+ utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
+
+ Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, (char*)data, fcb->inode_item.st_size);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(utf16);
+ goto end;
+ }
+
+ if (utf16[0] == '\\') { // absolute path
+ abspath.MaximumLength = abspath.Length = stringlen;
+ abspath.Buffer = utf16;
+ } else { // relative path
+ abspath.MaximumLength = fcb->par->full_filename.Length + sizeof(WCHAR) + stringlen;
+ abspath.Buffer = ExAllocatePoolWithTag(PagedPool, abspath.MaximumLength, ALLOC_TAG);
+
+ RtlCopyMemory(abspath.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+ abspath.Length = fcb->par->full_filename.Length;
+
+ if (fcb->par->par) {
+ abspath.Buffer[abspath.Length / sizeof(WCHAR)] = '\\';
+ abspath.Length += sizeof(WCHAR);
+ }
+
+ RtlCopyMemory(&abspath.Buffer[abspath.Length / sizeof(WCHAR)], utf16, stringlen);
+ abspath.Length += stringlen;
+
+ ExFreePool(utf16);
+ }
+
+#if defined(__REACTOS__) && (NTDDI_VERSION >= NTDDI_VISTA)
+ Status = FsRtlRemoveDotsFromPath(abspath.Buffer, abspath.Length, &newlen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("FsRtlRemoveDotsFromPath returned %08x\n", Status);
+ ExFreePool(abspath.Buffer);
+ goto end;
+ }
+
+ abspath.Length = newlen;
+#endif
+
+ TRACE("abspath = %.*S\n", abspath.Length / sizeof(WCHAR), abspath.Buffer);
+
+ TRACE("filename was %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+
+ if (FileObject->FileName.MaximumLength < oni->Name.Length + abspath.Length) {
+ ExFreePool(FileObject->FileName.Buffer);
+ FileObject->FileName.MaximumLength = oni->Name.Length + abspath.Length;
+ FileObject->FileName.Buffer = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.MaximumLength, ALLOC_TAG);
+ }
+
+ RtlCopyMemory(FileObject->FileName.Buffer, oni->Name.Buffer, oni->Name.Length);
+ RtlCopyMemory(&FileObject->FileName.Buffer[oni->Name.Length / sizeof(WCHAR)], abspath.Buffer, abspath.Length);
+ FileObject->FileName.Length = oni->Name.Length + abspath.Length;
+
+ TRACE("filename now %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+
+ ExFreePool(abspath.Buffer);
+
+ success = TRUE;
+end:
+ ExFreePool(data);
+ ExFreePool(oni);
+
+ return success;
+}
+
+NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
+ USHORT subnamelen, printnamelen, i;
+ ULONG stringlen;
+ DWORD reqlen;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ fcb* fcb = FileObject->FsContext;
+ char* data;
+ NTSTATUS Status;
+
+ TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
+
+ acquire_tree_lock(fcb->Vcb, FALSE);
+
+ if (fcb->type != BTRFS_TYPE_SYMLINK) {
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ goto end;
+ }
+
+ data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
+ TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
+ Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ subnamelen = stringlen;
+ printnamelen = stringlen;
+
+ reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
+
+ if (buflen < reqlen) {
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+ rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+ rdb->Reserved = 0;
+
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
+ rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+
+ Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ stringlen, &stringlen, data, fcb->inode_item.st_size);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
+ if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
+ rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
+ }
+
+ RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
+ &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
+
+ *retlen = reqlen;
+
+ ExFreePool(data);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ release_tree_lock(fcb->Vcb, FALSE);
+
+ return Status;
+}
+
+static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type) {
+ KEY searchkey;
+ UINT32 crc32;
+ traverse_ptr tp;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
+ BOOL found = FALSE;
+ ULONG len = tp.item->size;
+
+ RtlCopyMemory(di, tp.item->data, tp.item->size);
+
+ di2 = di;
+ do {
+ if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
+ di2->type = type;
+ found = TRUE;
+ break;
+ }
+
+ if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) {
+ len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n;
+ di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n];
+ } else
+ break;
+ } while (len > 0);
+
+ if (found) {
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL);
+ } else
+ ExFreePool(di);
+ } else {
+ WARN("search for DIR_ITEM by crc32 failed\n");
+ }
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = index;
+
+ if (!find_item(Vcb, subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(di2, di, tp.item->size);
+ di2->type = type;
+
+ delete_tree_item(Vcb, &tp);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL);
+ }
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ void* buffer = Irp->AssociatedIrp.SystemBuffer;
+ DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ NTSTATUS Status = STATUS_SUCCESS;
+ fcb* fcb;
+ ULONG tag, minlen;
+ UNICODE_STRING subname;
+ ANSI_STRING target;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LARGE_INTEGER offset;
+ USHORT i;
+
+ // FIXME - send notification if this succeeds? The attributes will have changed.
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fcb = FileObject->FsContext;
+
+ ERR("filename: %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ WARN("tried to set a reparse point on an existing symlink\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (!fcb->utf8.Buffer) {
+ ERR("error - utf8 on FCB not set\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // FIXME - die if not file
+ // FIXME - die if ADS
+
+ if (buflen < sizeof(ULONG)) {
+ WARN("buffer was not long enough to hold tag\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ RtlCopyMemory(&tag, buffer, sizeof(ULONG));
+
+ if (tag != IO_REPARSE_TAG_SYMLINK) {
+ FIXME("FIXME: handle arbitrary reparse tags (%08x)\n", tag);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto end;
+ }
+
+ minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
+ if (buflen < minlen) {
+ WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (!(rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) {
+ FIXME("FIXME: support non-relative symlinks\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ }
+
+ subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
+ subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
+
+ ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
+
+ fcb->type = BTRFS_TYPE_SYMLINK;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) {
+ INODE_REF* ir;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ir = (INODE_REF*)tp.item->data;
+
+ do {
+ utf8.Buffer = ir->name;
+ utf8.Length = utf8.MaximumLength = ir->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ goto end;
+ }
+
+ if (size > sizeof(INODE_REF) - 1 + ir->n) {
+ size -= sizeof(INODE_REF) - 1 + ir->n;
+
+ ir = (INODE_REF*)&ir->name[ir->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+ INODE_EXTREF* ier;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ier = (INODE_EXTREF*)tp.item->data;
+
+ do {
+ utf8.Buffer = ier->name;
+ utf8.Length = utf8.MaximumLength = ier->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ goto end;
+ }
+
+ if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
+ size -= sizeof(INODE_EXTREF) - 1 + ier->n;
+
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+ }
+
+ fcb->inode_item.st_mode |= __S_IFLNK;
+
+ Status = truncate_file(fcb, 0);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
+ goto end;
+ }
+
+ target.MaximumLength = target.Length;
+ target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
+
+ Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status);
+ goto end;
+ }
+
+ for (i = 0; i < target.Length; i++) {
+ if (target.Buffer[i] == '\\')
+ target.Buffer[i] = '/';
+ }
+
+ offset.QuadPart = 0;
+ Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE);
+
+ ExFreePool(target.Buffer);
+
+ if (NT_SUCCESS(Status))
+ Status = consider_write(fcb->Vcb);
+
+end:
+ release_tree_lock(fcb->Vcb, TRUE);
+
+ return Status;
+}
Index: reactos/drivers/filesystems/btrfs/resource.h
===================================================================
--- reactos/drivers/filesystems/btrfs/resource.h (revision 0)
+++ reactos/drivers/filesystems/btrfs/resource.h (working copy)
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by btrfs.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
Index: reactos/drivers/filesystems/btrfs/search.c
===================================================================
--- reactos/drivers/filesystems/btrfs/search.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/search.c (working copy)
@@ -0,0 +1,273 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#ifndef __REACTOS__
+#include
+#include
+#include
+#include
+#endif
+
+#include
+#ifndef __REACTOS__
+#include
+#endif
+#include
+
+#ifndef __REACTOS__
+typedef struct _OBJECT_DIRECTORY_INFORMATION {
+ UNICODE_STRING Name;
+ UNICODE_STRING TypeName;
+} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION;
+#endif
+
+#ifdef _GNU_NTIFS_
+#ifdef __REACTOS__
+NTSTATUS WINAPI ZwQueryDirectoryObject(HANDLE DirectoryHandle, PVOID Buffer, ULONG Length,
+ BOOLEAN ReturnSingleEntry, BOOLEAN RestartScan, PULONG Context,
+ PULONG ReturnLength);
+#endif
+#endif
+
+VOID WINAPI IopNotifyPlugPlayNotification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
+ IN LPCGUID Event,
+ IN PVOID EventCategoryData1,
+ IN PVOID EventCategoryData2
+);
+
+static const WCHAR devpath[] = {'\\','D','e','v','i','c','e',0};
+
+static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, PDEVICE_OBJECT DeviceObject) {
+ ULONG tnsize;
+ MOUNTMGR_TARGET_NAME* tn;
+ KEVENT Event;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PIRP Irp;
+ NTSTATUS Status;
+ ULONG mmdltsize;
+ MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
+ MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
+
+ TRACE("found BTRFS volume\n");
+
+ tnsize = sizeof(MOUNTMGR_TARGET_NAME) - sizeof(WCHAR) + us->Length;
+ tn = ExAllocatePoolWithTag(NonPagedPool, tnsize, ALLOC_TAG);
+ tn->DeviceNameLength = us->Length;
+ RtlCopyMemory(tn->DeviceName, us->Buffer, tn->DeviceNameLength);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
+ mountmgr, tn, tnsize,
+ NULL, 0, FALSE, &Event, &IoStatusBlock);
+ if (!Irp) {
+ ERR("IoBuildDeviceIoControlRequest failed\n");
+ return;
+ }
+
+ Status = IoCallDriver(mountmgr, Irp);
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status))
+ ERR("IoCallDriver (1) returned %08x\n", Status);
+
+ ExFreePool(tn);
+
+ mmdltsize = sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) - 1 + us->Length;
+
+ mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
+ mmdlt->DeviceNameLength = us->Length;
+ RtlCopyMemory(&mmdlt->DeviceName, us->Buffer, us->Length);
+ TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
+ mountmgr, mmdlt, mmdltsize,
+ &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock);
+ if (!Irp) {
+ ERR("IoBuildDeviceIoControlRequest failed\n");
+ return;
+ }
+
+ Status = IoCallDriver(mountmgr, Irp);
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status))
+ ERR("IoCallDriver (2) returned %08x\n", Status);
+ else
+ TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
+
+ ExFreePool(mmdlt);
+}
+
+static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_ENTRY* volumes) {
+ KEVENT Event;
+ PIRP Irp;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+ PFILE_OBJECT FileObject;
+ PDEVICE_OBJECT DeviceObject;
+ LARGE_INTEGER Offset;
+ ULONG toread;
+ UINT8* data;
+ UNICODE_STRING us2;
+
+ TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer);
+
+ us2.Length = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + us->Length;
+ us2.MaximumLength = us2.Length;
+ us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG);
+ RtlCopyMemory(us2.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR));
+ us2.Buffer[wcslen(devpath)] = '\\';
+ RtlCopyMemory(&us2.Buffer[wcslen(devpath)+1], us->Buffer, us->Length);
+
+ TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer);
+
+ Status = IoGetDeviceObjectPointer(&us2, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ goto exit;
+ }
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Offset.QuadPart = superblock_addrs[0];
+
+ toread = sector_align(sizeof(superblock), DeviceObject->SectorSize);
+ data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
+
+ if (!Irp) {
+ ERR("IoBuildSynchronousFsdRequest failed\n");
+ goto deref;
+ }
+
+ Status = IoCallDriver(DeviceObject, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+ Status = IoStatusBlock.Status;
+ }
+
+ if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) {
+ superblock* sb = (superblock*)data;
+ volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
+
+ v->devobj = DeviceObject;
+ RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID));
+ RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID));
+ v->devnum = sb->dev_item.dev_id;
+ InsertTailList(volumes, &v->list_entry);
+
+ TRACE("volume found\n");
+ TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7],
+ v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]);
+ TRACE("device uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ v->devuuid.uuid[0], v->devuuid.uuid[1], v->devuuid.uuid[2], v->devuuid.uuid[3], v->devuuid.uuid[4], v->devuuid.uuid[5], v->devuuid.uuid[6], v->devuuid.uuid[7],
+ v->devuuid.uuid[8], v->devuuid.uuid[9], v->devuuid.uuid[10], v->devuuid.uuid[11], v->devuuid.uuid[12], v->devuuid.uuid[13], v->devuuid.uuid[14], v->devuuid.uuid[15]);
+ TRACE("device number %llx\n", v->devnum);
+
+ add_volume(mountmgr, &us2, DeviceObject);
+ }
+
+deref:
+ ExFreePool(data);
+ ObDereferenceObject(FileObject);
+
+exit:
+ ExFreePool(us2.Buffer);
+}
+
+void STDCALL look_for_vols(LIST_ENTRY* volumes) {
+ PFILE_OBJECT FileObject;
+ PDEVICE_OBJECT mountmgr;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING mmdevpath, us;
+ HANDLE h;
+ OBJECT_DIRECTORY_INFORMATION* odi;
+ ULONG odisize;
+ ULONG context;
+ BOOL restart;
+ NTSTATUS Status;
+
+ static const WCHAR hdv[] = {'H','a','r','d','d','i','s','k','V','o','l','u','m','e',0};
+ static const WCHAR device[] = {'D','e','v','i','c','e',0};
+
+ RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+ Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+ return;
+ }
+
+ RtlInitUnicodeString(&us, devpath);
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.Attributes = OBJ_CASE_INSENSITIVE;
+ attr.ObjectName = &us;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ Status = ZwOpenDirectoryObject(&h, DIRECTORY_TRAVERSE, &attr);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwOpenDirectoryObject returned %08x\n", Status);
+ return;
+ }
+
+ odisize = sizeof(OBJECT_DIRECTORY_INFORMATION) * 16;
+ odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG);
+
+ restart = TRUE;
+ do {
+ Status = ZwQueryDirectoryObject(h, odi, odisize, FALSE, restart, &context, NULL/*&retlen*/);
+ restart = FALSE;
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status != STATUS_NO_MORE_ENTRIES)
+ ERR("ZwQueryDirectoryObject returned %08x\n", Status);
+ } else {
+ OBJECT_DIRECTORY_INFORMATION* odi2 = odi;
+
+ while (odi2->Name.Buffer) {
+ if (odi2->TypeName.Length == wcslen(device) * sizeof(WCHAR) &&
+ RtlCompareMemory(odi2->TypeName.Buffer, device, wcslen(device) * sizeof(WCHAR)) == wcslen(device) * sizeof(WCHAR) &&
+ odi2->Name.Length > wcslen(hdv) * sizeof(WCHAR) &&
+ RtlCompareMemory(odi2->Name.Buffer, hdv, wcslen(hdv) * sizeof(WCHAR)) == wcslen(hdv) * sizeof(WCHAR)) {
+ test_vol(mountmgr, &odi2->Name, volumes);
+ }
+ odi2 = &odi2[1];
+ }
+ }
+ } while (NT_SUCCESS(Status));
+
+ ZwClose(h);
+
+ ObDereferenceObject(FileObject);
+}
Index: reactos/drivers/filesystems/btrfs/security.c
===================================================================
--- reactos/drivers/filesystems/btrfs/security.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/security.c (working copy)
@@ -0,0 +1,923 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+typedef struct {
+ UCHAR revision;
+ UCHAR elements;
+ UCHAR auth[6];
+ UINT32 nums[8];
+} sid_header;
+
+static sid_header sid_BA = { 1, 2, SECURITY_NT_AUTHORITY, {32, 544}}; // BUILTIN\Administrators
+static sid_header sid_SY = { 1, 1, SECURITY_NT_AUTHORITY, {18}}; // NT AUTHORITY\SYSTEM
+static sid_header sid_BU = { 1, 2, SECURITY_NT_AUTHORITY, {32, 545}}; // BUILTIN\Users
+static sid_header sid_AU = { 1, 1, SECURITY_NT_AUTHORITY, {11}}; // NT AUTHORITY\Authenticated Users
+
+typedef struct {
+ UCHAR flags;
+ ACCESS_MASK mask;
+ sid_header* sid;
+} dacl;
+
+static dacl def_dacls[] = {
+ { 0, FILE_ALL_ACCESS, &sid_BA },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_BA },
+ { 0, FILE_ALL_ACCESS, &sid_SY },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_ALL_ACCESS, &sid_SY },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, &sid_BU },
+ { OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE, &sid_AU },
+ { 0, FILE_ADD_SUBDIRECTORY, &sid_AU },
+ // FIXME - Mandatory Label\High Mandatory Level:(OI)(NP)(IO)(NW)
+ { 0, 0, NULL }
+};
+
+extern LIST_ENTRY uid_map_list;
+
+// UINT32 STDCALL get_uid() {
+// PACCESS_TOKEN at = PsReferencePrimaryToken(PsGetCurrentProcess());
+// HANDLE h;
+// ULONG len, size;
+// TOKEN_USER* tu;
+// NTSTATUS Status;
+// UINT32 uid = UID_NOBODY;
+// LIST_ENTRY* le;
+// uid_map* um;
+// // char s[256];
+//
+// Status = ObOpenObjectByPointer(at, OBJ_KERNEL_HANDLE, NULL, GENERIC_READ, NULL, KernelMode, &h);
+// if (!NT_SUCCESS(Status)) {
+// ERR("ObOpenObjectByPointer returned %08x\n", Status);
+// goto exit;
+// }
+//
+// Status = ZwQueryInformationToken(h, TokenUser, NULL, 0, &len);
+// if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+// ERR("ZwQueryInformationToken(1) returned %08x (len = %u)\n", Status, len);
+// goto exit2;
+// }
+//
+// // TRACE("len = %u\n", len);
+//
+// tu = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+//
+// Status = ZwQueryInformationToken(h, TokenUser, tu, len, &len);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("ZwQueryInformationToken(2) returned %08x\n", Status);
+// goto exit3;
+// }
+//
+// size = 8 + (4 * ((sid_header*)tu->User.Sid)->elements);
+//
+// le = uid_map_list.Flink;
+// while (le != &uid_map_list) {
+// um = CONTAINING_RECORD(le, uid_map, listentry);
+//
+// if (((sid_header*)um->sid)->elements == ((sid_header*)tu->User.Sid)->elements &&
+// RtlCompareMemory(um->sid, tu->User.Sid, size) == size) {
+// uid = um->uid;
+// break;
+// }
+//
+// le = le->Flink;
+// }
+//
+// // sid_to_string(tu->User.Sid, s);
+//
+// // TRACE("got SID: %s\n", s);
+// TRACE("uid = %u\n", uid);
+//
+// exit3:
+// ExFreePool(tu);
+//
+// exit2:
+// ZwClose(h);
+//
+// exit:
+// PsDereferencePrimaryToken(at);
+//
+// return uid;
+// }
+
+void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) {
+ unsigned int i, np;
+ UINT8 numdashes;
+ UINT64 val;
+ ULONG sidsize;
+ sid_header* sid;
+ uid_map* um;
+// char s[255];
+
+ if (sidstringlength < 4 ||
+ sidstring[0] != 'S' ||
+ sidstring[1] != '-' ||
+ sidstring[2] != '1' ||
+ sidstring[3] != '-') {
+ ERR("invalid SID\n");
+ return;
+ }
+
+ sidstring = &sidstring[4];
+ sidstringlength -= 4;
+
+ numdashes = 0;
+ for (i = 0; i < sidstringlength; i++) {
+ if (sidstring[i] == '-') {
+ numdashes++;
+ sidstring[i] = 0;
+ }
+ }
+
+ sidsize = 8 + (numdashes * 4);
+ sid = ExAllocatePoolWithTag(PagedPool, sidsize, ALLOC_TAG);
+
+ sid->revision = 0x01;
+ sid->elements = numdashes;
+
+ np = 0;
+ while (sidstringlength > 0) {
+ val = 0;
+ i = 0;
+ while (sidstring[i] != '-' && i < sidstringlength) {
+ if (sidstring[i] >= '0' && sidstring[i] <= '9') {
+ val *= 10;
+ val += sidstring[i] - '0';
+ } else
+ break;
+
+ i++;
+ }
+
+ i++;
+ TRACE("val = %u, i = %u, ssl = %u\n", (UINT32)val, i, sidstringlength);
+
+ if (np == 0) {
+ sid->auth[0] = (val & 0xff0000000000) >> 40;
+ sid->auth[1] = (val & 0xff00000000) >> 32;
+ sid->auth[2] = (val & 0xff000000) >> 24;
+ sid->auth[3] = (val & 0xff0000) >> 16;
+ sid->auth[4] = (val & 0xff00) >> 8;
+ sid->auth[5] = val & 0xff;
+ } else {
+ sid->nums[np-1] = (UINT32)val;
+ }
+
+ np++;
+
+ if (sidstringlength > i) {
+ sidstringlength -= i;
+
+ sidstring = &sidstring[i];
+ } else
+ break;
+ }
+
+// sid_to_string(sid, s);
+
+// TRACE("%s\n", s);
+ um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
+ um->sid = sid;
+ um->uid = uid;
+
+ InsertTailList(&uid_map_list, &um->listentry);
+}
+
+static void uid_to_sid(UINT32 uid, PSID* sid) {
+ LIST_ENTRY* le;
+ uid_map* um;
+ sid_header* sh;
+ UCHAR els;
+
+ le = uid_map_list.Flink;
+ while (le != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ if (um->uid == uid) {
+ *sid = ExAllocatePoolWithTag(PagedPool, RtlLengthSid(um->sid), ALLOC_TAG);
+ RtlCopyMemory(*sid, um->sid, RtlLengthSid(um->sid));
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ if (uid == 0) { // root
+ // FIXME - find actual Administrator account, rather than SYSTEM (S-1-5-18)
+ // (of form S-1-5-21-...-500)
+
+ els = 1;
+
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = els;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 5;
+
+ sh->nums[0] = 18;
+ } else {
+ // fallback to S-1-22-1-X, Samba's SID scheme
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = 2;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 22;
+
+ sh->nums[0] = 1;
+ sh->nums[1] = uid;
+ }
+
+ *sid = sh;
+}
+
+static UINT32 sid_to_uid(PSID sid) {
+ LIST_ENTRY* le;
+ uid_map* um;
+ sid_header* sh = sid;
+
+ le = uid_map_list.Flink;
+ while (le != &uid_map_list) {
+ um = CONTAINING_RECORD(le, uid_map, listentry);
+
+ if (RtlEqualSid(sid, um->sid))
+ return um->uid;
+
+ le = le->Flink;
+ }
+
+ if (RtlEqualSid(sid, &sid_SY))
+ return 0; // root
+
+ // Samba's SID scheme: S-1-22-1-X
+ if (sh->revision == 1 && sh->elements == 2 && sh->auth[0] == 0 && sh->auth[1] == 0 && sh->auth[2] == 0 && sh->auth[3] == 0 &&
+ sh->auth[4] == 0 && sh->auth[5] == 22 && sh->nums[0] == 1)
+ return sh->nums[1];
+
+ return UID_NOBODY;
+}
+
+static void gid_to_sid(UINT32 gid, PSID* sid) {
+ sid_header* sh;
+ UCHAR els;
+
+ // FIXME - do this properly?
+
+ // fallback to S-1-22-2-X, Samba's SID scheme
+ els = 2;
+ sh = ExAllocatePoolWithTag(PagedPool, sizeof(sid_header) + ((els - 1) * sizeof(UINT32)), ALLOC_TAG);
+
+ sh->revision = 1;
+ sh->elements = els;
+
+ sh->auth[0] = 0;
+ sh->auth[1] = 0;
+ sh->auth[2] = 0;
+ sh->auth[3] = 0;
+ sh->auth[4] = 0;
+ sh->auth[5] = 22;
+
+ sh->nums[0] = 2;
+ sh->nums[1] = gid;
+
+ *sid = sh;
+}
+
+static ACL* load_default_acl() {
+ ULONG size;
+ ACL* acl;
+ ACCESS_ALLOWED_ACE* aaa;
+ UINT32 i;
+
+ size = sizeof(ACL);
+ i = 0;
+ while (def_dacls[i].sid) {
+ size += sizeof(ACCESS_ALLOWED_ACE);
+ size += 8 + (def_dacls[i].sid->elements * sizeof(UINT32)) - sizeof(ULONG);
+ i++;
+ }
+
+ acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+ acl->AclRevision = ACL_REVISION;
+ acl->Sbz1 = 0;
+ acl->AclSize = size;
+ acl->AceCount = i;
+ acl->Sbz2 = 0;
+
+ aaa = (ACCESS_ALLOWED_ACE*)&acl[1];
+ i = 0;
+ while (def_dacls[i].sid) {
+ aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ aaa->Header.AceFlags = def_dacls[i].flags;
+ aaa->Header.AceSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + 8 + (def_dacls[i].sid->elements * sizeof(UINT32));
+ aaa->Mask = def_dacls[i].mask;
+
+ RtlCopyMemory(&aaa->SidStart, def_dacls[i].sid, 8 + (def_dacls[i].sid->elements * sizeof(UINT32)));
+
+ aaa = (ACCESS_ALLOWED_ACE*)((UINT8*)aaa + aaa->Header.AceSize);
+
+ i++;
+ }
+
+ return acl;
+}
+
+static ACL* inherit_acl(SECURITY_DESCRIPTOR* parsd, BOOL file) {
+ ULONG size;
+ NTSTATUS Status;
+ ACL *paracl, *acl;
+ BOOLEAN parhasdacl, pardefaulted;
+ ACE_HEADER *ah, *parah;
+ UINT32 i;
+ USHORT num_aces;
+
+ // FIXME - replace this with SeAssignSecurity
+
+ Status = RtlGetDaclSecurityDescriptor(parsd, &parhasdacl, ¶cl, &pardefaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetDaclSecurityDescriptor returned %08x\n", Status);
+ return NULL;
+ }
+
+ // FIXME - handle parhasdacl == FALSE
+
+// OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
+ num_aces = 0;
+ size = sizeof(ACL);
+ ah = (ACE_HEADER*)¶cl[1];
+ for (i = 0; i < paracl->AceCount; i++) {
+ if (!file && ah->AceFlags & CONTAINER_INHERIT_ACE) {
+ num_aces++;
+ size += ah->AceSize;
+
+ if (ah->AceFlags & INHERIT_ONLY_ACE) {
+ num_aces++;
+ size += ah->AceSize;
+ }
+ }
+
+ if (ah->AceFlags & OBJECT_INHERIT_ACE && (file || !(ah->AceFlags & CONTAINER_INHERIT_ACE))) {
+ num_aces++;
+ size += ah->AceSize;
+ }
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+ acl->AclRevision = ACL_REVISION;
+ acl->Sbz1 = 0;
+ acl->AclSize = size;
+ acl->AceCount = num_aces;
+ acl->Sbz2 = 0;
+
+ ah = (ACE_HEADER*)&acl[1];
+ parah = (ACE_HEADER*)¶cl[1];
+ for (i = 0; i < paracl->AceCount; i++) {
+ if (!file && parah->AceFlags & CONTAINER_INHERIT_ACE) {
+ if (parah->AceFlags & INHERIT_ONLY_ACE) {
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags &= ~INHERIT_ONLY_ACE;
+ ah->AceFlags |= INHERITED_ACE;
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags |= INHERITED_ACE;
+
+ if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE)
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ if (parah->AceFlags & OBJECT_INHERIT_ACE && (file || !(parah->AceFlags & CONTAINER_INHERIT_ACE))) {
+ RtlCopyMemory(ah, parah, parah->AceSize);
+ ah->AceFlags |= INHERITED_ACE;
+
+ if (file)
+ ah->AceFlags &= ~INHERIT_ONLY_ACE;
+ else
+ ah->AceFlags |= INHERIT_ONLY_ACE;
+
+ if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE || file)
+ ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+ ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
+ }
+
+ parah = (ACE_HEADER*)((UINT8*)parah + parah->AceSize);
+ }
+
+ return acl;
+}
+
+// static void STDCALL sid_to_string(PSID sid, char* s) {
+// sid_header* sh = (sid_header*)sid;
+// LARGE_INTEGER authnum;
+// UINT8 i;
+//
+// authnum.LowPart = sh->auth[5] | (sh->auth[4] << 8) | (sh->auth[3] << 16) | (sh->auth[2] << 24);
+// authnum.HighPart = sh->auth[1] | (sh->auth[0] << 8);
+//
+// sprintf(s, "S-%u-%u", sh->revision, (UINT32)authnum.QuadPart);
+//
+// for (i = 0; i < sh->elements; i++) {
+// sprintf(s, "%s-%u", s, sh->nums[i]);
+// }
+// }
+
+static BOOL get_sd_from_xattr(fcb* fcb) {
+ ULONG buflen;
+ NTSTATUS Status;
+ PSID sid, usersid;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen))
+ return FALSE;
+
+ TRACE("using xattr " EA_NTACL " for security descriptor\n");
+
+ if (fcb->inode_item.st_uid != UID_NOBODY) {
+ BOOLEAN defaulted;
+
+ Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &sid, &defaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ } else {
+ uid_to_sid(fcb->inode_item.st_uid, &usersid);
+
+ if (!RtlEqualSid(sid, usersid)) {
+ SECURITY_DESCRIPTOR *newsd, *newsd2;
+ ULONG sdsize, daclsize, saclsize, ownersize, groupsize;
+ ACL *dacl, *sacl;
+ PSID owner, group;
+
+ sdsize = daclsize = saclsize = ownersize = groupsize = 0;
+
+ Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, NULL, &sdsize, NULL, &daclsize, NULL, &saclsize, NULL, &ownersize, NULL, &groupsize);
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlSelfRelativeToAbsoluteSD 1 returned %08x\n", Status);
+ }
+
+ ERR("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize);
+
+ newsd2 = sdsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, sdsize, ALLOC_TAG);
+ dacl = daclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, daclsize, ALLOC_TAG);
+ sacl = saclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, saclsize, ALLOC_TAG);
+ owner = ownersize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, ownersize, ALLOC_TAG);
+ group = groupsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, groupsize, ALLOC_TAG);
+
+ Status = RtlSelfRelativeToAbsoluteSD(fcb->sd, newsd2, &sdsize, dacl, &daclsize, sacl, &saclsize, owner, &ownersize, group, &groupsize);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSelfRelativeToAbsoluteSD returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ Status = RtlSetOwnerSecurityDescriptor(newsd2, usersid, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ buflen = 0;
+ Status = RtlAbsoluteToSelfRelativeSD(newsd2, NULL, &buflen);
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ if (buflen == 0 || NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ return FALSE;
+ }
+
+ newsd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
+
+ Status = RtlAbsoluteToSelfRelativeSD(newsd2, newsd, &buflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ ExFreePool(usersid);
+ ExFreePool(newsd);
+ return FALSE;
+ }
+
+ ExFreePool(fcb->sd);
+
+ fcb->sd = newsd;
+
+ if (newsd2) ExFreePool(newsd2);
+ if (dacl) ExFreePool(dacl);
+ if (sacl) ExFreePool(sacl);
+ if (owner) ExFreePool(owner);
+ if (group) ExFreePool(group);
+ }
+
+ ExFreePool(usersid);
+ }
+ }
+
+ // FIXME - check GID here if not GID_NOBODY
+
+ return TRUE;
+}
+
+void fcb_get_sd(fcb* fcb) {
+ NTSTATUS Status;
+ SECURITY_DESCRIPTOR sd;
+ ULONG buflen;
+ ACL* acl = NULL;
+ PSID usersid = NULL, groupsid = NULL;
+
+ if (get_sd_from_xattr(fcb))
+ goto end;
+
+ Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+// if (fcb->inode_item.st_uid != UID_NOBODY) {
+ uid_to_sid(fcb->inode_item.st_uid, &usersid);
+ RtlSetOwnerSecurityDescriptor(&sd, usersid, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+// }
+
+// if (fcb->inode_item.st_gid != GID_NOBODY) {
+ gid_to_sid(fcb->inode_item.st_gid, &groupsid);
+ RtlSetGroupSecurityDescriptor(&sd, groupsid, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetGroupSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+// }
+
+ if (!fcb->par)
+ acl = load_default_acl();
+ else
+ acl = inherit_acl(fcb->par->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
+
+ Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, acl, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetDaclSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ // FIXME - SACL_SECURITY_INFORMATION
+
+ buflen = 0;
+
+ // get sd size
+ Status = RtlAbsoluteToSelfRelativeSD(&sd, NULL, &buflen);
+ if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlAbsoluteToSelfRelativeSD 1 returned %08x\n", Status);
+ goto end;
+ }
+
+// fcb->sdlen = buflen;
+
+ if (buflen == 0 || Status == STATUS_SUCCESS) {
+ TRACE("RtlAbsoluteToSelfRelativeSD said SD is zero-length\n");
+ goto end;
+ }
+
+ fcb->sd = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
+
+ Status = RtlAbsoluteToSelfRelativeSD(&sd, fcb->sd, &buflen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlAbsoluteToSelfRelativeSD 2 returned %08x\n", Status);
+ goto end;
+ }
+
+end:
+ if (acl)
+ ExFreePool(acl);
+
+ if (usersid)
+ ExFreePool(usersid);
+
+ if (groupsid)
+ ExFreePool(groupsid);
+}
+
+static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
+ NTSTATUS Status;
+ fcb* fcb = FileObject->FsContext;
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+// TRACE("buflen = %u, fcb->sdlen = %u\n", *buflen, fcb->sdlen);
+
+ // Why (void**)? Is this a bug in mingw?
+ Status = SeQuerySecurityDescriptorInfo(&flags, relsd, buflen, (void**)&fcb->sd);
+
+ if (Status == STATUS_BUFFER_TOO_SMALL)
+ TRACE("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
+ else if (!NT_SUCCESS(Status))
+ ERR("SeQuerySecurityDescriptorInfo returned %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ SECURITY_DESCRIPTOR* sd;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG buflen;
+ BOOL top_level;
+
+ TRACE("query security\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Information = 0;
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
+ TRACE("OWNER_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
+ TRACE("GROUP_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
+ TRACE("DACL_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
+ TRACE("SACL_SECURITY_INFORMATION\n");
+
+ TRACE("length = %u\n", IrpSp->Parameters.QuerySecurity.Length);
+
+ sd = map_user_buffer(Irp);
+// sd = Irp->AssociatedIrp.SystemBuffer;
+ TRACE("sd = %p\n", sd);
+
+ buflen = IrpSp->Parameters.QuerySecurity.Length;
+
+ Status = get_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
+
+ if (NT_SUCCESS(Status))
+ Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
+ else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ Irp->IoStatus.Information = buflen;
+ Status = STATUS_BUFFER_OVERFLOW;
+ } else
+ Irp->IoStatus.Information = 0;
+
+ TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ TRACE("returning %08x\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, SECURITY_INFORMATION flags) {
+ NTSTATUS Status;
+ fcb* fcb = FileObject->FsContext;
+ SECURITY_DESCRIPTOR* oldsd;
+ INODE_ITEM* ii;
+ KEY searchkey;
+ traverse_ptr tp;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, flags);
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ if (fcb->ads)
+ fcb = fcb->par;
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ oldsd = fcb->sd;
+
+ Status = SeSetSecurityDescriptorInfo(NULL, &flags, sd, (void**)&fcb->sd, PagedPool, IoGetFileObjectGenericMapping());
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
+ goto end;
+ }
+
+ ExFreePool(oldsd);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ free_traverse_ptr(&tp);
+ goto end;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.sequence++;
+
+ if (flags & OWNER_SECURITY_INFORMATION) {
+ PSID owner;
+ BOOLEAN defaulted;
+
+ Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_uid = sid_to_uid(owner);
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL)) {
+ ERR("error - failed to insert INODE_ITEM\n");
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(ii);
+ goto end;
+ }
+
+ set_xattr(Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd));
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ Status = consider_write(Vcb);
+
+end:
+ release_tree_lock(Vcb, TRUE);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ BOOL top_level;
+
+ TRACE("set security\n");
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Information = 0;
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
+ TRACE("OWNER_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
+ TRACE("GROUP_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
+ TRACE("DACL_SECURITY_INFORMATION\n");
+
+ if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
+ TRACE("SACL_SECURITY_INFORMATION\n");
+
+ Status = set_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
+ IrpSp->Parameters.SetSecurity.SecurityInformation);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ TRACE("returning %08x\n", Status);
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as) {
+ NTSTATUS Status;
+ PSID owner;
+ BOOLEAN defaulted;
+
+ Status = SeAssignSecurity(fcb->par ? fcb->par->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
+ &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeAssignSecurity returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ fcb->inode_item.st_uid = UID_NOBODY;
+ } else {
+ fcb->inode_item.st_uid = sid_to_uid(&owner);
+ }
+
+ return STATUS_SUCCESS;
+}
Index: reactos/drivers/filesystems/btrfs/treefuncs.c
===================================================================
--- reactos/drivers/filesystems/btrfs/treefuncs.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/treefuncs.c (working copy)
@@ -0,0 +1,1093 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+// #define DEBUG_TREE_LOCKS
+
+enum read_tree_status {
+ ReadTreeStatus_Pending,
+ ReadTreeStatus_Success,
+ ReadTreeStatus_Cancelled,
+ ReadTreeStatus_Error,
+ ReadTreeStatus_CRCError,
+ ReadTreeStatus_MissingDevice
+};
+
+struct read_tree_context;
+
+typedef struct {
+ struct read_tree_context* context;
+ UINT8* buf;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum read_tree_status status;
+} read_tree_stripe;
+
+typedef struct {
+ KEVENT Event;
+ NTSTATUS Status;
+ chunk* c;
+// UINT8* buf;
+ UINT32 buflen;
+ UINT64 num_stripes, stripes_left;
+ UINT64 type;
+ read_tree_stripe* stripes;
+} read_tree_context;
+
+static NTSTATUS STDCALL read_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ read_tree_stripe* stripe = conptr;
+ read_tree_context* context = (read_tree_context*)stripe->context;
+ UINT64 i;
+
+ if (stripe->status == ReadTreeStatus_Cancelled)
+ goto end;
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ tree_header* th = (tree_header*)stripe->buf;
+ UINT32 crc32;
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
+
+ if (crc32 == *((UINT32*)th->csum)) {
+ stripe->status = ReadTreeStatus_Success;
+
+ for (i = 0; i < context->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Pending) {
+ context->stripes[i].status = ReadTreeStatus_Cancelled;
+ IoCancelIrp(context->stripes[i].Irp);
+ }
+ }
+
+ goto end;
+ } else
+ stripe->status = ReadTreeStatus_CRCError;
+ } else {
+ stripe->status = ReadTreeStatus_Error;
+ }
+
+end:
+ if (context->stripes_left == 1)
+ KeSetEvent(&context->Event, 0, FALSE);
+ else
+ context->stripes_left--;
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ read_tree_context* context;
+ UINT64 i/*, type*/, offset;
+ NTSTATUS Status;
+ device** devices;
+
+ // FIXME - make this work with RAID
+
+ if (Vcb->log_to_phys_loaded) {
+ chunk* c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ci = c->chunk_item;
+ offset = c->offset;
+ devices = c->devices;
+ } else {
+ LIST_ENTRY* le = Vcb->sys_chunks.Flink;
+
+ ci = NULL;
+
+ while (le != &Vcb->sys_chunks) {
+ sys_chunk* sc = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ if (sc->key.obj_id == 0x100 && sc->key.obj_type == TYPE_CHUNK_ITEM && sc->key.offset <= addr) {
+ CHUNK_ITEM* chunk_item = sc->data;
+
+ if ((addr - sc->key.offset) < chunk_item->size && chunk_item->num_stripes > 0) {
+ ci = chunk_item;
+ offset = sc->key.offset;
+ cis = (CHUNK_ITEM_STRIPE*)&chunk_item[1];
+
+ devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * ci->num_stripes, ALLOC_TAG);
+ for (i = 0; i < ci->num_stripes; i++) {
+ devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
+ }
+
+ break;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (!ci) {
+ ERR("could not find chunk for %llx in bootstrap\n", addr);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+// if (ci->type & BLOCK_FLAG_DUPLICATE) {
+// type = BLOCK_FLAG_DUPLICATE;
+// } else if (ci->type & BLOCK_FLAG_RAID0) {
+// FIXME("RAID0 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID1) {
+// FIXME("RAID1 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID10) {
+// FIXME("RAID10 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID5) {
+// FIXME("RAID5 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else if (ci->type & BLOCK_FLAG_RAID6) {
+// FIXME("RAID6 not yet supported\n");
+// return STATUS_NOT_IMPLEMENTED;
+// } else { // SINGLE
+// type = 0;
+// }
+
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(read_tree_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_stripe) * ci->num_stripes, ALLOC_TAG);
+ RtlZeroMemory(context->stripes, sizeof(read_tree_stripe) * ci->num_stripes);
+
+ context->buflen = Vcb->superblock.node_size;
+ context->num_stripes = ci->num_stripes;
+ context->stripes_left = context->num_stripes;
+// context->type = type;
+
+ // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ if (!devices[i]) {
+ context->stripes[i].status = ReadTreeStatus_MissingDevice;
+ context->stripes[i].buf = NULL;
+ context->stripes_left--;
+ } else {
+ context->stripes[i].context = (struct read_tree_context*)context;
+ context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+
+ context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+
+ if (!context->stripes[i].Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+ IrpSp->MajorFunction = IRP_MJ_READ;
+
+ if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+ FIXME("FIXME - buffered IO\n");
+ } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, Vcb->superblock.node_size, FALSE, FALSE, NULL);
+ if (!context->stripes[i].Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
+ }
+
+ IrpSp->Parameters.Read.Length = Vcb->superblock.node_size;
+ IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
+
+ context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+
+ IoSetCompletionRoutine(context->stripes[i].Irp, read_tree_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+
+ context->stripes[i].status = ReadTreeStatus_Pending;
+ }
+ }
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status != ReadTreeStatus_MissingDevice) {
+ IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
+ }
+ }
+
+ KeWaitForSingleObject(&context->Event, /*Executive*/Suspended, KernelMode, FALSE, NULL);
+
+ // FIXME - if checksum error, write good data over bad
+
+ // check if any of the stripes succeeded
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Success) {
+ RtlCopyMemory(buf, context->stripes[i].buf, Vcb->superblock.node_size);
+ Status = STATUS_SUCCESS;
+ goto exit;
+ }
+ }
+
+ // if not, see if we got a checksum error
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_CRCError) {
+#if DEBUG_LEVEL >= 2
+ tree_header* th = (tree_header*)context->stripes[i].buf;
+ UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
+// UINT64 j;
+
+ WARN("stripe %llu had a checksum error\n", i);
+ WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
+#endif
+
+// for (j = 0; j < ci->num_stripes; j++) {
+// WARN("stripe %llu: device = %p, status = %u\n", j, c->devices[j], context->stripes[j].status);
+// }
+// int3;
+
+ Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+ goto exit;
+ }
+ }
+
+ // failing that, return the first error we encountered
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Error) {
+ Status = context->stripes[i].iosb.Status;
+ goto exit;
+ }
+ }
+
+ // if we somehow get here, return STATUS_INTERNAL_ERROR
+
+ Status = STATUS_INTERNAL_ERROR;
+
+// for (i = 0; i < ci->num_stripes; i++) {
+// ERR("%llx: status = %u, NTSTATUS = %08x\n", i, context->stripes[i].status, context->stripes[i].iosb.Status);
+// }
+exit:
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].Irp) {
+ if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(context->stripes[i].Irp->MdlAddress);
+ IoFreeMdl(context->stripes[i].Irp->MdlAddress);
+ }
+ IoFreeIrp(context->stripes[i].Irp);
+ }
+
+ if (context->stripes[i].buf)
+ ExFreePool(context->stripes[i].buf);
+ }
+
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ if (!Vcb->log_to_phys_loaded)
+ ExFreePool(devices);
+
+ return Status;
+}
+
+tree* STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, const char* func, const char* file, unsigned int line) {
+ UINT8* buf;
+ NTSTATUS Status;
+ tree_header* th;
+ tree* t;
+ tree_data* td;
+ chunk* c;
+
+ TRACE("(%p, %llx)\n", Vcb, addr);
+
+ buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+
+ Status = read_tree(Vcb, addr, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_tree returned 0x%08x\n", Status);
+ ExFreePool(buf);
+ return NULL;
+ }
+
+ th = (tree_header*)buf;
+
+ t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&t->header, th, sizeof(tree_header));
+// t->address = addr;
+// t->level = th->level;
+ t->refcount = 1;
+ t->Vcb = Vcb;
+ t->parent = NULL;
+ t->root = r;
+// t->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ t->paritem = NULL;
+ t->size = 0;
+ t->new_address = 0;
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "loaded tree %p (%llx)\n", t, addr);
+#else
+ _debug_message(func, "loaded tree %p (%llx)\n", t, addr);
+#endif
+#endif
+
+ c = get_chunk_from_address(Vcb, addr);
+
+ if (c)
+ t->flags = c->chunk_item->type;
+ else
+ t->flags = 0;
+
+// ExInitializeResourceLite(&t->nonpaged->load_tree_lock);
+
+// t->items = ExAllocatePoolWithTag(PagedPool, num_items * sizeof(tree_data), ALLOC_TAG);
+ InitializeListHead(&t->itemlist);
+
+ if (t->header.level == 0) { // leaf node
+ leaf_node* ln = (leaf_node*)(buf + sizeof(tree_header));
+ unsigned int i;
+
+ for (i = 0; i < t->header.num_items; i++) {
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = ln[i].key;
+// TRACE("load_tree: leaf item %u (%x,%x,%x)\n", i, (UINT32)ln[i].key.obj_id, ln[i].key.obj_type, (UINT32)ln[i].key.offset);
+
+ td->data = ExAllocatePoolWithTag(PagedPool, ln[i].size, ALLOC_TAG);
+ RtlCopyMemory(td->data, buf + sizeof(tree_header) + ln[i].offset, ln[i].size);
+ td->size = ln[i].size;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+
+ InsertTailList(&t->itemlist, &td->list_entry);
+
+ t->size += ln[i].size;
+ }
+
+ t->size += t->header.num_items * sizeof(leaf_node);
+ } else {
+ internal_node* in = (internal_node*)(buf + sizeof(tree_header));
+ unsigned int i;
+
+ for (i = 0; i < t->header.num_items; i++) {
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = in[i].key;
+// TRACE("load_tree: internal item %u (%x,%x,%x)\n", i, (UINT32)in[i].key.obj_id, in[i].key.obj_type, (UINT32)in[i].key.offset);
+
+ td->treeholder.address = in[i].address;
+ td->treeholder.generation = in[i].generation;
+ td->treeholder.tree = NULL;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_unloaded;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+
+ InsertTailList(&t->itemlist, &td->list_entry);
+ }
+
+ t->size = t->header.num_items * sizeof(internal_node);
+ }
+
+ ExFreePool(buf);
+
+ InterlockedIncrement(&Vcb->open_trees);
+ InsertTailList(&Vcb->trees, &t->list_entry);
+
+ TRACE("returning %p\n", t);
+
+ return t;
+}
+
+static tree* free_tree2(tree* t, const char* func, const char* file, unsigned int line) {
+ LONG rc;
+ LIST_ENTRY* le;
+ tree_data* td;
+ tree* par;
+
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("(%p)\n", t);
+#endif
+
+ par = t->parent;
+
+// if (par) ExAcquireResourceExclusiveLite(&par->nonpaged->load_tree_lock, TRUE);
+
+ rc = InterlockedDecrement(&t->refcount);
+
+#ifdef DEBUG_TREE_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
+#else
+ _debug_message(func, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
+#endif
+#endif
+
+ if (rc < 0) {
+ ERR("error - negative refcount (%i)\n", rc);
+ int3;
+ }
+
+ if (rc == 0) {
+ root* r = t->root;
+ if (r && r->treeholder.tree != t)
+ r = NULL;
+
+// if (r) {
+// FsRtlEnterFileSystem();
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+// }
+
+ if (par) {
+ if (t->paritem)
+ t->paritem->treeholder.tree = NULL;
+
+// ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
+ }
+
+ if (t->parent)
+ t->parent = free_tree2(t->parent, func, file, line);
+
+// ExDeleteResourceLite(&t->nonpaged->load_tree_lock);
+
+// ExFreePool(t->nonpaged);
+
+#ifndef __REACTOS__
+ while ((le = RemoveHeadList(&t->itemlist)) != &t->itemlist) {
+#else
+ while (!IsListEmpty(&t->itemlist)) {
+ le = RemoveHeadList(&t->itemlist);
+#endif
+ td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (t->header.level == 0 && td->data)
+ ExFreePool(td->data);
+
+ ExFreePool(td);
+ }
+
+ InterlockedDecrement(&t->Vcb->open_trees);
+ RemoveEntryList(&t->list_entry);
+
+ if (r) {
+ r->treeholder.tree = NULL;
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// FsRtlExitFileSystem();
+ }
+
+ ExFreePool(t);
+
+ return NULL;
+ } else {
+// if (par) ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
+ }
+
+ return t;
+}
+
+BOOL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, const char* func, const char* file, unsigned int line) {
+// KIRQL irql;
+// tree_holder_nonpaged* thnp = th->nonpaged;
+ BOOL ret;
+
+// ExAcquireResourceExclusiveLite(&thnp->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+// KeAcquireSpinLock(&thnp->spin_lock, &irql);
+//
+// if (thnp->status == tree_header_loading) {
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// // FIXME - wait for Event
+// } else if (thnp->status == tree_header_unloaded || thnp->status == tree_header_unloading) {
+// if (thnp->status == tree_header_unloading) {
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+// // FIXME - wait for Event
+// }
+//
+// // FIXME - change status
+// thnp->status = tree_header_loading;
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// // FIXME - load
+// // FIXME - change status
+// // FIXME - trigger event
+// } else if (thnp->status == tree_header_loaded) {
+// _increase_tree_rc(th->tree, func, file, line);
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+//
+// ret = FALSE;
+// }
+
+ if (!th->tree) {
+ th->tree = _load_tree(Vcb, th->address, r, func, file, line);
+ th->tree->parent = t;
+ th->tree->paritem = td;
+
+ ret = TRUE;
+ } else {
+ _increase_tree_rc(th->tree, func, file, line);
+
+ ret = FALSE;
+ }
+
+// KeReleaseSpinLock(&thnp->spin_lock, irql);
+
+// ExReleaseResourceLite(&thnp->lock);
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+
+ return ret;
+}
+
+tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line) {
+ tree* ret;
+ root* r = t->root;
+
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+ ret = free_tree2(t, func, file, line);
+
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+
+ return ret;
+}
+
+static __inline tree_data* first_item(tree* t) {
+ LIST_ENTRY* le = t->itemlist.Flink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static __inline tree_data* next_item(tree* t, tree_data* td) {
+ LIST_ENTRY* le = td->list_entry.Flink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static BOOL STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ int cmp;
+ tree_data *td, *lasttd;
+
+ TRACE("(%p, %p, %p, %p, %p, %u)\n", Vcb, t, tp, searchkey, tc, ignore);
+
+ cmp = 1;
+ td = first_item(t);
+ lasttd = NULL;
+
+ do {
+ cmp = keycmp(searchkey, &td->key);
+// TRACE("(%u) comparing (%x,%x,%x) to (%x,%x,%x) - %i (ignore = %s)\n", t->header.level, (UINT32)searchkey->obj_id, searchkey->obj_type, (UINT32)searchkey->offset, (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset, cmp, td->ignore ? "TRUE" : "FALSE");
+ if (cmp == 1) {
+ if (!td->ignore || ignore)
+ lasttd = td;
+ td = next_item(t, td);
+ }
+
+ if (t->header.level == 0 && !ignore && td && td->ignore) {
+ while (td && td->ignore)
+ td = next_item(t, td);
+
+ if (td)
+ cmp = keycmp(searchkey, &td->key);
+ }
+ } while (td && cmp == 1);
+
+ if ((cmp == -1 || !td) && lasttd)
+ td = lasttd;
+
+ if (t->header.level == 0) {
+ tp->tree = t;
+ _increase_tree_rc(t, func, file, line);
+ tp->item = td;
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+ return TRUE;
+ } else {
+ BOOL b;
+
+// if (i > 0)
+// TRACE("entering tree from (%x,%x,%x) to (%x,%x,%x) (%p)\n", (UINT32)t->items[i].key.obj_id, t->items[i].key.obj_type, (UINT32)t->items[i].key.offset, (UINT32)t->items[i+1].key.obj_id, t->items[i+1].key.obj_type, (UINT32)t->items[i+1].key.offset, t->items[i].tree);
+
+ if (_do_load_tree(Vcb, &td->treeholder, t->root, t, td, func, file, line))
+ _increase_tree_rc(t, func, file, line);
+
+ b = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
+
+ td->treeholder.tree = _free_tree(td->treeholder.tree, func, file, line);
+ TRACE("tree now %p\n", td->treeholder.tree);
+
+ return b;
+ }
+}
+
+BOOL STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ BOOL b;
+// KIRQL irql;
+
+ TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
+
+// KeAcquireSpinLock(&r->load_tree_lock, &irql);
+
+ _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, func, file, line);
+
+// KeReleaseSpinLock(&r->load_tree_lock, irql);
+
+ b = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
+
+// FsRtlEnterFileSystem();
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+ /*r->treeholder.tree = */_free_tree(r->treeholder.tree, func, file, line);
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// FsRtlExitFileSystem();
+
+#ifdef DEBUG_PARANOID
+ if (b && !ignore && tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return b;
+}
+
+void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line) {
+ if (tp->tree) {
+ tp->tree = free_tree2(tp->tree, func, file, line);
+ }
+}
+
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ tree* t;
+ tree_data *td, *next;
+
+ next = next_item(tp->tree, tp->item);
+
+ if (!ignore) {
+ while (next && next->ignore)
+ next = next_item(tp->tree, next);
+ }
+
+ if (next) {
+ next_tp->tree = tp->tree;
+ _increase_tree_rc(next_tp->tree, func, file, line);
+ next_tp->item = next;
+
+#ifdef DEBUG_PARANOID
+ if (!ignore && next_tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return TRUE;
+ }
+
+ if (!tp->tree->parent)
+ return FALSE;
+
+ t = tp->tree;
+ do {
+ if (t->parent) {
+ td = next_item(t->parent, t->paritem);
+
+ if (td) break;
+ }
+
+ t = t->parent;
+ } while (t);
+
+ if (!t)
+ return FALSE;
+
+ if (_do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, func, file, line))
+ _increase_tree_rc(t->parent, func, file, line);
+
+ t = td->treeholder.tree;
+
+ while (t->header.level != 0) {
+ tree_data* fi;
+
+ fi = first_item(t);
+ _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, func, file, line);
+
+ t = fi->treeholder.tree;
+ }
+
+ next_tp->tree = t;
+ next_tp->item = first_item(t);
+
+ if (!ignore && next_tp->item->ignore) {
+ traverse_ptr ntp2;
+ BOOL b;
+
+ while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
+ _free_traverse_ptr(next_tp, func, file, line);
+ *next_tp = ntp2;
+
+ if (!next_tp->item->ignore)
+ break;
+ }
+
+ if (!b) {
+ _free_traverse_ptr(next_tp, func, file, line);
+ return FALSE;
+ }
+ }
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+#ifdef DEBUG_PARANOID
+ if (!ignore && next_tp->item->ignore) {
+ ERR("error - returning ignored item\n");
+ int3;
+ }
+#endif
+
+ return TRUE;
+}
+
+static __inline tree_data* prev_item(tree* t, tree_data* td) {
+ LIST_ENTRY* le = td->list_entry.Blink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+static __inline tree_data* last_item(tree* t) {
+ LIST_ENTRY* le = t->itemlist.Blink;
+
+ if (le == &t->itemlist)
+ return NULL;
+
+ return CONTAINING_RECORD(le, tree_data, list_entry);
+}
+
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+ tree* t;
+ tree_data* td;
+
+ // FIXME - support ignore flag
+ if (prev_item(tp->tree, tp->item)) {
+ prev_tp->tree = tp->tree;
+ _increase_tree_rc(prev_tp->tree, func, file, line);
+ prev_tp->item = prev_item(tp->tree, tp->item);
+
+ return TRUE;
+ }
+
+ if (!tp->tree->parent)
+ return FALSE;
+
+ t = tp->tree;
+ while (t && (!t->parent || !prev_item(t->parent, t->paritem))) {
+ t = t->parent;
+ }
+
+ if (!t)
+ return FALSE;
+
+ td = prev_item(t->parent, t->paritem);
+ if (_do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, func, file, line))
+ _increase_tree_rc(t->parent, func, file, line);
+
+ t = td->treeholder.tree;
+
+ while (t->header.level != 0) {
+ tree_data* li;
+
+ li = last_item(t);
+ _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, func, file, line);
+
+ t = li->treeholder.tree;
+ }
+
+ add_to_tree_cache(Vcb, t, FALSE);
+
+ prev_tp->tree = t;
+ prev_tp->item = last_item(t);
+
+ return TRUE;
+}
+
+BOOL STDCALL _get_item(device_extension* Vcb, root* r, UINT64 objid, UINT8 objtype, UINT64 offset, void* ptr, UINT32 size, const char* func, const char* file, unsigned int line) {
+ traverse_ptr tp;
+ KEY searchkey;
+
+ searchkey.obj_id = objid;
+ searchkey.obj_type = objtype;
+ searchkey.offset = offset;
+
+ if (!_find_item(Vcb, r, &tp, &searchkey, FALSE, func, file, line))
+ return FALSE;
+
+ TRACE("looked for (%llx,%x,%llx), got (%llx,%x,%llx)\n", objid, objtype, offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ _free_traverse_ptr(&tp, func, file, line);
+ return FALSE;
+ }
+
+ if (size > tp.item->size)
+ size = tp.item->size;
+
+ RtlCopyMemory(ptr, tp.item->data, size);
+
+ _free_traverse_ptr(&tp, func, file, line);
+ return TRUE;
+}
+
+// static void free_tree_holder(tree_holder* th) {
+// root* r = th->tree->root;
+//
+// // ExAcquireResourceExclusiveLite(&th->nonpaged->lock, TRUE);
+// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+//
+// free_tree2(th->tree, funcname, __FILE__, __LINE__);
+//
+// // ExReleaseResourceLite(&th->nonpaged->lock);
+// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+// }
+
+void STDCALL free_tree_cache(LIST_ENTRY* tc) {
+ LIST_ENTRY* le;
+ tree_cache* tc2;
+ root* r;
+
+ while (tc->Flink != tc) {
+ le = tc->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ r = tc2->tree->root;
+
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+
+ while (le != tc) {
+ LIST_ENTRY* nextle = le->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree->root == r) {
+ tree* nt;
+ BOOL top = !tc2->tree->paritem;
+
+ nt = free_tree2(tc2->tree, funcname, __FILE__, __LINE__);
+ if (top && !nt && r->treeholder.tree == tc2->tree)
+ r->treeholder.tree = NULL;
+
+ RemoveEntryList(&tc2->list_entry);
+ ExFreePool(tc2);
+ }
+
+ le = nextle;
+ }
+
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+ }
+}
+
+void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write) {
+ LIST_ENTRY* le;
+ tree_cache* tc2;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree == t) {
+ if (write && !tc2->write) {
+ Vcb->write_trees++;
+ tc2->write = TRUE;
+ }
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ tc2 = ExAllocatePoolWithTag(PagedPool, sizeof(tree_cache), ALLOC_TAG);
+
+ TRACE("adding %p to tree cache\n", t);
+
+ tc2->tree = t;
+ tc2->write = write;
+ increase_tree_rc(t);
+ InsertTailList(&Vcb->tree_cache, &tc2->list_entry);
+
+// print_trees(tc);
+}
+
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp) {
+ traverse_ptr tp;
+ KEY searchkey;
+ int cmp;
+ tree_data *td, *paritem;
+ tree* t;
+#if DEBUG_LEVEL >= 3
+ LIST_ENTRY* le;
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+#endif
+
+ TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, tc, ptp);
+
+ searchkey.obj_id = obj_id;
+ searchkey.obj_type = obj_type;
+ searchkey.offset = offset;
+
+ if (!find_item(Vcb, r, &tp, &searchkey, TRUE)) {
+ ERR("error: find_item failed\n");
+ return FALSE;
+ }
+
+// // don't insert into tree about to be deleted
+// while (tp.tree->header.num_items == 0) {
+// traverse_ptr prev_tp;
+//
+// ERR("tree about to be deleted, moving back\n");
+//
+// if (find_prev_item(Vcb, &tp, &prev_tp, NULL, TRUE)) {
+// free_traverse_ptr(&tp);
+// tp = prev_tp;
+// } else
+// break;
+// }
+
+ TRACE("tp.item = %p\n", tp.item);
+ TRACE("tp.item->key = %p\n", &tp.item->key);
+ cmp = keycmp(&searchkey, &tp.item->key);
+
+ if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
+ ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = searchkey;
+ td->size = size;
+ td->data = data;
+ td->ignore = FALSE;
+ td->inserted = TRUE;
+
+#if DEBUG_LEVEL >= 3
+ le = tp.tree->itemlist.Flink;
+ while (le != &tp.tree->itemlist) {
+ tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+ firstitem = td2->key;
+ break;
+ }
+
+ TRACE("inserting %llx,%x,%llx into tree beginning %llx,%x,%llx (num_items %x)\n", obj_id, obj_type, offset, firstitem.obj_id, firstitem.obj_type, firstitem.offset, tp.tree->header.num_items);
+#endif
+
+ if (cmp == -1) { // very first key in root
+ InsertHeadList(&tp.tree->itemlist, &td->list_entry);
+
+ paritem = tp.tree->paritem;
+ while (paritem) {
+// ERR("paritem = %llx,%x,%llx, tp.item->key = %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ if (!keycmp(&paritem->key, &tp.item->key)) {
+ paritem->key = searchkey;
+ } else
+ break;
+
+ paritem = paritem->treeholder.tree->paritem;
+ }
+
+ } else {
+ InsertAfter(&tp.tree->itemlist, &td->list_entry, &tp.item->list_entry); // FIXME - we don't need this
+ }
+
+ tp.tree->header.num_items++;
+ tp.tree->size += size + sizeof(leaf_node);
+// ERR("tree %p, num_items now %x\n", tp.tree, tp.tree->header.num_items);
+// ERR("size now %x\n", tp.tree->size);
+
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+
+ if (!ptp)
+ free_traverse_ptr(&tp);
+ else
+ *ptp = tp;
+
+ t = tp.tree;
+ while (t) {
+ t->header.generation = Vcb->superblock.generation;
+ t = t->parent;
+ }
+
+ // FIXME - free this correctly
+
+ return TRUE;
+}
+
+void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp) {
+ tree* t;
+ UINT64 gen;
+
+ TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->ignore ? "TRUE" : "FALSE");
+
+#ifdef DEBUG_PARANOID
+ if (tp->item->ignore) {
+ ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
+ int3;
+ }
+#endif
+
+ tp->item->ignore = TRUE;
+
+ add_to_tree_cache(Vcb, tp->tree, TRUE);
+
+ tp->tree->header.num_items--;
+
+ if (tp->tree->header.level == 0)
+ tp->tree->size -= sizeof(leaf_node) + tp->item->size;
+ else
+ tp->tree->size -= sizeof(internal_node);
+
+ gen = tp->tree->Vcb->superblock.generation;
+
+ t = tp->tree;
+ while (t) {
+ t->header.generation = gen;
+ t = t->parent;
+ }
+
+// if (tp->tree->header.num_items == 0 && tp->tree->parent) {
+// traverse_ptr tp2;
+//
+// tp2.tree = tp->tree->parent;
+// tp2.item = tp->tree->paritem;
+//
+// delete_tree_item(&tp2, tc);
+// }
+}
Index: reactos/drivers/filesystems/btrfs/write.c
===================================================================
--- reactos/drivers/filesystems/btrfs/write.c (revision 0)
+++ reactos/drivers/filesystems/btrfs/write.c (working copy)
@@ -0,0 +1,5714 @@
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see . */
+
+#include "btrfs_drv.h"
+
+#define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
+
+// BOOL did_split;
+BOOL chunk_test = FALSE;
+
+typedef struct {
+ KEVENT Event;
+ IO_STATUS_BLOCK iosb;
+} write_context;
+
+typedef struct {
+ EXTENT_ITEM ei;
+ UINT8 type;
+ EXTENT_DATA_REF edr;
+} EXTENT_ITEM_DATA_REF;
+
+typedef struct {
+ EXTENT_ITEM_TREE eit;
+ UINT8 type;
+ TREE_BLOCK_REF tbr;
+} EXTENT_ITEM_TREE2;
+
+typedef struct {
+ EXTENT_ITEM ei;
+ UINT8 type;
+ TREE_BLOCK_REF tbr;
+} EXTENT_ITEM_SKINNY_METADATA;
+
+typedef struct {
+ CHUNK_ITEM ci;
+ CHUNK_ITEM_STRIPE stripes[1];
+} CHUNK_ITEM2;
+
+typedef struct {
+ LIST_ENTRY list_entry;
+ UINT64 key;
+} ordered_list;
+
+typedef struct {
+ ordered_list ol;
+ ULONG length;
+ UINT32* checksums;
+ BOOL deleted;
+} changed_sector;
+
+static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size);
+static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
+static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size);
+
+static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ write_context* context = conptr;
+
+ context->iosb = Irp->IoStatus;
+ KeSetEvent(&context->Event, 0, FALSE);
+
+// return STATUS_SUCCESS;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL write_data_phys(PDEVICE_OBJECT device, UINT64 address, void* data, UINT32 length) {
+ NTSTATUS Status;
+ LARGE_INTEGER offset;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ write_context* context = NULL;
+
+ TRACE("(%p, %llx, %p, %x)\n", device, address, data, length);
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_context), ALLOC_TAG);
+ RtlZeroMemory(context, sizeof(write_context));
+
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ offset.QuadPart = address;
+
+// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
+
+ Irp = IoAllocateIrp(device->StackSize, FALSE);
+
+ if (!Irp) {
+ ERR("IoAllocateIrp failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit2;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (device->Flags & DO_BUFFERED_IO) {
+ Irp->AssociatedIrp.SystemBuffer = data;
+
+ Irp->Flags = IRP_BUFFERED_IO;
+ } else if (device->Flags & DO_DIRECT_IO) {
+ Irp->MdlAddress = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+ if (!Irp->MdlAddress) {
+ DbgPrint("IoAllocateMdl failed\n");
+ goto exit;
+ }
+
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ Irp->UserBuffer = data;
+ }
+
+ IrpSp->Parameters.Write.Length = length;
+ IrpSp->Parameters.Write.ByteOffset = offset;
+
+ Irp->UserIosb = &context->iosb;
+
+ Irp->UserEvent = &context->Event;
+
+ IoSetCompletionRoutine(Irp, write_completion, context, TRUE, TRUE, TRUE);
+
+ // FIXME - support multiple devices
+ Status = IoCallDriver(device, Irp);
+
+ if (Status == STATUS_PENDING) {
+ KeWaitForSingleObject(&context->Event, Suspended, KernelMode, FALSE, NULL);
+ Status = context->iosb.Status;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCallDriver returned %08x\n", Status);
+ }
+
+ if (device->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+exit:
+ IoFreeIrp(Irp);
+
+exit2:
+ if (context)
+ ExFreePool(context);
+
+ return Status;
+}
+
+static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) {
+ NTSTATUS Status;
+ unsigned int i = 0;
+ UINT32 crc32;
+
+ // FIXME - work with RAID
+
+ // FIXME - only write one superblock if on SSD (?)
+ while (superblock_addrs[i] > 0 && Vcb->length >= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("writing superblock %u\n", i);
+
+ Vcb->superblock.sb_phys_addr = superblock_addrs[i];
+ RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM));
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
+ crc32 = ~crc32;
+ TRACE("crc32 is %08x\n", crc32);
+ RtlCopyMemory(&Vcb->superblock.checksum, &crc32, sizeof(UINT32));
+
+ Status = write_data_phys(device->devobj, superblock_addrs[i], &Vcb->superblock, sizeof(superblock));
+
+ if (!NT_SUCCESS(Status))
+ break;
+
+ i++;
+ }
+
+ return Status;
+}
+
+static BOOL find_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) {
+ LIST_ENTRY* le;
+ space *s, *bestfit = NULL;
+
+ TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address);
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->type == SPACE_TYPE_FREE) {
+ if (s->size == length) {
+ *address = s->offset;
+ TRACE("returning exact fit at %llx\n", s->offset);
+ return TRUE;
+ } else if (s->size > length && (!bestfit || bestfit->size > s->size)) {
+ bestfit = s;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (bestfit) {
+ TRACE("returning best fit at %llx\n", bestfit->offset);
+ *address = bestfit->offset;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type) {
+ LIST_ENTRY *le = c->space.Flink, *nextle, *insbef;
+ space *s, *s2, *s3;
+#ifdef DEBUG_PARANOID
+ UINT64 lastaddr;
+#endif
+
+ TRACE("(%p, %llx, %llx, %x)\n", c, offset, size, type);
+
+#ifdef DEBUG_PARANOID
+ // TESTING
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+#endif
+
+ c->space_changed = TRUE;
+
+ le = c->space.Flink;
+ insbef = &c->space;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+ nextle = le->Flink;
+
+ if (s->offset >= offset + size) {
+ insbef = le;
+ break;
+ }
+
+ if (s->offset >= offset && s->offset + s->size <= offset + size) { // delete entirely
+ RemoveEntryList(&s->list_entry);
+
+ if (s->offset + s->size == offset + size) {
+ insbef = s->list_entry.Flink;
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ break;
+ }
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ } else if (s->offset < offset && s->offset + s->size > offset + size) { // split in two
+ s3 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s3->offset = offset + size;
+ s3->size = s->size - size - offset + s->offset;
+ s3->type = s->type;
+ InsertHeadList(&s->list_entry, &s3->list_entry);
+ insbef = &s3->list_entry;
+
+ s->size = offset - s->offset;
+ break;
+ } else if (s->offset + s->size > offset && s->offset + s->size <= offset + size) { // truncate before
+ s->size = offset - s->offset;
+ } else if (s->offset < offset + size && s->offset + s->size > offset + size) { // truncate after
+ s->size -= s->offset - offset + size;
+ s->offset = offset + size;
+
+ insbef = le;
+ break;
+ }
+
+ le = nextle;
+ }
+
+ s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s2->offset = offset;
+ s2->size = size;
+ s2->type = type;
+ InsertTailList(insbef, &s2->list_entry);
+
+ // merge entries if same type
+
+ if (s2->list_entry.Blink != &c->space) {
+ s = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
+
+ if (s->type == type) {
+ s->size += s2->size;
+
+ RemoveEntryList(&s2->list_entry);
+ ExFreePool(s2);
+
+ s2 = s;
+ }
+ }
+
+ if (s2->list_entry.Flink != &c->space) {
+ s = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
+
+ if (s->type == type) {
+ s2->size += s->size;
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ }
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+
+#ifdef DEBUG_PARANOID
+ // TESTING
+ lastaddr = c->offset;
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset != lastaddr) {
+ ERR("inconsistency detected!\n");
+ int3;
+ }
+
+ lastaddr = s->offset + s->size;
+
+ le = le->Flink;
+ }
+
+ if (lastaddr != c->offset + c->chunk_item->size) {
+ ERR("inconsistency detected - space doesn't run all the way to end of chunk\n");
+ int3;
+ }
+#endif
+}
+
+chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) {
+ LIST_ENTRY* le2;
+ chunk* c;
+
+ le2 = Vcb->chunks.Flink;
+ while (le2 != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le2, chunk, list_entry);
+
+// TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
+
+ if (address >= c->offset && address < c->offset + c->chunk_item->size)
+ return c;
+
+ le2 = le2->Flink;
+ }
+
+ return NULL;
+}
+
+typedef struct {
+ disk_hole* dh;
+ device* device;
+} stripe;
+
+static void add_provisional_disk_hole(device_extension* Vcb, stripe* s, UINT64 max_stripe_size) {
+// LIST_ENTRY* le = s->device->disk_holes.Flink;
+// disk_hole* dh;
+
+// ERR("old holes:\n");
+// while (le != &s->device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+
+ if (s->dh->size <= max_stripe_size) {
+ s->dh->provisional = TRUE;
+ } else {
+ disk_hole* newdh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
+
+ newdh->address = s->dh->address + max_stripe_size;
+ newdh->size = s->dh->size - max_stripe_size;
+ newdh->provisional = FALSE;
+ InsertTailList(&s->device->disk_holes, &newdh->listentry);
+
+ s->dh->size = max_stripe_size;
+ s->dh->provisional = TRUE;
+ }
+
+// ERR("new holes:\n");
+// le = s->device->disk_holes.Flink;
+// while (le != &s->device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+}
+
+static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ UINT64 lastaddr;
+
+ searchkey.obj_id = 0x100;
+ searchkey.obj_type = TYPE_CHUNK_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return 0xffffffffffffffff;
+ }
+
+ lastaddr = 0;
+
+ do {
+ if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
+ CHUNK_ITEM* ci = (CHUNK_ITEM*)tp.item->data;
+
+ if (tp.item->key.offset >= lastaddr + size) {
+ free_traverse_ptr(&tp);
+ return lastaddr;
+ }
+
+ lastaddr = tp.item->key.offset + ci->size;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ return lastaddr;
+}
+
+static BOOL increase_dev_item_used(device_extension* Vcb, device* device, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp;
+ DEV_ITEM* di;
+
+ searchkey.obj_id = 1;
+ searchkey.obj_type = TYPE_DEV_ITEM;
+ searchkey.offset = device->devitem.dev_id;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find DEV_ITEM for device %llx\n", device->devitem.dev_id);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ device->devitem.bytes_used += size;
+
+ di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
+ RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM));
+
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void reset_disk_holes(device* device, BOOL commit) {
+ LIST_ENTRY* le = device->disk_holes.Flink;
+ disk_hole* dh;
+
+// ERR("old holes:\n");
+// while (le != &device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+
+ le = device->disk_holes.Flink;
+ while (le != &device->disk_holes) {
+ LIST_ENTRY* le2 = le->Flink;
+
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ if (dh->provisional) {
+ if (commit) {
+ RemoveEntryList(le);
+ ExFreePool(dh);
+ } else {
+ dh->provisional = FALSE;
+ }
+ }
+
+ le = le2;
+ }
+
+ if (!commit) {
+ le = device->disk_holes.Flink;
+ while (le != &device->disk_holes) {
+ LIST_ENTRY* le2 = le->Flink;
+
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ while (le2 != &device->disk_holes) {
+ disk_hole* dh2 = CONTAINING_RECORD(le2, disk_hole, listentry);
+
+ if (dh2->address == dh->address + dh->size) {
+ LIST_ENTRY* le3 = le2->Flink;
+ dh->size += dh2->size;
+
+ RemoveEntryList(le2);
+ ExFreePool(dh2);
+
+ le2 = le3;
+ } else
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+// ERR("new holes:\n");
+// le = device->disk_holes.Flink;
+// while (le != &device->disk_holes) {
+// dh = CONTAINING_RECORD(le, disk_hole, listentry);
+//
+// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
+//
+// le = le->Flink;
+// }
+}
+
+static NTSTATUS add_to_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, ULONG size) {
+ sys_chunk *sc, *sc2;
+ LIST_ENTRY* le;
+ USHORT i;
+
+ if (Vcb->superblock.n + sizeof(KEY) + size > SYS_CHUNK_ARRAY_SIZE) {
+ ERR("error - bootstrap is full\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
+
+ sc->key.obj_id = obj_id;
+ sc->key.obj_type = obj_type;
+ sc->key.offset = offset;
+ sc->size = size;
+ sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
+ RtlCopyMemory(sc->data, data, sc->size);
+
+ le = Vcb->sys_chunks.Flink;
+ while (le != &Vcb->sys_chunks) {
+ sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ if (keycmp(&sc2->key, &sc->key) == 1)
+ break;
+
+ le = le->Flink;
+ }
+ InsertTailList(le, &sc->list_entry);
+
+ Vcb->superblock.n += sizeof(KEY) + size;
+
+ i = 0;
+ le = Vcb->sys_chunks.Flink;
+ while (le != &Vcb->sys_chunks) {
+ sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
+
+ TRACE("%llx,%x,%llx\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset);
+
+ RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY));
+ i += sizeof(KEY);
+
+ RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size);
+ i += sc2->size;
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) {
+ UINT64 max_stripe_size, max_chunk_size, stripe_size;
+ UINT64 total_size = 0, i, j, logaddr;
+ int num_stripes;
+ disk_hole* dh;
+ stripe* stripes;
+ ULONG cisize;
+ CHUNK_ITEM* ci;
+ CHUNK_ITEM_STRIPE* cis;
+ chunk* c = NULL;
+ space* s = NULL;
+ BOOL success = FALSE;
+ BLOCK_GROUP_ITEM* bgi;
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ total_size += Vcb->devices[i].devitem.num_bytes;
+ }
+ TRACE("total_size = %llx\n", total_size);
+
+ if (flags & BLOCK_FLAG_DATA) {
+ max_stripe_size = 0x40000000; // 1 GB
+ max_chunk_size = 10 * max_stripe_size;
+ } else if (flags & BLOCK_FLAG_METADATA) {
+ if (total_size > 0xC80000000) // 50 GB
+ max_stripe_size = 0x40000000; // 1 GB
+ else
+ max_stripe_size = 0x10000000; // 256 MB
+
+ max_chunk_size = max_stripe_size;
+ } else if (flags & BLOCK_FLAG_SYSTEM) {
+ max_stripe_size = 0x2000000; // 32 MB
+ max_chunk_size = 2 * max_stripe_size;
+ }
+
+ // FIXME - make sure whole number of sectors?
+ max_chunk_size = min(max_chunk_size, total_size / 10); // cap at 10%
+
+ TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size);
+
+ if (flags & BLOCK_FLAG_DUPLICATE) {
+ num_stripes = 2;
+ } else if (flags & BLOCK_FLAG_RAID0) {
+ FIXME("RAID0 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID1) {
+ FIXME("RAID1 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID10) {
+ FIXME("RAID10 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID5) {
+ FIXME("RAID5 not yet supported\n");
+ return NULL;
+ } else if (flags & BLOCK_FLAG_RAID6) {
+ FIXME("RAID6 not yet supported\n");
+ return NULL;
+ } else { // SINGLE
+ num_stripes = 1;
+ }
+
+ stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < num_stripes; i++) {
+ stripes[i].dh = NULL;
+
+ for (j = 0; j < Vcb->superblock.num_devices; j++) {
+ LIST_ENTRY* le = Vcb->devices[j].disk_holes.Flink;
+
+ while (le != &Vcb->devices[j].disk_holes) {
+ dh = CONTAINING_RECORD(le, disk_hole, listentry);
+
+ if (!dh->provisional) {
+ if (!stripes[i].dh || dh->size > stripes[i].dh->size) {
+ stripes[i].dh = dh;
+ stripes[i].device = &Vcb->devices[j];
+
+ if (stripes[i].dh->size >= max_stripe_size)
+ break;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ if (stripes[i].dh && stripes[i].dh->size >= max_stripe_size)
+ break;
+ }
+
+ if (stripes[i].dh) {
+ TRACE("good DH: device %llx, address %llx, size %llx\n", stripes[i].device->devitem.dev_id, stripes[i].dh->address, stripes[i].dh->size);
+ } else {
+ TRACE("good DH not found\n");
+ goto end;
+ }
+
+ add_provisional_disk_hole(Vcb, &stripes[i], max_stripe_size);
+ }
+
+ stripe_size = min(stripes[0].dh->size, max_stripe_size);
+ for (i = 1; i < num_stripes; i++) {
+ stripe_size = min(stripe_size, stripes[1].dh->size);
+ }
+ // FIXME - make sure stripe_size aligned properly
+ // FIXME - obey max_chunk_size
+
+ c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+
+ // add CHUNK_ITEM to tree 3
+
+ cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
+ ci = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+
+ ci->size = stripe_size; // FIXME for RAID
+ ci->root_id = Vcb->extent_root->id;
+ ci->stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
+ ci->type = flags;
+ ci->opt_io_alignment = ci->stripe_length;
+ ci->opt_io_width = ci->stripe_length;
+ ci->sector_size = stripes[0].device->devitem.minimal_io_size;
+ ci->num_stripes = num_stripes;
+ ci->sub_stripes = 1;
+
+ c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
+
+ for (i = 0; i < num_stripes; i++) {
+ if (i == 0)
+ cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+ else
+ cis = &cis[1];
+
+ cis->dev_id = stripes[i].device->devitem.dev_id;
+ cis->offset = stripes[i].dh->address;
+ cis->dev_uuid = stripes[i].device->devitem.device_uuid;
+
+ c->devices[i] = stripes[i].device;
+ }
+
+ logaddr = find_new_chunk_address(Vcb, ci->size);
+ if (logaddr == 0xffffffffffffffff) {
+ ERR("find_new_chunk_address failed\n");
+ ExFreePool(ci);
+ goto end;
+ }
+
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ci);
+ goto end;
+ }
+
+ if (flags & BLOCK_FLAG_SYSTEM) {
+ NTSTATUS Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_to_bootstrap returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ Vcb->superblock.chunk_root_generation = Vcb->superblock.generation;
+
+ c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+ RtlCopyMemory(c->chunk_item, ci, cisize);
+ c->size = cisize;
+ c->offset = logaddr;
+ c->used = c->oldused = 0;
+ c->space_changed = FALSE;
+ InitializeListHead(&c->space);
+
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s->offset = c->offset;
+ s->size = c->chunk_item->size;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ protect_superblocks(Vcb, c);
+
+ // add BLOCK_GROUP_ITEM to tree 2
+
+ bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG);
+ bgi->used = 0;
+ bgi->chunk_tree = 0x100;
+ bgi->flags = flags;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, logaddr, TYPE_BLOCK_GROUP_ITEM, ci->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(bgi);
+ goto end;
+ }
+
+ // add DEV_EXTENTs to tree 4
+
+ for (i = 0; i < num_stripes; i++) {
+ DEV_EXTENT* de;
+
+ de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG);
+ de->chunktree = Vcb->chunk_root->id;
+ de->objid = 0x100;
+ de->address = logaddr;
+ de->length = ci->size;
+ de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid;
+
+ if (!insert_tree_item(Vcb, Vcb->dev_root, stripes[i].device->devitem.dev_id, TYPE_DEV_EXTENT, stripes[i].dh->address, de, sizeof(DEV_EXTENT), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(de);
+ goto end;
+ }
+
+ if (!increase_dev_item_used(Vcb, stripes[i].device, ci->size)) {
+ ERR("increase_dev_item_used failed\n");
+ goto end;
+ }
+ }
+
+ for (i = 0; i < num_stripes; i++) {
+ BOOL b = FALSE;
+ for (j = 0; j < i; j++) {
+ if (stripes[j].device == stripes[i].device)
+ b = TRUE;
+ }
+
+ if (!b)
+ reset_disk_holes(stripes[i].device, TRUE);
+ }
+
+ success = TRUE;
+
+end:
+ ExFreePool(stripes);
+
+ if (!success) {
+ for (i = 0; i < num_stripes; i++) {
+ BOOL b = FALSE;
+ for (j = 0; j < i; j++) {
+ if (stripes[j].device == stripes[i].device)
+ b = TRUE;
+ }
+
+ if (!b)
+ reset_disk_holes(stripes[i].device, FALSE);
+ }
+
+ if (c) ExFreePool(c);
+ if (s) ExFreePool(s);
+ } else
+ InsertTailList(&Vcb->chunks, &c->list_entry);
+
+ return success ? c : NULL;
+}
+
+static void decrease_chunk_usage(chunk* c, UINT64 delta) {
+ c->used -= delta;
+
+ TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
+static void increase_chunk_usage(chunk* c, UINT64 delta) {
+ c->used += delta;
+
+ TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
+static NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length) {
+ KEY searchkey;
+ traverse_ptr tp;
+ CHUNK_ITEM2* ci;
+ NTSTATUS Status;
+ UINT32 i;
+
+ TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
+
+ // FIXME - use version cached in Vcb
+
+ searchkey.obj_id = 0x100; // fixed?
+ searchkey.obj_type = TYPE_CHUNK_ITEM;
+ searchkey.offset = address;
+
+ if (!find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in chunk_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - unexpected item in chunk tree\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ci = (CHUNK_ITEM2*)tp.item->data;
+
+ if (tp.item->key.offset > address || tp.item->key.offset + ci->ci.size < address) {
+ ERR("error - address %llx was out of chunk bounds\n", address);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // FIXME - only do this for chunks marked DUPLICATE?
+ // FIXME - for multiple writes, if PENDING do waits at the end
+ // FIXME - work with RAID
+ for (i = 0; i < ci->ci.num_stripes; i++) {
+ Status = write_data_phys(Vcb->devices[0].devobj, address - tp.item->key.offset + ci->stripes[i].offset, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - write_data_phys failed\n");
+ goto end;
+ }
+ }
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) {
+ LIST_ENTRY *le, *nextle;
+ space *s, *s2;
+
+// // TESTING
+// le = c->space.Flink;
+// while (le != &c->space) {
+// s = CONTAINING_RECORD(le, space, list_entry);
+//
+// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
+//
+// le = le->Flink;
+// }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+ nextle = le->Flink;
+
+ if (s->type == SPACE_TYPE_DELETING)
+ s->type = SPACE_TYPE_FREE;
+ else if (s->type == SPACE_TYPE_WRITING)
+ s->type = SPACE_TYPE_USED;
+
+ if (le->Blink != &c->space) {
+ s2 = CONTAINING_RECORD(le->Blink, space, list_entry);
+
+ if (s2->type == s->type) { // do merge
+ s2->size += s->size;
+
+ RemoveEntryList(&s->list_entry);
+ ExFreePool(s);
+ }
+ }
+
+ le = nextle;
+ }
+
+// le = c->space.Flink;
+// while (le != &c->space) {
+// s = CONTAINING_RECORD(le, space, list_entry);
+//
+// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
+//
+// le = le->Flink;
+// }
+}
+
+static void clean_space_cache(device_extension* Vcb) {
+ LIST_ENTRY* le;
+ chunk* c;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->space_changed) {
+ clean_space_cache_chunk(Vcb, c);
+ c->space_changed = FALSE;
+ }
+
+ le = le->Flink;
+ }
+}
+
+static BOOL trees_consistent(device_extension* Vcb) {
+ ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header);
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write) {
+ if (tc2->tree->header.num_items == 0)
+ return FALSE;
+
+ if (tc2->tree->size > maxsize)
+ return FALSE;
+
+ if (tc2->tree->new_address == 0)
+ return FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ return TRUE;
+}
+
+static NTSTATUS add_parents(device_extension* Vcb) {
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write) {
+ if (tc2->tree->parent)
+ add_to_tree_cache(Vcb, tc2->tree->parent, TRUE);
+ else if (tc2->tree->root != Vcb->chunk_root && tc2->tree->root != Vcb->root_root) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
+ ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+
+ RtlCopyMemory(ri, tp.item->data, tp.item->size);
+ RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+ }
+
+ free_traverse_ptr(&tp);
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void print_trees(LIST_ENTRY* tc) {
+ LIST_ENTRY *le, *le2;
+
+ le = tc->Flink;
+ while (le != tc) {
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ UINT32 num_items = 0;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ firstitem = td->key;
+ num_items++;
+ }
+ le2 = le2->Flink;
+ }
+
+ ERR("tree: root %llx, first key %llx,%x,%llx, level %x, num_items %x / %x\n",
+ tc2->tree->header.tree_id, firstitem.obj_id, firstitem.obj_type, firstitem.offset, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
+
+ le = le->Flink;
+ }
+}
+
+static void add_parents_to_cache(device_extension* Vcb, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ while (t->parent) {
+ t = t->parent;
+
+ add_to_tree_cache(Vcb, t, TRUE);
+ }
+
+ if (t->root == Vcb->root_root || t->root == Vcb->chunk_root)
+ return;
+
+ searchkey.obj_id = t->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ add_to_tree_cache(Vcb, tp.tree, TRUE);
+
+ free_traverse_ptr(&tp);
+}
+
+static BOOL insert_tree_extent_skinny(device_extension* Vcb, tree* t, chunk* c, UINT64 address) {
+ EXTENT_ITEM_SKINNY_METADATA* eism;
+ traverse_ptr insert_tp;
+
+ eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
+
+ eism->ei.refcount = 1;
+ eism->ei.generation = Vcb->superblock.generation;
+ eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
+ eism->type = TYPE_TREE_BLOCK_REF;
+ eism->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, t->header.level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eism);
+ return FALSE;
+ }
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ t->new_address = address;
+
+ return TRUE;
+}
+
+static BOOL insert_tree_extent(device_extension* Vcb, tree* t, chunk* c) {
+ UINT64 address;
+ EXTENT_ITEM_TREE2* eit2;
+ traverse_ptr insert_tp;
+
+ TRACE("(%p, %p, %p, %p)\n", Vcb, t, c);
+
+ if (!find_address_in_chunk(Vcb, c, Vcb->superblock.node_size, &address))
+ return FALSE;
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+ return insert_tree_extent_skinny(Vcb, t, c, address);
+
+ eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
+
+ eit2->eit.extent_item.refcount = 1;
+ eit2->eit.extent_item.generation = Vcb->superblock.generation;
+ eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
+// eit2->eit.firstitem = wt->firstitem;
+ eit2->eit.level = t->header.level;
+ eit2->type = TYPE_TREE_BLOCK_REF;
+ eit2->tbr.offset = t->header.tree_id;
+
+// #ifdef DEBUG_PARANOID
+// if (wt->firstitem.obj_type == 0xcc) { // TESTING
+// ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
+// ERR("num_items = %u, level = %u, root = %x, delete = %u\n", wt->tree->header.num_items, wt->tree->header.level, (UINT32)wt->tree->root->id, wt->delete);
+// int3;
+// }
+// #endif
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eit2);
+ return FALSE;
+ }
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ t->new_address = address;
+
+ return TRUE;
+}
+
+static NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t) {
+ chunk *origchunk = NULL, *c;
+ LIST_ENTRY* le;
+ UINT64 flags = t->flags;
+
+ if (flags == 0)
+ flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE;
+
+// TRACE("flags = %x\n", (UINT32)wt->flags);
+
+// if (!chunk_test) { // TESTING
+// if ((c = alloc_chunk(Vcb, flags))) {
+// if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+// if (insert_tree_extent(Vcb, t, c)) {
+// chunk_test = TRUE;
+// return STATUS_SUCCESS;
+// }
+// }
+// }
+// }
+
+ if (t->header.address != 0) {
+ origchunk = get_chunk_from_address(Vcb, t->header.address);
+
+ if (insert_tree_extent(Vcb, t, origchunk))
+ return STATUS_SUCCESS;
+ }
+
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ // FIXME - make sure to avoid superblocks
+
+ if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+ if (insert_tree_extent(Vcb, t, c))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ // allocate new chunk if necessary
+ if ((c = alloc_chunk(Vcb, flags))) {
+ if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
+ if (insert_tree_extent(Vcb, t, c))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb->superblock.node_size);
+
+ return STATUS_DISK_FULL;
+}
+
+static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+ chunk* c;
+ EXTENT_ITEM_SKINNY_METADATA* eism;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_METADATA_ITEM;
+ searchkey.offset = t->header.level;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return FALSE;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ eism = (EXTENT_ITEM_SKINNY_METADATA*)tp.item->data;
+ if (t->header.level == 0 && eism->ei.flags & EXTENT_ITEM_SHARED_BACKREFS && eism->type == TYPE_TREE_BLOCK_REF) {
+ // convert shared data extents
+
+ LIST_ENTRY* le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF;
+ }
+
+ c = get_chunk_from_address(Vcb, address);
+
+ if (c) {
+ decrease_chunk_usage(c, Vcb->superblock.node_size);
+
+ add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_DELETING);
+ } else
+ ERR("could not find chunk for address %llx\n", address);
+
+ free_traverse_ptr(&tp);
+
+ return TRUE;
+}
+
+// TESTING
+// static void check_tree_num_items(tree* t) {
+// LIST_ENTRY* le2;
+// UINT32 ni;
+//
+// le2 = t->itemlist.Flink;
+// ni = 0;
+// while (le2 != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+// if (!td->ignore)
+// ni++;
+// le2 = le2->Flink;
+// }
+//
+// if (t->header.num_items != ni) {
+// ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
+// int3;
+// } else {
+// ERR("tree %p okay\n", t);
+// }
+// }
+//
+// static void check_trees_num_items(LIST_ENTRY* tc) {
+// LIST_ENTRY* le = tc->Flink;
+// while (le != tc) {
+// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+//
+// check_tree_num_items(tc2->tree);
+//
+// le = le->Flink;
+// }
+// }
+
+static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp, tp2, insert_tp;
+ EXTENT_REF_V0* erv0;
+
+ TRACE("(%p, %p, %p)\n", Vcb, td, t);
+
+ searchkey.obj_id = td->treeholder.address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ searchkey.obj_id = td->treeholder.address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ if (keycmp(&searchkey, &tp2.item->key)) {
+ ERR("could not find %llx,%x,%llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+
+ erv0 = (EXTENT_REF_V0*)tp.item->data;
+
+ delete_tree_item(Vcb, &tp);
+ delete_tree_item(Vcb, &tp2);
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+ EXTENT_ITEM_SKINNY_METADATA* eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
+
+ eism->ei.refcount = 1;
+ eism->ei.generation = erv0->gen;
+ eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
+ eism->type = TYPE_TREE_BLOCK_REF;
+ eism->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+ } else {
+ EXTENT_ITEM_TREE2* eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
+
+ eit2->eit.extent_item.refcount = 1;
+ eit2->eit.extent_item.generation = erv0->gen;
+ eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
+ eit2->eit.firstitem = td->key;
+ eit2->eit.level = t->header.level - 1;
+ eit2->type = TYPE_TREE_BLOCK_REF;
+ eit2->tbr.offset = t->header.tree_id;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp)) {
+ ERR("insert_tree_item failed\n");
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+ return;
+ }
+ }
+
+// add_to_tree_cache(tc, insert_tp.tree, TRUE);
+ add_parents_to_cache(Vcb, insert_tp.tree);
+ add_parents_to_cache(Vcb, tp.tree);
+ add_parents_to_cache(Vcb, tp2.tree);
+
+ free_traverse_ptr(&insert_tp);
+
+ free_traverse_ptr(&tp2);
+ free_traverse_ptr(&tp);
+}
+
+static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ EXTENT_ITEM_V0* eiv0;
+ chunk* c;
+
+ // FIXME - deal with refcounts > 1
+
+ TRACE("(%p, %llx, %p)\n", Vcb, address, t);
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+ if (reduce_tree_extent_skinny(Vcb, address, t)) {
+ return STATUS_SUCCESS;
+ }
+ }
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ int3;
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
+
+ if (eiv0->refcount > 1) {
+ FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0->refcount);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (ei->refcount > 1) {
+ FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei->refcount);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0 && ei->flags & EXTENT_ITEM_SHARED_BACKREFS) {
+ // convert shared data extents
+
+ LIST_ENTRY* le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF;
+ }
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ // if EXTENT_ITEM_V0, delete corresponding B4 item
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ traverse_ptr tp2;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ if (tp2.item->key.obj_id == searchkey.obj_id && tp2.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp2);
+ }
+ free_traverse_ptr(&tp2);
+ }
+ }
+
+ if (!(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
+ LIST_ENTRY* le;
+
+ // when writing old internal trees, convert related extents
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+// ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+
+ if (!td->ignore && !td->inserted) {
+ if (t->header.level > 0) {
+ convert_old_tree_extent(Vcb, td, t);
+ } else if (td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address != 0) {
+ TRACE("trying to convert old data extent %llx,%llx\n", ed2->address, ed2->size);
+ convert_old_data_extent(Vcb, ed2->address, ed2->size);
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ c = get_chunk_from_address(Vcb, address);
+
+ if (c) {
+ decrease_chunk_usage(c, tp.item->key.offset);
+
+ add_to_space_list(c, address, tp.item->key.offset, SPACE_TYPE_DELETING);
+ } else
+ ERR("could not find chunk for address %llx\n", address);
+
+ free_traverse_ptr(&tp);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS allocate_tree_extents(device_extension* Vcb) {
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->new_address == 0) {
+ chunk* c;
+
+ Status = get_tree_new_address(Vcb, tc2->tree);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_tree_new_address returned %08x\n", Status);
+ return Status;
+ }
+
+ TRACE("allocated extent %llx\n", tc2->tree->new_address);
+
+ if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb, tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ c = get_chunk_from_address(Vcb, tc2->tree->new_address);
+
+ if (c) {
+ increase_chunk_usage(c, Vcb->superblock.node_size);
+ } else {
+ ERR("could not find chunk for address %llx\n", tc2->tree->new_address);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS update_root_root(device_extension* Vcb) {
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && !tc2->tree->parent) {
+ if (tc2->tree->root != Vcb->root_root && tc2->tree->root != Vcb->chunk_root) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ TRACE("updating the address for root %llx to %llx\n", searchkey.obj_id, tc2->tree->new_address);
+
+ tc2->tree->root->root_item.block_number = tc2->tree->new_address;
+ tc2->tree->root->root_item.root_level = tc2->tree->header.level;
+ tc2->tree->root->root_item.generation = Vcb->superblock.generation;
+ tc2->tree->root->root_item.generation2 = Vcb->superblock.generation;
+
+ if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, delete and create new entry
+ ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+
+ RtlCopyMemory(ri, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else
+ RtlCopyMemory(tp.item->data, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
+
+ free_traverse_ptr(&tp);
+ }
+
+ tc2->tree->root->treeholder.address = tc2->tree->new_address;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+enum write_tree_status {
+ WriteTreeStatus_Pending,
+ WriteTreeStatus_Success,
+ WriteTreeStatus_Error,
+ WriteTreeStatus_Cancelling,
+ WriteTreeStatus_Cancelled
+};
+
+struct write_tree_context;
+
+typedef struct {
+ struct write_tree_context* context;
+ UINT8* buf;
+ device* device;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum write_tree_status status;
+ LIST_ENTRY list_entry;
+} write_tree_stripe;
+
+typedef struct {
+ KEVENT Event;
+ LIST_ENTRY stripes;
+} write_tree_context;
+
+static NTSTATUS STDCALL write_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ write_tree_stripe* stripe = conptr;
+ write_tree_context* context = (write_tree_context*)stripe->context;
+ LIST_ENTRY* le;
+ BOOL complete;
+
+ if (stripe->status == WriteTreeStatus_Cancelling) {
+ stripe->status = WriteTreeStatus_Cancelled;
+ goto end;
+ }
+
+ stripe->iosb = Irp->IoStatus;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ stripe->status = WriteTreeStatus_Success;
+ } else {
+ le = context->stripes.Flink;
+
+ stripe->status = WriteTreeStatus_Error;
+
+ while (le != &context->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (s2->status == WriteTreeStatus_Pending) {
+ s2->status = WriteTreeStatus_Cancelling;
+ IoCancelIrp(s2->Irp);
+ }
+
+ le = le->Flink;
+ }
+ }
+
+end:
+ le = context->stripes.Flink;
+ complete = TRUE;
+
+ while (le != &context->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (s2->status == WriteTreeStatus_Pending || s2->status == WriteTreeStatus_Cancelling) {
+ complete = FALSE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (complete)
+ KeSetEvent(&context->Event, 0, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc) {
+ chunk* c;
+ CHUNK_ITEM_STRIPE* cis;
+ write_tree_stripe* stripe;
+ UINT64 i;
+
+ c = get_chunk_from_address(Vcb, addr);
+
+ if (!c) {
+ ERR("get_chunk_from_address failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+
+ // FIXME - make this work with RAID
+
+ for (i = 0; i < c->chunk_item->num_stripes; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ // FIXME - handle missing devices
+
+ stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_stripe), ALLOC_TAG);
+ stripe->context = (struct write_tree_context*)wtc;
+ stripe->buf = data;
+ stripe->device = c->devices[i];
+ RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK));
+ stripe->status = WriteTreeStatus_Pending;
+
+ stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE);
+
+ if (!stripe->Irp) {
+ ERR("IoAllocateIrp failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
+ IrpSp->MajorFunction = IRP_MJ_WRITE;
+
+ if (stripe->device->devobj->Flags & DO_BUFFERED_IO) {
+ stripe->Irp->AssociatedIrp.SystemBuffer = data;
+
+ stripe->Irp->Flags = IRP_BUFFERED_IO;
+ } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
+ stripe->Irp->MdlAddress = IoAllocateMdl(data, Vcb->superblock.node_size, FALSE, FALSE, NULL);
+ if (!stripe->Irp->MdlAddress) {
+ ERR("IoAllocateMdl failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } else {
+ stripe->Irp->UserBuffer = data;
+ }
+
+ IrpSp->Parameters.Write.Length = Vcb->superblock.node_size;
+ IrpSp->Parameters.Write.ByteOffset.QuadPart = addr - c->offset + cis[i].offset;
+
+ stripe->Irp->UserIosb = &stripe->iosb;
+
+ IoSetCompletionRoutine(stripe->Irp, write_tree_completion, stripe, TRUE, TRUE, TRUE);
+
+ InsertTailList(&wtc->stripes, &stripe->list_entry);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void free_stripes(write_tree_context* wtc) {
+ LIST_ENTRY *le, *le2, *nextle;
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
+ MmUnlockPages(stripe->Irp->MdlAddress);
+ IoFreeMdl(stripe->Irp->MdlAddress);
+ }
+
+ le = le->Flink;
+ }
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ nextle = le->Flink;
+
+ if (stripe->buf) {
+ ExFreePool(stripe->buf);
+
+ le2 = le->Flink;
+ while (le2 != &wtc->stripes) {
+ write_tree_stripe* s2 = CONTAINING_RECORD(le2, write_tree_stripe, list_entry);
+
+ if (s2->buf == stripe->buf)
+ s2->buf = NULL;
+
+ le2 = le2->Flink;
+ }
+
+ }
+
+ ExFreePool(stripe);
+
+ le = nextle;
+ }
+}
+
+static NTSTATUS write_trees(device_extension* Vcb) {
+ UINT8 level;
+ UINT8 *data, *body;
+ UINT32 crc32;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ write_tree_context* wtc;
+
+ TRACE("(%p)\n", Vcb);
+
+ for (level = 0; level <= 255; level++) {
+ BOOL nothing_found = TRUE;
+
+ TRACE("level = %u\n", level);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ KEY firstitem, searchkey;
+ LIST_ENTRY* le2;
+ traverse_ptr tp;
+ EXTENT_ITEM_TREE* eit;
+
+ if (tc2->tree->new_address == 0) {
+ ERR("error - tried to write tree with no new address\n");
+ int3;
+ }
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ firstitem = td->key;
+ break;
+ }
+ le2 = le2->Flink;
+ }
+
+ if (tc2->tree->parent) {
+ tc2->tree->paritem->key = firstitem;
+ tc2->tree->paritem->treeholder.address = tc2->tree->new_address;
+ tc2->tree->paritem->treeholder.generation = Vcb->superblock.generation;
+ }
+
+ if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) {
+ searchkey.obj_id = tc2->tree->new_address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = Vcb->superblock.node_size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+// traverse_ptr next_tp;
+// BOOL b;
+// tree_data* paritem;
+
+ ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ free_traverse_ptr(&tp);
+
+// searchkey.obj_id = 0;
+// searchkey.obj_type = 0;
+// searchkey.offset = 0;
+//
+// find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+//
+// paritem = NULL;
+// do {
+// if (tp.tree->paritem != paritem) {
+// paritem = tp.tree->paritem;
+// ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
+// }
+//
+// ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ eit = (EXTENT_ITEM_TREE*)tp.item->data;
+ eit->firstitem = firstitem;
+
+ free_traverse_ptr(&tp);
+ }
+
+ nothing_found = FALSE;
+ }
+
+ le = le->Flink;
+ }
+
+ if (nothing_found)
+ break;
+ }
+
+ TRACE("allocated tree extents\n");
+
+ wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG);
+ KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
+ InitializeListHead(&wtc->stripes);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+#ifdef DEBUG_PARANOID
+ UINT32 num_items = 0, size = 0;
+ LIST_ENTRY* le2;
+ BOOL crash = FALSE;
+#endif
+
+ if (tc2->write) {
+#ifdef DEBUG_PARANOID
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ num_items++;
+
+ if (tc2->tree->header.level == 0)
+ size += td->size;
+ }
+ le2 = le2->Flink;
+ }
+
+ if (tc2->tree->header.level == 0)
+ size += num_items * sizeof(leaf_node);
+ else
+ size += num_items * sizeof(internal_node);
+
+ if (num_items != tc2->tree->header.num_items) {
+ ERR("tree %llx, level %x: num_items was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
+ crash = TRUE;
+ }
+
+ if (size != tc2->tree->size) {
+ ERR("tree %llx, level %x: size was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, size, tc2->tree->size);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->new_address == 0) {
+ ERR("tree %llx, level %x: tried to write tree to address 0\n", tc2->tree->root->id, tc2->tree->header.level);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->header.num_items == 0) {
+ ERR("tree %llx, level %x: tried to write empty tree\n", tc2->tree->root->id, tc2->tree->header.level);
+ crash = TRUE;
+ }
+
+ if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", tc2->tree->root->id, tc2->tree->header.level, tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+ crash = TRUE;
+ }
+
+ if (crash) {
+ ERR("tree %p\n", tc2->tree);
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ ERR("%llx,%x,%llx inserted=%u\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->inserted);
+ }
+ le2 = le2->Flink;
+ }
+ int3;
+ }
+#endif
+ tc2->tree->header.address = tc2->tree->new_address;
+ tc2->tree->header.generation = Vcb->superblock.generation;
+ tc2->tree->header.flags |= HEADER_FLAG_MIXED_BACKREF;
+
+ data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+ body = data + sizeof(tree_header);
+
+ RtlCopyMemory(data, &tc2->tree->header, sizeof(tree_header));
+ RtlZeroMemory(body, Vcb->superblock.node_size - sizeof(tree_header));
+
+ if (tc2->tree->header.level == 0) {
+ leaf_node* itemptr = (leaf_node*)body;
+ int i = 0;
+ LIST_ENTRY* le2;
+ UINT8* dataptr = data + Vcb->superblock.node_size;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ dataptr = dataptr - td->size;
+
+ itemptr[i].key = td->key;
+ itemptr[i].offset = (UINT8*)dataptr - (UINT8*)body;
+ itemptr[i].size = td->size;
+ i++;
+
+ RtlCopyMemory(dataptr, td->data, td->size);
+ }
+
+ le2 = le2->Flink;
+ }
+ } else {
+ internal_node* itemptr = (internal_node*)body;
+ int i = 0;
+ LIST_ENTRY* le2;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore) {
+ itemptr[i].key = td->key;
+ itemptr[i].address = td->treeholder.address;
+ itemptr[i].generation = td->treeholder.generation;
+ i++;
+ }
+
+ le2 = le2->Flink;
+ }
+ }
+
+ crc32 = calc_crc32c(0xffffffff, (UINT8*)&((tree_header*)data)->fs_uuid, Vcb->superblock.node_size - sizeof(((tree_header*)data)->csum));
+ crc32 = ~crc32;
+ *((UINT32*)data) = crc32;
+ TRACE("setting crc32 to %08x\n", crc32);
+
+ Status = write_tree(Vcb, tc2->tree->new_address, data, wtc);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_tree returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ if (wtc->stripes.Flink != &wtc->stripes) {
+ // launch writes and wait
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ IoCallDriver(stripe->device->devobj, stripe->Irp);
+
+ le = le->Flink;
+ }
+
+ KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (!NT_SUCCESS(stripe->iosb.Status)) {
+ Status = stripe->iosb.Status;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ free_stripes(wtc);
+ }
+
+end:
+ ExFreePool(wtc);
+
+ return Status;
+}
+
+static NTSTATUS write_superblocks(device_extension* Vcb) {
+ UINT64 i;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && !tc2->tree->parent) {
+ if (tc2->tree->root == Vcb->root_root) {
+ Vcb->superblock.root_tree_addr = tc2->tree->new_address;
+ Vcb->superblock.root_level = tc2->tree->header.level;
+ } else if (tc2->tree->root == Vcb->chunk_root) {
+ Vcb->superblock.chunk_tree_addr = tc2->tree->new_address;
+ Vcb->superblock.chunk_root_generation = tc2->tree->header.generation;
+ Vcb->superblock.chunk_root_level = tc2->tree->header.level;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ if (Vcb->devices[i].devobj) {
+ Status = write_superblock(Vcb, &Vcb->devices[i]);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_superblock returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS update_chunk_usage(device_extension* Vcb) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ traverse_ptr tp;
+ BLOCK_GROUP_ITEM* bgi;
+
+ TRACE("(%p)\n", Vcb);
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->used != c->oldused) {
+ searchkey.obj_id = c->offset;
+ searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
+ searchkey.offset = c->chunk_item->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ int3;
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(bgi, tp.item->data, tp.item->size);
+ bgi->used = c->used;
+
+ TRACE("adjusting usage of chunk %llx to %llx\n", c->offset, c->used);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(bgi);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
+ TRACE("chunk_item type = %llx\n", c->chunk_item->type);
+
+ if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
+ FIXME("RAID0 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID1) {
+ FIXME("RAID1 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE) {
+ Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + (2 * (c->used - c->oldused));
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
+ FIXME("RAID10 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
+ FIXME("RAID5 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+ FIXME("RAID6 not yet supported\n");
+ ExFreePool(bgi);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ } else { // SINGLE
+ Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + c->used - c->oldused;
+ }
+
+ TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
+
+ free_traverse_ptr(&tp);
+
+ c->oldused = c->used;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void get_first_item(tree* t, KEY* key) {
+ LIST_ENTRY* le;
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ *key = td->key;
+ return;
+ }
+}
+
+static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirstitem, UINT32 numitems, UINT32 size) {
+ tree *nt, *pt;
+ tree_data* td;
+ tree_data* oldlastitem;
+// write_tree* wt2;
+// // tree_data *firsttd, *lasttd;
+// // LIST_ENTRY* le;
+// #ifdef DEBUG_PARANOID
+// KEY lastkey1, lastkey2;
+// traverse_ptr tp, next_tp;
+// ULONG numitems1, numitems2;
+// #endif
+
+ TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t->root->id, newfirstitem->key.obj_id, newfirstitem->key.obj_type, newfirstitem->key.offset);
+
+// #ifdef DEBUG_PARANOID
+// lastkey1.obj_id = 0xffffffffffffffff;
+// lastkey1.obj_type = 0xff;
+// lastkey1.offset = 0xffffffffffffffff;
+//
+// if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
+// ERR("error - find_item failed\n");
+// else {
+// lastkey1 = tp.item->key;
+// numitems1 = 0;
+// while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// numitems1++;
+// }
+// free_traverse_ptr(&tp);
+// }
+// #endif
+
+ nt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&nt->header, &t->header, sizeof(tree_header));
+ nt->header.address = 0;
+ nt->header.generation = Vcb->superblock.generation;
+ nt->header.num_items = t->header.num_items - numitems;
+ nt->header.flags = HEADER_FLAG_MIXED_BACKREF;
+
+ nt->refcount = 0;
+ nt->Vcb = Vcb;
+ nt->parent = t->parent;
+ nt->root = t->root;
+// nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ nt->new_address = 0;
+ nt->flags = t->flags;
+ InitializeListHead(&nt->itemlist);
+
+// ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
+
+ oldlastitem = CONTAINING_RECORD(newfirstitem->list_entry.Blink, tree_data, list_entry);
+
+// // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
+// // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
+// //
+// // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
+// // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
+// // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
+// //
+// // le = wt->tree->itemlist.Flink;
+// // while (le != &wt->tree->itemlist) {
+// // td = CONTAINING_RECORD(le, tree_data, list_entry);
+// // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // le = le->Flink;
+// // }
+
+ nt->itemlist.Flink = &newfirstitem->list_entry;
+ nt->itemlist.Blink = t->itemlist.Blink;
+ nt->itemlist.Flink->Blink = &nt->itemlist;
+ nt->itemlist.Blink->Flink = &nt->itemlist;
+
+ t->itemlist.Blink = &oldlastitem->list_entry;
+ t->itemlist.Blink->Flink = &t->itemlist;
+
+// // le = wt->tree->itemlist.Flink;
+// // while (le != &wt->tree->itemlist) {
+// // td = CONTAINING_RECORD(le, tree_data, list_entry);
+// // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // le = le->Flink;
+// // }
+// //
+// // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
+// // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
+// //
+// // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
+// // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
+// // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
+
+ nt->size = t->size - size;
+ t->size = size;
+ t->header.num_items = numitems;
+ add_to_tree_cache(Vcb, nt, TRUE);
+
+ InterlockedIncrement(&Vcb->open_trees);
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("created new split tree %p\n", nt);
+#endif
+ InsertTailList(&Vcb->trees, &nt->list_entry);
+
+// // // TESTING
+// // td = wt->tree->items;
+// // while (td) {
+// // if (!td->ignore) {
+// // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
+// // }
+// // td = td->next;
+// // }
+
+// // oldlastitem->next = NULL;
+// // wt->tree->lastitem = oldlastitem;
+
+// // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
+
+ if (nt->parent) {
+ increase_tree_rc(nt->parent);
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = newfirstitem->key;
+
+ InsertAfter(&t->itemlist, &td->list_entry, &t->paritem->list_entry);
+
+ td->ignore = FALSE;
+ td->inserted = TRUE;
+ td->treeholder.tree = nt;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ nt->paritem = td;
+
+ nt->parent->header.num_items++;
+ nt->parent->size += sizeof(internal_node);
+
+ goto end;
+ }
+
+ TRACE("adding new tree parent\n");
+
+ if (nt->header.level == 255) {
+ ERR("cannot add parent to tree at level 255\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ pt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ RtlCopyMemory(&pt->header, &nt->header, sizeof(tree_header));
+ pt->header.address = 0;
+ pt->header.num_items = 2;
+ pt->header.level = nt->header.level + 1;
+ pt->header.flags = HEADER_FLAG_MIXED_BACKREF;
+
+ pt->refcount = 2;
+ pt->Vcb = Vcb;
+ pt->parent = NULL;
+ pt->paritem = NULL;
+ pt->root = t->root;
+ pt->new_address = 0;
+// pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
+ pt->size = pt->header.num_items * sizeof(internal_node);
+ pt->flags = t->flags;
+ InitializeListHead(&pt->itemlist);
+
+// ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
+
+ InterlockedIncrement(&Vcb->open_trees);
+#ifdef DEBUG_TREE_REFCOUNTS
+ TRACE("created new parent tree %p\n", pt);
+#endif
+ InsertTailList(&Vcb->trees, &pt->list_entry);
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ get_first_item(t, &td->key);
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+ td->treeholder.address = 0;
+ td->treeholder.generation = Vcb->superblock.generation;
+ td->treeholder.tree = t;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ InsertTailList(&pt->itemlist, &td->list_entry);
+ t->paritem = td;
+
+ td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
+ td->key = newfirstitem->key;
+ td->ignore = FALSE;
+ td->inserted = FALSE;
+ td->treeholder.address = 0;
+ td->treeholder.generation = Vcb->superblock.generation;
+ td->treeholder.tree = nt;
+ init_tree_holder(&td->treeholder);
+// td->treeholder.nonpaged->status = tree_holder_loaded;
+ InsertTailList(&pt->itemlist, &td->list_entry);
+ nt->paritem = td;
+
+ add_to_tree_cache(Vcb, pt, TRUE);
+
+ t->root->treeholder.tree = pt;
+
+ t->parent = pt;
+ nt->parent = pt;
+
+end:
+
+// #ifdef DEBUG_PARANOID
+// lastkey2.obj_id = 0xffffffffffffffff;
+// lastkey2.obj_type = 0xff;
+// lastkey2.offset = 0xffffffffffffffff;
+//
+// if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
+// ERR("error - find_item failed\n");
+// else {
+// lastkey2 = tp.item->key;
+//
+// numitems2 = 0;
+// while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// numitems2++;
+// }
+// free_traverse_ptr(&tp);
+// }
+//
+// ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
+// ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
+// ERR("numitems1 = %u\n", numitems1);
+// ERR("numitems2 = %u\n", numitems2);
+// #endif
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL split_tree(device_extension* Vcb, tree* t) {
+ LIST_ENTRY* le;
+ UINT32 size, ds, numitems;
+
+ size = 0;
+ numitems = 0;
+
+ // FIXME - naïve implementation: maximizes number of filled trees
+
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ if (t->header.level == 0)
+ ds = sizeof(leaf_node) + td->size;
+ else
+ ds = sizeof(internal_node);
+
+ // FIXME - move back if previous item was deleted item with same key
+ if (size + ds > Vcb->superblock.node_size - sizeof(tree_header))
+ return split_tree_at(Vcb, t, td, numitems, size);
+
+ size += ds;
+ numitems++;
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t) {
+ LIST_ENTRY* le;
+ tree_data* nextparitem = NULL;
+ NTSTATUS Status;
+ tree *next_tree, *par;
+
+ TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t->root->id, t->header.level, t->size);
+
+ // FIXME - doesn't capture everything, as it doesn't ascend
+ // FIXME - write proper function and put it in treefuncs.c
+ le = t->paritem->list_entry.Flink;
+ while (le != &t->parent->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ nextparitem = td;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!nextparitem)
+ return STATUS_SUCCESS;
+
+ // FIXME - loop, and capture more than one tree if we can
+
+ TRACE("nextparitem: key = %llx,%x,%llx, address = %llx, tree = %p\n", nextparitem->key.obj_id, nextparitem->key.obj_type, nextparitem->key.offset, nextparitem->address, nextparitem->tree);
+// nextparitem = t->paritem;
+
+// ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
+
+ if (do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem))
+ increase_tree_rc(t->parent);
+
+// ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
+
+ next_tree = nextparitem->treeholder.tree;
+
+ if (t->size + next_tree->size <= Vcb->superblock.node_size - sizeof(tree_header)) {
+ // merge two trees into one
+
+ t->header.num_items += next_tree->header.num_items;
+ t->size += next_tree->size;
+
+ t->itemlist.Blink->Flink = next_tree->itemlist.Flink;
+ t->itemlist.Blink->Flink->Blink = t->itemlist.Blink;
+ t->itemlist.Blink = next_tree->itemlist.Blink;
+ t->itemlist.Blink->Flink = &t->itemlist;
+
+// // TESTING
+// le = t->itemlist.Flink;
+// while (le != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+// if (!td->ignore) {
+// ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+// }
+// le = le->Flink;
+// }
+
+ next_tree->itemlist.Flink = next_tree->itemlist.Blink = &next_tree->itemlist;
+
+ next_tree->header.num_items = 0;
+ next_tree->size = 0;
+
+ if (next_tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ free_tree(next_tree);
+ return Status;
+ }
+ } else if (next_tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ free_tree(next_tree);
+ return Status;
+ }
+ }
+
+ if (!nextparitem->ignore) {
+ nextparitem->ignore = TRUE;
+ next_tree->parent->header.num_items--;
+ next_tree->parent->size -= sizeof(internal_node);
+ }
+
+ par = next_tree->parent;
+ while (par) {
+ add_to_tree_cache(Vcb, par, TRUE);
+ par = par->parent;
+ }
+
+ RemoveEntryList(&nextparitem->list_entry);
+ ExFreePool(next_tree->paritem);
+ next_tree->paritem = NULL;
+
+ free_tree(next_tree);
+
+ // remove next_tree from tree cache
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->tree == next_tree) {
+ free_tree(next_tree);
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ break;
+ }
+
+ le = le->Flink;
+ }
+ } else {
+ // rebalance by moving items from second tree into first
+ ULONG avg_size = (t->size + next_tree->size) / 2;
+ KEY firstitem = {0, 0, 0};
+
+ TRACE("attempting rebalance\n");
+
+ le = next_tree->itemlist.Flink;
+ while (le != &next_tree->itemlist && t->size < avg_size && next_tree->header.num_items > 1) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+ ULONG size;
+
+ if (!td->ignore) {
+ if (next_tree->header.level == 0)
+ size = sizeof(leaf_node) + td->size;
+ else
+ size = sizeof(internal_node);
+ } else
+ size = 0;
+
+ if (t->size + size < Vcb->superblock.node_size - sizeof(tree_header)) {
+ RemoveEntryList(&td->list_entry);
+ InsertTailList(&t->itemlist, &td->list_entry);
+
+ if (!td->ignore) {
+ next_tree->size -= size;
+ t->size += size;
+ next_tree->header.num_items--;
+ t->header.num_items++;
+ }
+ } else
+ break;
+
+ le = next_tree->itemlist.Flink;
+ }
+
+ le = next_tree->itemlist.Flink;
+ while (le != &next_tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ firstitem = td->key;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+// ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
+
+ // FIXME - once ascension is working, make this work with parent's parent, etc.
+ if (next_tree->paritem)
+ next_tree->paritem->key = firstitem;
+
+ par = next_tree;
+ while (par) {
+ add_to_tree_cache(Vcb, par, TRUE);
+ par = par->parent;
+ }
+
+ free_tree(next_tree);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL do_splits(device_extension* Vcb) {
+// LIST_ENTRY *le, *le2;
+// write_tree* wt;
+// tree_data* td;
+ UINT8 level, max_level;
+ UINT32 min_size;
+ BOOL empty, done_deletions = FALSE;
+ NTSTATUS Status;
+ tree_cache* tc2;
+
+ TRACE("(%p)\n", Vcb);
+
+ max_level = 0;
+
+ for (level = 0; level <= 255; level++) {
+ LIST_ENTRY *le, *nextle;
+
+ empty = TRUE;
+
+ TRACE("doing level %u\n", level);
+
+ le = Vcb->tree_cache.Flink;
+
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ nextle = le->Flink;
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ empty = FALSE;
+
+ if (tc2->tree->header.num_items == 0) {
+ LIST_ENTRY* le2;
+ KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
+
+ done_deletions = TRUE;
+
+ le2 = tc2->tree->itemlist.Flink;
+ while (le2 != &tc2->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ firstitem = td->key;
+ break;
+ }
+
+ ERR("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
+ tc2->tree->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
+
+ if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ if (tc2->tree->parent) {
+ if (!tc2->tree->paritem->ignore) {
+ tc2->tree->paritem->ignore = TRUE;
+ tc2->tree->parent->header.num_items--;
+ tc2->tree->parent->size -= sizeof(internal_node);
+ }
+
+ RemoveEntryList(&tc2->tree->paritem->list_entry);
+ ExFreePool(tc2->tree->paritem);
+ tc2->tree->paritem = NULL;
+
+ free_tree(tc2->tree);
+
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ } else {
+ FIXME("trying to delete top root, not sure what to do here\n"); // FIXME
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ TRACE("splitting overlarge tree (%x > %x)\n", tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+ Status = split_tree(Vcb, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("split_tree returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+ }
+
+ le = nextle;
+ }
+
+ if (!empty) {
+ max_level = level;
+ } else {
+ TRACE("nothing found for level %u\n", level);
+ break;
+ }
+ }
+
+ min_size = (Vcb->superblock.node_size - sizeof(tree_header)) / 2;
+
+ for (level = 0; level <= max_level; level++) {
+ LIST_ENTRY* le;
+
+ le = Vcb->tree_cache.Flink;
+
+ while (le != &Vcb->tree_cache) {
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level && tc2->tree->header.num_items > 0 && tc2->tree->parent && tc2->tree->size < min_size) {
+ Status = try_tree_amalgamate(Vcb, tc2->tree);
+ if (!NT_SUCCESS(Status)) {
+ ERR("try_tree_amalgamate returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ // simplify trees if top tree only has one entry
+
+ if (done_deletions) {
+ for (level = max_level; level > 0; level--) {
+ LIST_ENTRY *le, *nextle;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ nextle = le->Flink;
+ tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ if (tc2->write && tc2->tree->header.level == level) {
+ if (!tc2->tree->parent && tc2->tree->header.num_items == 1) {
+ LIST_ENTRY* le2 = tc2->tree->itemlist.Flink;
+ tree_data* td;
+ tree* child_tree = NULL;
+
+ while (le2 != &tc2->tree->itemlist) {
+ td = CONTAINING_RECORD(le2, tree_data, list_entry);
+ if (!td->ignore)
+ break;
+ le2 = le2->Flink;
+ }
+
+ ERR("deleting top-level tree in root %llx with one item\n", tc2->tree->root->id);
+
+ if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else if (tc2->tree->header.address != 0) {
+ Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ if (!td->treeholder.tree) { // load first item if not already loaded
+ KEY searchkey = {0,0,0};
+ traverse_ptr tp;
+
+ if (!find_item(Vcb, tc2->tree->root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol %llx\n", tc2->tree->root->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ child_tree = td->treeholder.tree;
+
+ if (child_tree) {
+ child_tree->parent = NULL;
+ free_tree(tc2->tree);
+ }
+
+ free_tree(tc2->tree);
+
+ if (child_tree)
+ child_tree->root->treeholder.tree = child_tree;
+
+ RemoveEntryList(le);
+ ExFreePool(tc2);
+ }
+ }
+
+ le = nextle;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL do_write(device_extension* Vcb) {
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+
+ TRACE("(%p)\n", Vcb);
+
+ // If only changing superblock, e.g. changing label, we still need to rewrite
+ // the root tree so the generations mach. Otherwise you won't be able to mount on Linux.
+ if (Vcb->write_trees > 0) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in root_root\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ add_to_tree_cache(Vcb, Vcb->root_root->treeholder.tree, TRUE);
+
+ free_traverse_ptr(&tp);
+ }
+
+ do {
+ Status = add_parents(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_parents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = do_splits(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_splits returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = allocate_tree_extents(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_parents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = update_chunk_usage(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_chunk_usage returned %08x\n", Status);
+ goto end;
+ }
+ } while (!trees_consistent(Vcb));
+
+ TRACE("trees consistent\n");
+
+ Status = update_root_root(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_root returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = write_trees(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_trees returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = write_superblocks(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_superblocks returned %08x\n", Status);
+ goto end;
+ }
+
+ clean_space_cache(Vcb);
+
+ Vcb->superblock.generation++;
+
+// print_trees(tc); // TESTING
+
+ Status = STATUS_SUCCESS;
+
+ le = Vcb->tree_cache.Flink;
+ while (le != &Vcb->tree_cache) {
+ tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+
+ tc2->write = FALSE;
+
+ le = le->Flink;
+ }
+
+ Vcb->write_trees = 0;
+
+end:
+ TRACE("do_write returning %08x\n", Status);
+
+ return Status;
+}
+
+NTSTATUS consider_write(device_extension* Vcb) {
+ // FIXME - call do_write if Vcb->write_trees high
+
+ return STATUS_SUCCESS;
+}
+
+static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* ins) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > ins->key) {
+ le->Blink->Flink = &ins->list_entry;
+ ins->list_entry.Blink = le->Blink;
+ le->Blink = &ins->list_entry;
+ ins->list_entry.Flink = le;
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ InsertTailList(list, &ins->list_entry);
+}
+
+static UINT64 get_extent_data_ref_hash(UINT64 root, UINT64 objid, UINT64 offset) {
+ UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff;
+
+ // FIXME - can we test this?
+
+ // FIXME - make sure numbers here are little-endian
+ high_crc = calc_crc32c(high_crc, (UINT8*)&root, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&objid, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&offset, sizeof(UINT64));
+
+ return ((UINT64)high_crc << 31) ^ (UINT64)low_crc;
+}
+
+NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT8 *siptr, *type;
+ ULONG len;
+ UINT64 hash;
+ EXTENT_DATA_REF* edr;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ // create new entry
+
+ len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+ free_traverse_ptr(&tp);
+
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+ ei->refcount = 1;
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = EXTENT_ITEM_DATA;
+
+ type = (UINT8*)&ei[1];
+ *type = TYPE_EXTENT_DATA_REF;
+
+ edr = (EXTENT_DATA_REF*)&type[1];
+ edr->root = subvol->id;
+ edr->objid = inode;
+ edr->offset = offset;
+ edr->count = 1;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - update free space in superblock and CHUNK_ITEM
+
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ }
+
+ if (ei->flags != EXTENT_ITEM_DATA) {
+ ERR("error - flag was not EXTENT_ITEM_DATA\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
+
+ hash = get_extent_data_ref_hash(subvol->id, inode, offset);
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ siptr = (UINT8*)&ei[1];
+
+ // FIXME - increase subitem refcount if there already?
+ do {
+ if (*siptr == TYPE_EXTENT_DATA_REF) {
+ UINT64 sihash;
+
+ edr = (EXTENT_DATA_REF*)&siptr[1];
+ sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset);
+
+ if (sihash >= hash)
+ break;
+
+ siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+
+ if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+ len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+ } else
+ break;
+ // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+
+ RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+ ei->refcount++;
+
+ type = (UINT8*)ei + (siptr - tp.item->data);
+ *type = TYPE_EXTENT_DATA_REF;
+
+ edr = (EXTENT_DATA_REF*)&type[1];
+ edr->root = subvol->id;
+ edr->objid = inode;
+ edr->offset = offset;
+ edr->count = 1;
+
+ if (siptr < tp.item->data + tp.item->size)
+ RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr);
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ EXTENT_DATA_REF edr;
+ LIST_ENTRY list_entry;
+} data_ref;
+
+static void add_data_ref(LIST_ENTRY* data_refs, UINT64 root, UINT64 objid, UINT64 offset) {
+ data_ref* dr = ExAllocatePoolWithTag(PagedPool, sizeof(data_ref), ALLOC_TAG);
+
+ // FIXME - increase count if entry there already
+ // FIXME - put in order?
+
+ dr->edr.root = root;
+ dr->edr.objid = objid;
+ dr->edr.offset = offset;
+ dr->edr.count = 1;
+
+ InsertTailList(data_refs, &dr->list_entry);
+}
+
+static void free_data_refs(LIST_ENTRY* data_refs) {
+ LIST_ENTRY* le;
+
+ while ((le = RemoveHeadList(data_refs)) != data_refs) {
+ data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
+
+ ExFreePool(dr);
+ }
+}
+
+static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LIST_ENTRY data_refs;
+ LIST_ENTRY* le;
+ UINT64 refcount;
+ EXTENT_ITEM* ei;
+ ULONG eisize;
+ UINT8* type;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size != sizeof(EXTENT_ITEM_V0)) {
+ TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ InitializeListHead(&data_refs);
+
+ do {
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ tree* t;
+
+ // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
+
+ t = load_tree(Vcb, tp.item->key.offset, NULL);
+
+ if (!t) {
+ ERR("couldn't load tree at %llx\n", tp.item->key.offset);
+ free_traverse_ptr(&tp);
+ free_data_refs(&data_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0) {
+ le = t->itemlist.Flink;
+ while (le != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address == address)
+ add_data_ref(&data_refs, t->header.tree_id, td->key.obj_id, td->key.offset);
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ free_tree(t);
+
+ delete_tree_item(Vcb, &tp);
+ }
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ free_traverse_ptr(&tp);
+
+ if (IsListEmpty(&data_refs)) {
+ WARN("no data refs found\n");
+ return STATUS_SUCCESS;
+ }
+
+ // create new entry
+
+ refcount = 0;
+
+ le = data_refs.Flink;
+ while (le != &data_refs) {
+ refcount++;
+ le = le->Flink;
+ }
+
+ eisize = sizeof(EXTENT_ITEM) + ((sizeof(char) + sizeof(EXTENT_DATA_REF)) * refcount);
+ ei = ExAllocatePoolWithTag(PagedPool, eisize, ALLOC_TAG);
+
+ ei->refcount = refcount;
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = EXTENT_ITEM_DATA;
+
+ type = (UINT8*)&ei[1];
+
+ le = data_refs.Flink;
+ while (le != &data_refs) {
+ data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
+
+ type[0] = TYPE_EXTENT_DATA_REF;
+ RtlCopyMemory(&type[1], &dr->edr, sizeof(EXTENT_DATA_REF));
+
+ type = &type[1 + sizeof(EXTENT_DATA_REF)];
+
+ le = le->Flink;
+ }
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_data_refs(&data_refs);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct {
+ UINT8 type;
+ void* data;
+ BOOL allocated;
+ LIST_ENTRY list_entry;
+} extent_ref;
+
+static void free_extent_refs(LIST_ENTRY* extent_refs) {
+ LIST_ENTRY* le;
+
+ while ((le = RemoveHeadList(extent_refs)) != extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+}
+
+static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size) {
+ KEY searchkey;
+ traverse_ptr tp;
+ LIST_ENTRY extent_refs;
+ LIST_ENTRY *le, *next_le;
+ EXTENT_ITEM *ei, *newei;
+ UINT8* siptr;
+ ULONG len;
+ UINT64 count;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+
+ InitializeListHead(&extent_refs);
+
+ siptr = (UINT8*)&ei[1];
+
+ do {
+ extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+
+ er->type = *siptr;
+ er->data = siptr+1;
+ er->allocated = FALSE;
+
+ InsertTailList(&extent_refs, &er->list_entry);
+
+ if (*siptr == TYPE_TREE_BLOCK_REF) {
+ siptr += sizeof(TREE_BLOCK_REF);
+ len -= sizeof(TREE_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_EXTENT_DATA_REF) {
+ siptr += sizeof(EXTENT_DATA_REF);
+ len -= sizeof(EXTENT_DATA_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
+ siptr += sizeof(SHARED_BLOCK_REF);
+ len -= sizeof(SHARED_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_DATA_REF) {
+ siptr += sizeof(SHARED_DATA_REF);
+ len -= sizeof(SHARED_DATA_REF) + 1;
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ next_le = le->Flink;
+
+ if (er->type == TYPE_SHARED_DATA_REF) {
+ // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
+ SHARED_DATA_REF* sdr = er->data;
+ tree* t = load_tree(Vcb, sdr->offset, NULL);
+
+ if (!t) {
+ ERR("couldn't load tree at %llx\n", sdr->offset);
+ free_traverse_ptr(&tp);
+ free_data_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (t->header.level == 0) {
+ LIST_ENTRY* le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+
+ if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->address == address) {
+ extent_ref* er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+ EXTENT_DATA_REF* edr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA_REF), ALLOC_TAG);
+
+ edr->root = t->header.tree_id;
+ edr->objid = td->key.obj_id;
+ edr->offset = td->key.offset;
+ edr->count = 1;
+
+ er2->type = TYPE_EXTENT_DATA_REF;
+ er2->data = edr;
+ er2->allocated = TRUE;
+
+ InsertTailList(&extent_refs, &er2->list_entry); // FIXME - list should be in order
+ }
+ }
+ }
+
+ le2 = le2->Flink;
+ }
+ }
+
+ free_tree(t);
+
+ RemoveEntryList(&er->list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+ // FIXME - also do for SHARED_BLOCK_REF?
+
+ le = next_le;
+ }
+
+ if (IsListEmpty(&extent_refs)) {
+ WARN("no extent refs found\n");
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ len = 0;
+ count = 0;
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ len++;
+ if (er->type == TYPE_TREE_BLOCK_REF) {
+ len += sizeof(TREE_BLOCK_REF);
+ } else if (er->type == TYPE_EXTENT_DATA_REF) {
+ len += sizeof(EXTENT_DATA_REF);
+ } else {
+ ERR("unexpected extent subitem %x\n", er->type);
+ }
+
+ count++;
+
+ le = le->Flink;
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM) + len, ALLOC_TAG);
+ RtlCopyMemory(newei, ei, sizeof(EXTENT_ITEM));
+ newei->refcount = count;
+
+ siptr = (UINT8*)&newei[1];
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ *siptr = er->type;
+ siptr++;
+
+ if (er->type == TYPE_TREE_BLOCK_REF) {
+ RtlCopyMemory(siptr, er->data, sizeof(TREE_BLOCK_REF));
+ } else if (er->type == TYPE_EXTENT_DATA_REF) {
+ RtlCopyMemory(siptr, er->data, sizeof(EXTENT_DATA_REF));
+ } else {
+ ERR("unexpected extent subitem %x\n", er->type);
+ }
+
+ le = le->Flink;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, newei, sizeof(EXTENT_ITEM) + len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(newei);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ free_extent_refs(&extent_refs);
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len) {
+ UINT8* siptr = (UINT8*)&ei[1];
+
+ do {
+ if (*siptr == TYPE_TREE_BLOCK_REF) {
+ siptr += sizeof(TREE_BLOCK_REF) + 1;
+ len -= sizeof(TREE_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_EXTENT_DATA_REF) {
+ siptr += sizeof(EXTENT_DATA_REF) + 1;
+ len -= sizeof(EXTENT_DATA_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
+ return TRUE;
+ } else if (*siptr == TYPE_SHARED_DATA_REF) {
+ return TRUE;
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ return FALSE;
+ }
+ } while (len > 0);
+
+ return FALSE;
+}
+
+NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT8* siptr;
+ ULONG len;
+ EXTENT_DATA_REF* edr;
+ BOOL found;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ if (!(ei->flags & EXTENT_ITEM_DATA)) {
+ ERR("error - EXTENT_ITEM_DATA flag not set\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
+
+ if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, address, size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+
+ free_traverse_ptr(&tp);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ }
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ siptr = (UINT8*)&ei[1];
+ found = FALSE;
+
+ do {
+ if (*siptr == TYPE_EXTENT_DATA_REF) {
+ edr = (EXTENT_DATA_REF*)&siptr[1];
+
+ if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
+ found = TRUE;
+ break;
+ }
+
+ siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+
+ if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+ len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+ } else
+ break;
+// // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (len > 0);
+
+ if (!found) {
+ WARN("could not find extent data ref\n");
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ // FIXME - decrease subitem refcount if there already?
+
+ len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF);
+
+ delete_tree_item(Vcb, &tp);
+
+ if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed
+ chunk* c;
+ LIST_ENTRY* le2;
+
+ if (changed_sector_list) {
+ changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = address;
+ sc->checksums = NULL;
+ sc->length = size / Vcb->superblock.sector_size;
+
+ sc->deleted = TRUE;
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ c = NULL;
+ le2 = Vcb->chunks.Flink;
+ while (le2 != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le2, chunk, list_entry);
+
+ TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
+
+ if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
+ break;
+
+ le2 = le2->Flink;
+ }
+ if (le2 == &Vcb->chunks) c = NULL;
+
+ if (c) {
+ decrease_chunk_usage(c, size);
+
+ add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
+ }
+
+ free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+ }
+
+ ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+ RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+ ei->refcount--;
+ ei->generation = Vcb->superblock.generation;
+
+ if (tp.item->data + len != siptr)
+ RtlCopyMemory((UINT8*)ei + (siptr - tp.item->data), siptr + sizeof(UINT8) + sizeof(EXTENT_DATA_REF), tp.item->size - (siptr - tp.item->data) - sizeof(UINT8) - sizeof(EXTENT_DATA_REF));
+
+ free_traverse_ptr(&tp);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > value)
+ return FALSE;
+ else if (ol->key == value)
+ return TRUE;
+
+ le = le->Flink;
+ }
+
+ return FALSE;
+}
+
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, changed_sector_list);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.offset < end_data && tp.item->key.offset + ed->decoded_size >= start_data) {
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME - compression not supported at present\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, ed->encryption);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ // FIXME - is ed->decoded_size the size of the whole extent, or just this bit of it?
+
+ if (ed->type == EXTENT_TYPE_INLINE) {
+ if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+ delete_tree_item(Vcb, &tp);
+
+ fcb->inode_item.st_blocks -= ed->decoded_size;
+ } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = ed->decoded_size - (end_data - tp.item->key.offset);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = start_data - tp.item->key.offset;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[0], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
+ } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+ EXTENT_DATA* ned;
+ UINT64 size;
+
+ delete_tree_item(Vcb, &tp);
+
+ size = start_data - tp.item->key.offset;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[0], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ size = tp.item->key.offset + ed->decoded_size - end_data;
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = size;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+
+ RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - start_data;
+ }
+ } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+
+ if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+ if (ed2->address != 0) {
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= ed->decoded_size;
+ }
+
+ delete_tree_item(Vcb, &tp);
+ } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = ed->decoded_size - (end_data - tp.item->key.offset);
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
+ ned2->num_bytes = ed2->num_bytes - (end_data - tp.item->key.offset);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0)
+ fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = start_data - tp.item->key.offset;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+ EXTENT_DATA* ned;
+ EXTENT_DATA2* ned2;
+
+ if (ed2->address != 0) {
+ Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_extent_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->inode_item.st_blocks -= end_data - start_data;
+ }
+
+ delete_tree_item(Vcb, &tp);
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = start_data - tp.item->key.offset;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ ned2 = (EXTENT_DATA2*)&ned->data[0];
+
+ ned->generation = Vcb->superblock.generation;
+ ned->decoded_size = tp.item->key.offset + ed->decoded_size - end_data;
+ ned->compression = ed->compression;
+ ned->encryption = ed->encryption;
+ ned->encoding = ed->encoding;
+ ned->type = ed->type;
+ ned2->address = ed2->address;
+ ned2->size = ed2->size;
+ ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
+ ned2->num_bytes = tp.item->key.offset + ed->decoded_size - end_data;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ }
+ }
+ }
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > fcb->inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
+ break;
+ }
+ } while (b);
+
+ // FIXME - do bitmap analysis of changed extents, and free what we can
+
+ Status = STATUS_SUCCESS;
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+static BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ UINT64 address;
+ NTSTATUS Status;
+ EXTENT_ITEM_DATA_REF* eidr;
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
+ changed_sector* sc;
+ traverse_ptr tp;
+ int i;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, data, changed_sector_list);
+
+ if (!find_address_in_chunk(Vcb, c, length, &address))
+ return FALSE;
+
+ eidr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_DATA_REF), ALLOC_TAG);
+ eidr->ei.refcount = 1;
+ eidr->ei.generation = Vcb->superblock.generation;
+ eidr->ei.flags = EXTENT_ITEM_DATA;
+ eidr->type = TYPE_EXTENT_DATA_REF;
+ eidr->edr.root = fcb->subvol->id;
+ eidr->edr.objid = fcb->inode;
+ eidr->edr.offset = start_data;
+ eidr->edr.count = 1;
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, length, eidr, sizeof(EXTENT_ITEM_DATA_REF), &tp)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(eidr);
+ return FALSE;
+ }
+
+ tp.tree->header.generation = eidr->ei.generation;
+
+ free_traverse_ptr(&tp);
+
+ Status = write_data(Vcb, address, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (changed_sector_list) {
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = address;
+ sc->length = length / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ // add extent data to inode
+ ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
+ ed->generation = Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = address;
+ ed2->size = length;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, ed, edsize, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ed);
+ return FALSE;
+ }
+
+ increase_chunk_usage(c, length);
+ add_to_space_list(c, address, length, SPACE_TYPE_WRITING);
+
+ fcb->inode_item.st_blocks += length;
+
+ return TRUE;
+}
+
+static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
+ LIST_ENTRY* changed_sector_list, traverse_ptr* edtp, traverse_ptr* eitp) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ EXTENT_ITEM* ei;
+ NTSTATUS Status;
+ changed_sector* sc;
+ chunk* c;
+ int i;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data,
+ length, data, changed_sector_list, edtp, eitp);
+
+ ed = ExAllocatePoolWithTag(PagedPool, edtp->item->size, ALLOC_TAG);
+ RtlCopyMemory(ed, edtp->item->data, edtp->item->size);
+
+ ed->decoded_size += length;
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->size += length;
+ ed2->num_bytes += length;
+
+ delete_tree_item(Vcb, edtp);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, edtp->item->key.obj_id, edtp->item->key.obj_type, edtp->item->key.offset, ed, edtp->item->size, NULL)) {
+ TRACE("insert_tree_item failed\n");
+
+ ExFreePool(ed);
+ return FALSE;
+ }
+
+ ei = ExAllocatePoolWithTag(PagedPool, eitp->item->size, ALLOC_TAG);
+ RtlCopyMemory(ei, eitp->item->data, eitp->item->size);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, eitp->item->key.obj_id, eitp->item->key.obj_type, eitp->item->key.offset + length, ei, eitp->item->size, NULL)) {
+ ERR("insert_tree_item failed\n");
+
+ ExFreePool(ei);
+ return FALSE;
+ }
+
+ delete_tree_item(Vcb, eitp);
+
+ Status = write_data(Vcb, eitp->item->key.obj_id + eitp->item->key.offset, data, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (changed_sector_list) {
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = eitp->item->key.obj_id + eitp->item->key.offset;
+ sc->length = length / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ c = get_chunk_from_address(Vcb, eitp->item->key.obj_id);
+
+ if (c) {
+ increase_chunk_usage(c, length);
+
+ add_to_space_list(c, eitp->item->key.obj_id + eitp->item->key.offset, length, SPACE_TYPE_WRITING);
+ }
+
+ fcb->inode_item.st_blocks += length;
+
+ return TRUE;
+}
+
+static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
+ LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ BOOL success = FALSE;
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ EXTENT_ITEM* ei;
+ chunk* c;
+ LIST_ENTRY* le;
+ space* s;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find last EXTENT_DATA item\n");
+ return FALSE;
+ }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
+ WARN("previous EXTENT_DATA not found\n");
+ goto end;
+ }
+
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (ed->type != EXTENT_TYPE_REGULAR) {
+ TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
+ goto end;
+ }
+
+ if (tp.item->key.offset + ed->decoded_size != start_data) {
+ TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp.item->key.offset, ed->decoded_size, start_data);
+ goto end;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME: compression not yet supported\n");
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("encryption not supported\n");
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ goto end;
+ }
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (ed2->size - ed2->offset != ed->decoded_size) {
+ TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
+ goto end;
+ }
+
+ searchkey.obj_id = ed2->address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = ed2->size;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find extent in tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ ERR("error - extent %llx,%llx not found in tree\n", ed2->address, ed2->size);
+ int3; // TESTING
+ goto end2;
+ }
+
+ if (tp2.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, ed2->address, ed2->size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ goto end2;
+ }
+
+ free_traverse_ptr(&tp2);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end2;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+
+ // FIXME - test this
+ if (extent_item_is_shared(ei, tp2.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, ed2->address, ed2->size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ goto end2;
+ }
+
+ free_traverse_ptr(&tp2);
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent tree\n");
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end2;
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+ }
+
+ if (ei->refcount != 1) {
+ TRACE("extent refcount was not 1\n");
+ goto end2;
+ }
+
+ if (ei->flags != EXTENT_ITEM_DATA) {
+ ERR("error - extent was not a data extent\n");
+ goto end2;
+ }
+
+ c = get_chunk_from_address(Vcb, ed2->address);
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset == ed2->address + ed2->size) {
+ if (s->type == SPACE_TYPE_FREE && s->size >= length) {
+ success = extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, &tp, &tp2);
+ }
+ break;
+ } else if (s->offset > ed2->address + ed2->size)
+ break;
+
+ le = le->Flink;
+ }
+
+end2:
+ free_traverse_ptr(&tp2);
+
+end:
+ free_traverse_ptr(&tp);
+
+ return success;
+}
+
+NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb, r->id, inode, start, length);
+
+ ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+
+ ed->generation = Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = 0;
+ ed2->size = 0;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(Vcb, r, inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ed);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+// static void print_tree(tree* t) {
+// LIST_ENTRY* le = t->itemlist.Flink;
+// while (le != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+// ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
+// le = le->Flink;
+// }
+// }
+
+static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ UINT64 flags;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
+
+ // FIXME - split data up if not enough space for just one extent
+
+ if (start_data > 0 && try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+
+ // if there is a gap before start_data, plug it with a sparse extent
+ if (start_data > 0) {
+ traverse_ptr tp;
+ NTSTATUS Status;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+// if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
+// traverse_ptr next_tp;
+//
+// ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
+// searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+// print_tree(tp.tree);
+//
+// if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+// ERR("---\n");
+// ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset);
+// print_tree(next_tp.tree);
+//
+// free_traverse_ptr(&next_tp);
+// } else
+// ERR("next item not found\n");
+//
+// int3;
+// free_traverse_ptr(&tp);
+// return STATUS_INTERNAL_ERROR;
+// }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size < start_data) {
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data);
+ else {
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size,
+ start_data - tp.item->key.offset - ((EXTENT_DATA*)tp.item->data)->decoded_size);
+ }
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ free_traverse_ptr(&tp);
+ return Status;
+ }
+ }
+
+ free_traverse_ptr(&tp);
+ }
+
+ // FIXME - how do we know which RAID level to put this to?
+ flags = BLOCK_FLAG_DATA; // SINGLE
+
+// if (!chunk_test) { // TESTING
+// if ((c = alloc_chunk(Vcb, flags, NULL))) {
+// ERR("chunk_item->type = %llx\n", c->chunk_item->type);
+// ERR("size = %llx\n", c->chunk_item->size);
+// ERR("used = %llx\n", c->used);
+//
+// if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+// if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
+// // chunk_test = TRUE;
+// ERR("SUCCESS\n");
+// return STATUS_SUCCESS;
+// } else
+// ERR(":-(\n");
+// } else
+// ERR("???\n");
+// }
+// }
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ if ((c = alloc_chunk(Vcb, flags))) {
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ // FIXME - rebalance chunks if free space elsewhere?
+ WARN("couldn't find any data chunks with %llx bytes free\n", length);
+
+ return STATUS_DISK_FULL;
+}
+
+void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list) {
+ LIST_ENTRY* le = changed_sector_list->Flink;
+ changed_sector* cs;
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT32* data;
+
+ // FIXME - use local tree_cache
+
+ if (!Vcb->checksum_root) {
+ ERR("no checksum root\n");
+ goto exit;
+ }
+
+ while (le != changed_sector_list) {
+ UINT64 startaddr, endaddr;
+ ULONG len;
+ UINT32* checksums;
+ RTL_BITMAP bmp;
+ ULONG* bmparr;
+ ULONG runlength, index;
+
+ cs = (changed_sector*)le;
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ // FIXME - check entry is TYPE_EXTENT_CSUM?
+
+ if (tp.item->key.offset < cs->ol.key && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key)
+ startaddr = tp.item->key.offset;
+ else
+ startaddr = cs->ol.key;
+
+ free_traverse_ptr(&tp);
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ if (tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key + (cs->length * Vcb->superblock.sector_size))
+ endaddr = tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32));
+ else
+ endaddr = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ free_traverse_ptr(&tp);
+
+ TRACE("cs starts at %llx (%x sectors)\n", cs->ol.key, cs->length);
+ TRACE("startaddr = %llx\n", startaddr);
+ TRACE("endaddr = %llx\n", endaddr);
+
+ len = (endaddr - startaddr) / Vcb->superblock.sector_size;
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * len, ALLOC_TAG);
+ bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG);
+
+ RtlInitializeBitMap(&bmp, bmparr, len);
+ RtlSetAllBits(&bmp);
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
+
+ if (!find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in extent_root\n");
+ goto exit;
+ }
+
+ // set bit = free space, cleared bit = allocated sector
+
+// ERR("start loop\n");
+ while (tp.item->key.offset < endaddr) {
+// ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ if (tp.item->key.offset >= startaddr) {
+ RtlCopyMemory(&checksums[(tp.item->key.offset - startaddr) / Vcb->superblock.sector_size], tp.item->data, tp.item->size);
+
+ RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, tp.item->size / sizeof(UINT32));
+
+ delete_tree_item(Vcb, &tp);
+ }
+
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+ } else
+ break;
+ }
+// ERR("end loop\n");
+
+ free_traverse_ptr(&tp);
+
+ if (cs->deleted) {
+ RtlSetBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ } else {
+ RtlCopyMemory(&checksums[(cs->ol.key - startaddr) / Vcb->superblock.sector_size], cs->checksums, cs->length * sizeof(UINT32));
+ RtlClearBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ }
+
+ runlength = RtlFindFirstRunClear(&bmp, &index);
+
+ while (runlength != 0) {
+ do {
+ ULONG rl;
+
+ if (runlength * sizeof(UINT32) > MAX_CSUM_SIZE)
+ rl = MAX_CSUM_SIZE / sizeof(UINT32);
+ else
+ rl = runlength;
+
+ data = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * rl, ALLOC_TAG);
+ RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
+
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(data);
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+ goto exit;
+ }
+
+ runlength -= rl;
+ index += rl;
+ } while (runlength > 0);
+
+ runlength = RtlFindNextForwardRunClear(&bmp, index, &index);
+ }
+
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+
+ le = le->Flink;
+ }
+
+exit:
+ while ((le = RemoveHeadList(changed_sector_list)) != changed_sector_list) {
+ cs = (changed_sector*)le;
+
+ if (cs->checksums)
+ ExFreePool(cs->checksums);
+
+ ExFreePool(cs);
+ }
+}
+
+NTSTATUS truncate_file(fcb* fcb, UINT64 end) {
+ LIST_ENTRY changed_sector_list;
+ NTSTATUS Status;
+ BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
+
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
+
+ // FIXME - convert into inline extent if short enough
+
+ Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), nocsum ? NULL : &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents failed\n");
+ return Status;
+ }
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ // FIXME - inform cache manager of this
+
+ TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
+
+ if (!nocsum)
+ update_checksum_tree(fcb->Vcb, &changed_sector_list);
+
+ return STATUS_SUCCESS;
+}
+
+static UINT64 get_extent_item_refcount(device_extension* Vcb, UINT64 address) {
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT64 rc;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find EXTENT_ITEM for %llx\n", address);
+ return 0;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ rc = ei->refcount;
+
+ free_traverse_ptr(&tp);
+
+ return rc;
+}
+
+static NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ EXTENT_DATA* ed;
+ BOOL b, do_cow;
+ EXTENT_DATA2* eds;
+ UINT64 size, new_start, new_end, last_write = 0;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find last EXTENT_DATA item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset > start_data) {
+ ERR("previous EXTENT_DATA not found (found %llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do {
+ ed = (EXTENT_DATA*)tp.item->data;
+ eds = (EXTENT_DATA2*)&ed->data[0];
+
+ b = find_next_item(Vcb, &tp, &next_tp, TRUE);
+
+ switch (ed->type) {
+ case EXTENT_TYPE_REGULAR:
+ {
+ UINT64 rc = get_extent_item_refcount(Vcb, eds->address);
+
+ if (rc == 0) {
+ ERR("get_extent_item_refcount failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ do_cow = rc > 1;
+ break;
+ }
+
+ case EXTENT_TYPE_INLINE:
+ do_cow = TRUE;
+ break;
+
+ case EXTENT_TYPE_PREALLOC:
+ FIXME("FIXME - handle prealloc extents\n"); // FIXME
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+
+ default:
+ ERR("error - unknown extent type %x\n", ed->type);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME: compression not yet supported\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("encryption not supported\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : eds->num_bytes;
+
+ TRACE("extent: start = %llx, length = %llx\n", tp.item->key.offset, size);
+
+ new_start = tp.item->key.offset < start_data ? start_data : tp.item->key.offset;
+ new_end = tp.item->key.offset + size > start_data + length ? (start_data + length) : (tp.item->key.offset + size);
+
+ TRACE("new_start = %llx\n", new_start);
+ TRACE("new_end = %llx\n", new_end);
+
+ if (do_cow) {
+ TRACE("doing COW write\n");
+
+ Status = excise_extents(Vcb, fcb, new_start, new_start + new_end, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ goto end;
+ }
+ } else {
+ UINT64 writeaddr;
+
+ writeaddr = eds->address + eds->offset + new_start - tp.item->key.offset;
+ TRACE("doing non-COW write to %llx\n", writeaddr);
+
+ Status = write_data(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - write_data returned %08x\n", Status);
+ goto end;
+ }
+
+ if (changed_sector_list) {
+ unsigned int i;
+ changed_sector* sc;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ sc->ol.key = writeaddr;
+ sc->length = (new_end - new_start) / Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + new_start - start_data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+ }
+
+ last_write = new_end;
+
+ if (b) {
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data + length)
+ b = FALSE;
+ }
+ } while (b);
+
+ if (last_write < start_data + length) {
+ new_start = last_write;
+ new_end = start_data + length;
+
+ TRACE("new_start = %llx\n", new_start);
+ TRACE("new_end = %llx\n", new_end);
+
+ Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ free_traverse_ptr(&tp);
+
+ return Status;
+}
+
+#ifdef DEBUG_PARANOID
+static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ UINT64 length, oldlength, lastoff;
+
+ if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
+ return;
+
+ TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0;
+
+ if (!find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvol\n");
+ goto failure2;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find EXTENT_DATA at offset 0\n");
+ goto failure;
+ }
+
+ length = oldlength = ((EXTENT_DATA*)tp.item->data)->decoded_size;
+ lastoff = tp.item->key.offset;
+
+ TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+
+ while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type) {
+ free_traverse_ptr(&next_tp);
+ break;
+ }
+
+ free_traverse_ptr(&tp);
+ tp = next_tp;
+
+ length = ((EXTENT_DATA*)tp.item->data)->decoded_size;
+
+ TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+
+ if (tp.item->key.offset != lastoff + oldlength) {
+ ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
+ goto failure;
+ }
+
+ oldlength = length;
+ lastoff = tp.item->key.offset;
+ }
+
+// if (fcb->inode_item.st_blocks != lastoff + oldlength) {
+// ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
+// goto failure;
+// }
+
+ free_traverse_ptr(&tp);
+
+ return;
+
+failure:
+ free_traverse_ptr(&tp);
+
+failure2:
+ int3;
+}
+
+// static void check_extent_tree_consistent(device_extension* Vcb) {
+// KEY searchkey;
+// traverse_ptr tp, next_tp;
+// UINT64 lastaddr;
+// BOOL b, inconsistency;
+//
+// searchkey.obj_id = 0;
+// searchkey.obj_type = 0;
+// searchkey.offset = 0;
+//
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
+// }
+//
+// lastaddr = 0;
+// inconsistency = FALSE;
+//
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// // ERR("inconsistency!\n");
+// // int3;
+// inconsistency = TRUE;
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+// }
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+//
+// if (!inconsistency)
+// return;
+//
+// ERR("Inconsistency detected:\n");
+//
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
+// }
+//
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// ERR("inconsistency!\n");
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+// }
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
+//
+// free_traverse_ptr(&tp);
+//
+// int3;
+// }
+#endif
+
+NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ KEY searchkey;
+ traverse_ptr tp;
+ EXTENT_DATA* ed2;
+ UINT64 newlength, start_data, end_data;
+ UINT32 bufhead;
+ BOOL make_inline;
+ UINT8* data;
+ LIST_ENTRY changed_sector_list;
+ INODE_ITEM *ii, *origii;
+ BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE*/, write_eof = FALSE;
+ NTSTATUS Status;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ fcb* fcb;
+
+ TRACE("(%p, %p, %llx, %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");
+ return STATUS_SUCCESS;
+ }
+
+ if (!FileObject) {
+ ERR("error - FileObject was NULL\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ fcb = FileObject->FsContext;
+
+ if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
+ WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1) {
+ offset = fcb->Header.FileSize;
+ write_eof = TRUE;
+ }
+
+ TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
+
+ nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
+ nocow = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATACOW;
+
+ newlength = fcb->ads ? fcb->adssize : fcb->inode_item.st_size;
+
+ if (fcb->deleted)
+ newlength = 0;
+
+ TRACE("newlength = %llx\n", newlength);
+
+// if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
+// ERR("lazy writer on the TV\n");
+// lazy_writer = TRUE;
+// }
+
+ if (offset.QuadPart + *length > newlength) {
+ if (paging_io && !write_eof) {
+ if (offset.QuadPart >= newlength) {
+ WARN("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, offset.QuadPart, *length);
+ ERR("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ ERR("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
+ fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+ return STATUS_SUCCESS;
+ }
+
+ *length = newlength - offset.QuadPart;
+ } else {
+ newlength = offset.QuadPart + *length;
+ changed_length = TRUE;
+
+ TRACE("extending length to %llx\n", newlength);
+ }
+ }
+
+ if (fcb->ads) {
+ UINT16 datalen;
+ UINT8* data2;
+ UINT32 maxlen;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
+ ERR("get_xattr failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (changed_length) {
+ // find maximum length of xattr
+ maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = fcb->adshash;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find key for xattr\n");
+ free_traverse_ptr(&tp);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
+
+ free_traverse_ptr(&tp);
+
+ if (newlength > maxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen);
+ return STATUS_DISK_FULL;
+ }
+
+ fcb->adssize = newlength;
+
+ data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG);
+ RtlCopyMemory(data2, data, datalen);
+
+ if (offset.QuadPart > datalen)
+ RtlZeroMemory(&data2[datalen], offset.QuadPart - datalen);
+ } else
+ data2 = data;
+
+ if (*length > 0)
+ RtlCopyMemory(&data2[offset.QuadPart], buf, *length);
+
+ set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, newlength);
+
+ if (data) ExFreePool(data);
+ if (data2 != data) ExFreePool(data2);
+
+ if (newlength > fcb->Header.AllocationSize.QuadPart)
+ fcb->Header.AllocationSize.QuadPart = newlength;
+
+ fcb->Header.FileSize.QuadPart = newlength;
+ fcb->Header.ValidDataLength.QuadPart = newlength;
+ } else {
+ if (!no_cache && FileObject->PrivateCacheMap) {
+ BOOL wait;
+
+// ERR("FileObject->Flags: %x, changed_length = %u, write_eof = %u\n", FileObject->Flags, changed_length, write_eof);
+// if (changed_length) {
+// CC_FILE_SIZES ccfs;
+// UINT64 oldalloc;
+//
+// fcb->inode_item.st_size = newlength;
+//
+// ccfs.AllocationSize = fcb->Header.AllocationSize;
+// ccfs.FileSize = fcb->Header.FileSize;
+// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//
+// TRACE("new FileSize = %llx\n", fcb->Header.FileSize);
+//
+// CcSetFileSizes(FileObject, &ccfs);
+//
+// searchkey.obj_id = fcb->inode;
+// searchkey.obj_type = TYPE_EXTENT_DATA;
+// searchkey.offset = 0xffffffffffffffff;
+//
+// if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// oldalloc = 0;
+// if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+// oldalloc = tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size;
+// }
+// TRACE("oldalloc = %llx, newalloc = %llx\n", oldalloc, fcb->Header.AllocationSize.QuadPart);
+//
+// // FIXME - handle inline extents
+// if (fcb->Header.AllocationSize.QuadPart > oldalloc) {
+// Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, oldalloc, fcb->Header.AllocationSize.QuadPart - oldalloc);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("insert_sparse_extent returned %08x\n", Status);
+// free_traverse_ptr(&tp);
+// return Status;
+// }
+// }
+//
+// fcb->inode_item.st_size = newlength;
+//
+// free_traverse_ptr(&tp);
+// }
+//
+// if (write_eof || FileObject->Flags & FO_SYNCHRONOUS_IO) {
+// TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+// FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + *length;
+// TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+// }
+
+ // FIXME - uncomment this when async is working
+// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
+ wait = TRUE;
+
+ TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf);
+ if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
+ TRACE("CcCopyWrite failed.\n");
+
+ IoMarkIrpPending(Irp);
+ return STATUS_PENDING;
+ }
+ TRACE("CcCopyWrite finished\n");
+
+ return STATUS_SUCCESS;
+ } else {
+ make_inline = newlength <= fcb->Vcb->max_inline;
+
+ if (make_inline) {
+ start_data = 0;
+ end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
+ bufhead = sizeof(EXTENT_DATA) - 1;
+ } else {
+ start_data = offset.QuadPart & ~(fcb->Vcb->superblock.sector_size - 1);
+ end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size);
+ bufhead = 0;
+ }
+
+ if (end_data > fcb->Header.AllocationSize.QuadPart)
+ fcb->Header.AllocationSize.QuadPart = end_data;
+
+ fcb->Header.AllocationSize.QuadPart = sector_align(newlength, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = newlength;
+ fcb->Header.ValidDataLength.QuadPart = newlength;
+ TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
+
+ // FIXME - make sure freed if necessary
+ data = ExAllocatePoolWithTag(PagedPool, end_data - start_data + bufhead, ALLOC_TAG);
+ RtlZeroMemory(data + bufhead, end_data - start_data);
+
+ TRACE("start_data = %llx\n", start_data);
+ TRACE("end_data = %llx\n", end_data);
+
+ if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) {
+ if (changed_length) {
+ if (fcb->inode_item.st_size > start_data)
+ Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL);
+ else
+ Status = STATUS_SUCCESS;
+ } else
+ Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, end_data - start_data, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ }
+
+ RtlCopyMemory(data + bufhead + offset.QuadPart - start_data, buf, *length);
+
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
+
+ if (make_inline || !nocow) {
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, nocsum ? NULL : &changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+
+ if (!make_inline) {
+ Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ } else {
+ ed2 = (EXTENT_DATA*)data;
+ ed2->generation = fcb->Vcb->superblock.generation;
+ ed2->decoded_size = newlength;
+ ed2->compression = BTRFS_COMPRESSION_NONE;
+ ed2->encryption = BTRFS_ENCRYPTION_NONE;
+ ed2->encoding = BTRFS_ENCODING_NONE;
+ ed2->type = EXTENT_TYPE_INLINE;
+
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, NULL);
+
+ fcb->inode_item.st_blocks += newlength;
+ }
+ } else {
+ Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - do_nocow_write returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ }
+
+ if (!no_cache && !FileObject->PrivateCacheMap) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ TRACE("calling CcInitializeCacheMap...\n");
+ CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+
+ CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+
+ changed_length = FALSE;
+ }
+ }
+ }
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+// ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+
+// if (!no_cache) {
+// if (!FileObject->PrivateCacheMap) {
+// CC_FILE_SIZES ccfs;
+//
+// ccfs.AllocationSize = fcb->Header.AllocationSize;
+// ccfs.FileSize = fcb->Header.FileSize;
+// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//
+// TRACE("calling CcInitializeCacheMap...\n");
+// CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+//
+// changed_length = FALSE;
+// }
+// }
+
+ if (fcb->ads)
+ origii = &fcb->par->inode_item;
+ else
+ origii = &fcb->inode_item;
+
+ origii->transid = Vcb->superblock.generation;
+ origii->sequence++;
+ origii->st_ctime = now;
+
+ if (!fcb->ads) {
+ TRACE("setting st_size to %llx\n", newlength);
+ origii->st_size = newlength;
+ origii->st_mtime = now;
+ }
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ if (!find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE)) {
+ ERR("error - could not find any entries in subvolume %llx\n", fcb->subvol->id);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp);
+ else
+ WARN("couldn't find existing INODE_ITEM\n");
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ RtlCopyMemory(ii, origii, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL);
+
+ free_traverse_ptr(&tp);
+
+ // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
+
+ if (!nocsum)
+ update_checksum_tree(Vcb, &changed_sector_list);
+
+ if (changed_length) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+
+ CcSetFileSizes(FileObject, &ccfs);
+ }
+
+ if (write_eof || FileObject->Flags & FO_SYNCHRONOUS_IO) {
+ TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+ FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + *length;
+ TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
+ }
+
+ // FIXME - make sure this still called if STATUS_PENDING and async
+// if (!no_cache) {
+// if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
+// ERR("CcCopyWrite failed.\n");
+// }
+// }
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ void* buf;
+ NTSTATUS Status;
+ LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset;
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject ? FileObject->FsContext : NULL;
+ BOOL locked = FALSE;
+// LARGE_INTEGER freq, time1, time2;
+
+ if (Vcb->readonly)
+ return STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (fcb && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
+ return STATUS_ACCESS_DENIED;
+
+ if (!(Irp->Flags & IRP_NOCACHE) && !(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer->DataSectionObject) {
+ IO_STATUS_BLOCK iosb;
+
+ TRACE("CcFlushCache(%p, %llx, %x, %p)\n", FileObject->SectionObjectPointer, offset, IrpSp->Parameters.Write.Length, &iosb);
+ CcFlushCache(FileObject->SectionObjectPointer, &offset, IrpSp->Parameters.Write.Length, &iosb);
+ if (!NT_SUCCESS(iosb.Status)) {
+ ERR("CcFlushCache returned %08x\n", iosb.Status);
+ return iosb.Status;
+ }
+ TRACE("CcFlushCache finished\n");
+
+ TRACE("CcPurgeCacheSection(%p, %llx, %x, %u)\n", FileObject->SectionObjectPointer, offset, IrpSp->Parameters.Write.Length, FALSE);
+ CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, IrpSp->Parameters.Write.Length, FALSE);
+ TRACE("CcPurgeCacheSection finished\n");
+ }
+
+// time1 = KeQueryPerformanceCounter(&freq);
+
+ TRACE("write\n");
+
+ Irp->IoStatus.Information = 0;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_COMPLETE:
+ FIXME("unsupported - IRP_MN_COMPLETE\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
+ break;
+
+ case IRP_MN_COMPLETE_MDL_DPC:
+ FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
+ break;
+
+ case IRP_MN_COMPRESSED:
+ FIXME("unsupported - IRP_MN_COMPRESSED\n");
+ break;
+
+ case IRP_MN_DPC:
+ FIXME("unsupported - IRP_MN_DPC\n");
+ break;
+
+ case IRP_MN_MDL:
+ FIXME("unsupported - IRP_MN_MDL\n");
+ break;
+
+ case IRP_MN_MDL_DPC:
+ FIXME("unsupported - IRP_MN_MDL_DPC\n");
+ break;
+
+ case IRP_MN_NORMAL:
+ TRACE("IRP_MN_NORMAL\n");
+ break;
+
+ default:
+ WARN("unknown minor function %x\n", IrpSp->MinorFunction);
+ break;
+ }
+
+ TRACE("offset = %llx\n", offset.QuadPart);
+ TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
+
+ if (!Irp->AssociatedIrp.SystemBuffer)
+ buf = map_user_buffer(Irp);
+ else
+ buf = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("buf = %p\n", buf);
+
+ acquire_tree_lock(Vcb, TRUE);
+ locked = TRUE;
+
+ if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
+ WARN("tried to write to locked region\n");
+ Status = STATUS_FILE_LOCK_CONFLICT;
+ goto exit;
+ }
+
+// ERR("Irp->Flags = %x\n", Irp->Flags);
+ Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE);
+ if (!NT_SUCCESS(Status)) {
+ if (Status != STATUS_PENDING)
+ ERR("write_file2 returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = consider_write(Vcb);
+
+ if (NT_SUCCESS(Status)) {
+ Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
+
+#ifdef DEBUG_PARANOID
+ check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
+
+// check_extent_tree_consistent(Vcb);
+#endif
+ }
+
+exit:
+ if (locked)
+ release_tree_lock(Vcb, TRUE);
+
+// time2 = KeQueryPerformanceCounter(NULL);
+
+// ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
+
+ return Status;
+}
Index: reactos/drivers/filesystems/CMakeLists.txt
===================================================================
--- reactos/drivers/filesystems/CMakeLists.txt (revision 70774)
+++ reactos/drivers/filesystems/CMakeLists.txt (working copy)
@@ -1,4 +1,5 @@
+add_subdirectory(btrfs)
add_subdirectory(cdfs)
add_subdirectory(ext2)
add_subdirectory(fastfat)
Index: reactos/drivers/filesystems/fs_rec/btrfs.c
===================================================================
--- reactos/drivers/filesystems/fs_rec/btrfs.c (revision 0)
+++ reactos/drivers/filesystems/fs_rec/btrfs.c (working copy)
@@ -0,0 +1,111 @@
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS File System Recognizer
+ * FILE: drivers/filesystems/fs_rec/btrfs.c
+ * PURPOSE: BTRFS Recognizer
+ * PROGRAMMER: Eric Kohl
+ * Pierre Schweitzer (pierre@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "fs_rec.h"
+#include "btrfs.h"
+
+#define NDEBUG
+#include
+
+/* FUNCTIONS ****************************************************************/
+
+BOOLEAN
+NTAPI
+FsRecIsBtrfsVolume(IN PBTRFS_SUPER_BLOCK SuperBlock)
+{
+ /* Just check for magic */
+ return (SuperBlock->magic == BTRFS_MAGIC);
+}
+
+NTSTATUS
+NTAPI
+FsRecBtrfsFsControl(IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+{
+ PIO_STACK_LOCATION Stack;
+ NTSTATUS Status;
+ PDEVICE_OBJECT MountDevice;
+ PBTRFS_SUPER_BLOCK Spb = NULL;
+ ULONG SectorSize;
+ LARGE_INTEGER Offset;
+ BOOLEAN DeviceError = FALSE;
+ PAGED_CODE();
+
+ /* Get the I/O Stack and check the function type */
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+ switch (Stack->MinorFunction)
+ {
+ case IRP_MN_MOUNT_VOLUME:
+
+ /* Assume failure */
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+
+ /* Get the device object and request the sector size */
+ MountDevice = Stack->Parameters.MountVolume.DeviceObject;
+ if (FsRecGetDeviceSectorSize(MountDevice, &SectorSize))
+ {
+ /* Try to read the superblock */
+ Offset.QuadPart = BTRFS_SB_OFFSET;
+ if (FsRecReadBlock(MountDevice,
+ &Offset,
+ SectorSize,
+ SectorSize,
+ (PVOID)&Spb,
+ &DeviceError))
+ {
+ /* Check if it's an actual BTRFS volume */
+ if (FsRecIsBtrfsVolume(Spb))
+ {
+ /* It is! */
+ Status = STATUS_FS_DRIVER_REQUIRED;
+ }
+ }
+
+ /* Free the boot sector if we have one */
+ ExFreePool(Spb);
+ }
+ else
+ {
+ /* We have some sort of failure in the storage stack */
+ DeviceError = TRUE;
+ }
+
+ /* Check if we have an error on the stack */
+ if (DeviceError)
+ {
+ /* Was this because of a floppy? */
+ if (MountDevice->Characteristics & FILE_FLOPPY_DISKETTE)
+ {
+ /* Let the FS try anyway */
+ Status = STATUS_FS_DRIVER_REQUIRED;
+ }
+ }
+
+ break;
+
+ case IRP_MN_LOAD_FILE_SYSTEM:
+
+ /* Load the file system */
+ Status = FsRecLoadFileSystem(DeviceObject,
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\btrfs");
+ break;
+
+ default:
+
+ /* Invalid request */
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ /* Return Status */
+ return Status;
+}
+
+/* EOF */
Index: reactos/drivers/filesystems/fs_rec/btrfs.h
===================================================================
--- reactos/drivers/filesystems/fs_rec/btrfs.h (revision 0)
+++ reactos/drivers/filesystems/fs_rec/btrfs.h (working copy)
@@ -0,0 +1,30 @@
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS File System Recognizer
+ * FILE: drivers/filesystems/fs_rec/ext2.h
+ * PURPOSE: EXT2 Header File
+ * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
+ */
+
+#include
+
+typedef struct {
+ UINT8 uuid[16];
+} BTRFS_UUID;
+
+typedef struct _BTRFS_SUPER_BLOCK {
+ UINT8 checksum[32];
+ BTRFS_UUID uuid;
+ UINT64 sb_phys_addr;
+ UINT64 flags;
+ UINT64 magic;
+ // Partial
+} BTRFS_SUPER_BLOCK, *PBTRFS_SUPER_BLOCK;
+#include
+
+C_ASSERT(FIELD_OFFSET(BTRFS_SUPER_BLOCK, uuid) == 0x20);
+C_ASSERT(FIELD_OFFSET(BTRFS_SUPER_BLOCK, sb_phys_addr) == 0x30);
+C_ASSERT(FIELD_OFFSET(BTRFS_SUPER_BLOCK, magic) == 0x40);
+
+#define BTRFS_MAGIC 0x4d5f53665248425f
+#define BTRFS_SB_OFFSET 0x10000
Index: reactos/drivers/filesystems/fs_rec/CMakeLists.txt
===================================================================
--- reactos/drivers/filesystems/fs_rec/CMakeLists.txt (revision 70774)
+++ reactos/drivers/filesystems/fs_rec/CMakeLists.txt (working copy)
@@ -1,5 +1,6 @@
list(APPEND SOURCE
+ btrfs.c
blockdev.c
cdfs.c
ext2.c
Index: reactos/drivers/filesystems/fs_rec/fs_rec.c
===================================================================
--- reactos/drivers/filesystems/fs_rec/fs_rec.c (revision 70774)
+++ reactos/drivers/filesystems/fs_rec/fs_rec.c (working copy)
@@ -164,6 +164,12 @@
Status = FsRecExt2FsControl(DeviceObject, Irp);
break;
+ case FS_TYPE_BTRFS:
+
+ /* Send EXT2 command */
+ Status = FsRecBtrfsFsControl(DeviceObject, Irp);
+ break;
+
default:
/* Unrecognized FS */
@@ -382,6 +388,16 @@
FILE_DEVICE_DISK_FILE_SYSTEM);
if (NT_SUCCESS(Status)) DeviceCount++;
+ /* Register BTRFS */
+ Status = FsRecRegisterFs(DriverObject,
+ NULL,
+ NULL,
+ L"\\btrfs",
+ L"\\FileSystem\\BtrfsRecognizer",
+ FS_TYPE_BTRFS,
+ FILE_DEVICE_DISK_FILE_SYSTEM);
+ if (NT_SUCCESS(Status)) DeviceCount++;
+
/* Return appropriate Status */
return (DeviceCount > 0) ? STATUS_SUCCESS : STATUS_IMAGE_ALREADY_LOADED;
}
Index: reactos/drivers/filesystems/fs_rec/fs_rec.h
===================================================================
--- reactos/drivers/filesystems/fs_rec/fs_rec.h (revision 70774)
+++ reactos/drivers/filesystems/fs_rec/fs_rec.h (working copy)
@@ -174,6 +174,7 @@
FS_TYPE_CDFS,
FS_TYPE_UDFS,
FS_TYPE_EXT2,
+ FS_TYPE_BTRFS,
} FILE_SYSTEM_TYPE, *PFILE_SYSTEM_TYPE;
/* FS Recognizer State */
@@ -228,6 +229,13 @@
IN PIRP Irp
);
+NTSTATUS
+NTAPI
+FsRecBtrfsFsControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+);
+
BOOLEAN
NTAPI
FsRecGetDeviceSectors(