From 74750c6d1ae2ad2dbb43ff4d9fbc09546d0569b2 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sun, 4 Sep 2016 15:27:46 +0000 Subject: [PATCH] [BTRFS] Sync btrfs to 0.6. CORE-11937 svn path=/trunk/; revision=72576 --- .../drivers/filesystems/btrfs/CMakeLists.txt | 5 +- reactos/drivers/filesystems/btrfs/btrfs.c | 392 ++- reactos/drivers/filesystems/btrfs/btrfs.rc | 2 +- reactos/drivers/filesystems/btrfs/btrfs_drv.h | 197 +- .../drivers/filesystems/btrfs/btrfsioctl.h | 29 + reactos/drivers/filesystems/btrfs/compress.c | 898 ++++++ reactos/drivers/filesystems/btrfs/create.c | 875 +++-- reactos/drivers/filesystems/btrfs/dirctrl.c | 38 +- .../drivers/filesystems/btrfs/extent-tree.c | 94 +- reactos/drivers/filesystems/btrfs/fastio.c | 21 +- reactos/drivers/filesystems/btrfs/fileinfo.c | 425 ++- .../drivers/filesystems/btrfs/flushthread.c | 8 +- .../drivers/filesystems/btrfs/free-space.c | 189 +- reactos/drivers/filesystems/btrfs/fsctl.c | 681 ++-- reactos/drivers/filesystems/btrfs/pnp.c | 30 +- reactos/drivers/filesystems/btrfs/read.c | 271 +- reactos/drivers/filesystems/btrfs/registry.c | 175 +- reactos/drivers/filesystems/btrfs/reparse.c | 49 +- reactos/drivers/filesystems/btrfs/security.c | 16 +- reactos/drivers/filesystems/btrfs/treefuncs.c | 171 +- .../drivers/filesystems/btrfs/worker-thread.c | 19 + reactos/drivers/filesystems/btrfs/write.c | 2845 ++++++++--------- .../filesystems/btrfs/zlib/CMakeLists.txt | 17 + reactos/media/doc/README.FSD | 2 +- 24 files changed, 4693 insertions(+), 2756 deletions(-) create mode 100755 reactos/drivers/filesystems/btrfs/compress.c create mode 100644 reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt diff --git a/reactos/drivers/filesystems/btrfs/CMakeLists.txt b/reactos/drivers/filesystems/btrfs/CMakeLists.txt index 1156757b4f4..7e98893f5c1 100644 --- a/reactos/drivers/filesystems/btrfs/CMakeLists.txt +++ b/reactos/drivers/filesystems/btrfs/CMakeLists.txt @@ -1,10 +1,13 @@ +add_subdirectory(zlib) include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers + ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib inc) list(APPEND SOURCE btrfs.c cache.c + compress.c crc32c.c create.c dirctrl.c @@ -29,7 +32,7 @@ add_library(btrfs SHARED ${SOURCE} btrfs.rc) add_definitions(-D__KERNEL__) set_module_type(btrfs kernelmodedriver) -target_link_libraries(btrfs ntoskrnl_vista ${PSEH_LIB}) +target_link_libraries(btrfs ntoskrnl_vista zlib_solo ${PSEH_LIB}) 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) diff --git a/reactos/drivers/filesystems/btrfs/btrfs.c b/reactos/drivers/filesystems/btrfs/btrfs.c index 190db9c149c..24263629856 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs.c +++ b/reactos/drivers/filesystems/btrfs/btrfs.c @@ -35,8 +35,9 @@ #endif #include -#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 INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \ + BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \ + BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES) #define COMPAT_RO_SUPPORTED 0 static WCHAR device_name[] = {'\\','B','t','r','f','s',0}; @@ -53,6 +54,12 @@ LIST_ENTRY volumes; LIST_ENTRY VcbList; ERESOURCE global_loading_lock; UINT32 debug_log_level = 0; +UINT32 mount_compress = 0; +UINT32 mount_compress_force = 0; +UINT32 mount_compress_type = 0; +UINT32 mount_zlib_level = 3; +UINT32 mount_flush_interval = 30; +UINT32 mount_max_inline = 2048; BOOL log_started = FALSE; UNICODE_STRING log_device, log_file, registry_path; @@ -313,7 +320,7 @@ static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) { ExFreePool(registry_path.Buffer); } -BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) { +BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp) { KEY searchkey; traverse_ptr tp, prev_tp; NTSTATUS Status; @@ -323,13 +330,13 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) { searchkey.obj_type = 0xff; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, r, &tp, &searchkey, FALSE); + Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return FALSE; } - while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) { + while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) { tp = prev_tp; TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); @@ -348,7 +355,7 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) { return TRUE; } -BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) { +BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp) { KEY searchkey; traverse_ptr tp; DIR_ITEM* xa; @@ -361,7 +368,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return FALSE; @@ -419,7 +426,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* return FALSE; } -NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) { +NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; UINT8* di2; @@ -429,7 +436,7 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 searchkey.obj_type = TYPE_DIR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -456,11 +463,11 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, Irp, rollback); ExFreePool(di); } else { - insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, Irp, rollback); } return STATUS_SUCCESS; @@ -704,8 +711,24 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj break; case FileFsDeviceInformation: - FIXME("STUB: FileFsDeviceInformation\n"); + { + FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer; + + TRACE("FileFsDeviceInformation\n"); + + ffdi->DeviceType = FILE_DEVICE_DISK; + ffdi->Characteristics = Vcb->devices[0].devobj->Characteristics; + + if (Vcb->readonly) + ffdi->Characteristics |= FILE_READ_ONLY_DEVICE; + else + ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE; + + BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION); + Status = STATUS_SUCCESS; + break; + } case FileFsDriverPathInformation: FIXME("STUB: FileFsDriverPathInformation\n"); @@ -919,7 +942,7 @@ static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, P // } // } -NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) { +NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) { root* r; tree* t; ROOT_ITEM* ri; @@ -975,7 +998,7 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t // We ask here for a traverse_ptr to the item we're inserting, so we can // copy some of the tree's variables - if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(ri); @@ -1159,30 +1182,46 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) { ULONG utf8len; NTSTATUS Status; + USHORT vollen, i; TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel); - Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength); - if (!NT_SUCCESS(Status)) - goto end; + vollen = ffli->VolumeLabelLength; - if (utf8len > MAX_LABEL_SIZE) { - Status = STATUS_INVALID_VOLUME_LABEL; - goto end; + for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) { + if (ffli->VolumeLabel[i] == 0) { + vollen = i * sizeof(WCHAR); + break; + } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') { + Status = STATUS_INVALID_VOLUME_LABEL; + goto end; + } } - // FIXME - check for '/' and '\\' and reject - -// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG); - - Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength); - if (!NT_SUCCESS(Status)) - goto release; + if (vollen == 0) { + utf8len = 0; + } else { + Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen); + if (!NT_SUCCESS(Status)) + goto end; + + if (utf8len > MAX_LABEL_SIZE) { + Status = STATUS_INVALID_VOLUME_LABEL; + goto end; + } + } ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR)) - RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len); + if (utf8len > 0) { + Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen); + if (!NT_SUCCESS(Status)) + goto release; + } else + Status = STATUS_SUCCESS; + + if (utf8len < MAX_LABEL_SIZE) + RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len); // test_tree_deletion(Vcb); // TESTING // test_tree_splitting(Vcb); @@ -1266,7 +1305,7 @@ exit: return Status; } -NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) { +NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -1275,7 +1314,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U searchkey.obj_type = TYPE_DIR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1319,7 +1358,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size) RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data)); - insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback); + insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, Irp, rollback); } break; @@ -1336,7 +1375,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U return STATUS_SUCCESS; } -NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) { +NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; BOOL changed = FALSE; @@ -1346,7 +1385,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN searchkey.obj_type = TYPE_INODE_REF; searchkey.offset = parinode; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1400,7 +1439,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size) RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data)); - insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback); + insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, Irp, rollback); } break; @@ -1431,7 +1470,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN searchkey.obj_type = TYPE_INODE_EXTREF; searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length); - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1485,7 +1524,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size) RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data)); - insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback); + insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, Irp, rollback); } break; @@ -1612,7 +1651,7 @@ void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); file_ref* parfr; - Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr); + Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08x\n", Status); @@ -1732,6 +1771,13 @@ void mark_fileref_dirty(file_ref* fileref) { void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) { LONG rc; +// #ifdef DEBUG +// if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) { +// ERR("fcb_lock not acquired exclusively\n"); +// int3; +// } +// #endif + rc = InterlockedDecrement(&fcb->refcount); #ifdef DEBUG_FCB_REFCOUNTS @@ -1746,7 +1792,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) if (rc > 0) return; - ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE); +// ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE); if (fcb->list_entry.Flink) RemoveEntryList(&fcb->list_entry); @@ -1754,7 +1800,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) if (fcb->list_entry_all.Flink) RemoveEntryList(&fcb->list_entry_all); - ExReleaseResourceLite(&fcb->Vcb->fcb_lock); +// ExReleaseResourceLite(&fcb->Vcb->fcb_lock); ExDeleteResourceLite(&fcb->nonpaged->resource); ExDeleteResourceLite(&fcb->nonpaged->paging_resource); @@ -1821,6 +1867,13 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) { LONG rc; +// #ifdef DEBUG +// if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) { +// ERR("fcb_lock not acquired exclusively\n"); +// int3; +// } +// #endif + rc = InterlockedDecrement(&fr->refcount); #ifdef DEBUG_FCB_REFCOUNTS @@ -1915,6 +1968,9 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec CcUninitializeCacheMap(FileObject, NULL, NULL); + if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) + return STATUS_SUCCESS; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); if (fileref) @@ -1935,9 +1991,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { LIST_ENTRY* le; LARGE_INTEGER time; -#ifndef __REACTOS__ + Vcb->removing = TRUE; + RemoveEntryList(&Vcb->list_entry); -#endif Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid); if (!NT_SUCCESS(Status)) @@ -1948,8 +2004,8 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->need_write) - do_write(Vcb, &rollback); + if (Vcb->need_write && !Vcb->readonly) + do_write(Vcb, NULL, &rollback); free_trees(Vcb); @@ -1969,8 +2025,6 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { ExFreePool(Vcb->threads.threads); - Vcb->removing = TRUE; - time.QuadPart = 0; KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL); @@ -2027,10 +2081,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { if (c->cache) free_fcb(c->cache); - ExDeleteResourceLite(&c->nonpaged->lock); - ExDeleteResourceLite(&c->nonpaged->changed_extents_lock); + ExDeleteResourceLite(&c->lock); + ExDeleteResourceLite(&c->changed_extents_lock); - ExFreePool(c->nonpaged); ExFreePool(c->chunk_item); ExFreePool(c); } @@ -2064,7 +2117,7 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { ZwClose(Vcb->flush_thread_handle); } -NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) { +NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback) { LARGE_INTEGER newlength, time; BTRFS_TIME now; NTSTATUS Status; @@ -2103,7 +2156,7 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* // excise extents if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) { - Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback); + Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("excise_extents returned %08x\n", Status); ExReleaseResourceLite(fileref->fcb->Header.Resource); @@ -2265,7 +2318,7 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); - Status = delete_fileref(fileref, FileObject, &rollback); + Status = delete_fileref(fileref, FileObject, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); do_rollback(Vcb, &rollback); @@ -2319,14 +2372,14 @@ exit2: 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 STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp) { ULONG att; char* eaval; UINT16 ealen; // ii can be NULL - if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) { + if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) { if (ealen > 2) { if (eaval[0] == '0' && eaval[1] == 'x') { int i; @@ -2388,7 +2441,7 @@ ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r return att; } -NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) { +static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) { IO_STATUS_BLOCK* IoStatus; LARGE_INTEGER Offset; PIRP Irp; @@ -2425,9 +2478,13 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL goto exit; } + Irp->Flags |= IRP_NOCACHE; IrpSp = IoGetNextIrpStackLocation(Irp); IrpSp->MajorFunction = IRP_MJ_READ; + if (override) + IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME; + if (DeviceObject->Flags & DO_BUFFERED_IO) { FIXME("FIXME - buffered IO\n"); } else if (DeviceObject->Flags & DO_DIRECT_IO) { @@ -2461,20 +2518,10 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL 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, KernelMode, FALSE, NULL); -// TRACE("Getting IO Status... for %p\n", Irp); Status = context->iosb.Status; } @@ -2512,7 +2559,7 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length) break; - Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb); + Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE); if (!NT_SUCCESS(Status)) { ERR("Failed to read superblock %u: %08x\n", i, Status); ExFreePool(sb); @@ -2646,7 +2693,7 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, return STATUS_SUCCESS; } -static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) { +static NTSTATUS STDCALL look_for_roots(device_extension* Vcb, PIRP Irp) { traverse_ptr tp, next_tp; KEY searchkey; BOOL b; @@ -2656,7 +2703,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) { searchkey.obj_type = 0; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_tree returned %08x\n", Status); return Status; @@ -2681,7 +2728,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) { } } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) tp = next_tp; @@ -2690,7 +2737,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) { return STATUS_SUCCESS; } -static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { +static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; BOOL b; @@ -2703,7 +2750,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { searchkey.obj_type = TYPE_DEV_EXTENT; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_tree returned %08x\n", Status); return Status; @@ -2730,7 +2777,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { } } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -2858,7 +2905,7 @@ static void init_device(device_extension* Vcb, device* dev, BOOL get_length) { } } -static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { +static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) { traverse_ptr tp, next_tp; KEY searchkey; BOOL b; @@ -2872,7 +2919,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { Vcb->data_flags = 0; - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -2950,33 +2997,24 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { if (tp.item->size < sizeof(CHUNK_ITEM)) { ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM)); } else { - c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG); + c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG); if (!c) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } - c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG); - - if (!c->nonpaged) { - ERR("out of memory\n"); - ExFreePool(c); - return STATUS_INSUFFICIENT_RESOURCES; - } - c->size = tp.item->size; c->offset = tp.item->key.offset; c->used = c->oldused = 0; c->cache = NULL; c->created = FALSE; - c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); + c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG); if (!c->chunk_item) { ERR("out of memory\n"); ExFreePool(c); - ExFreePool(c->nonpaged); return STATUS_INSUFFICIENT_RESOURCES; } @@ -2988,12 +3026,11 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { 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); + c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG); if (!c->devices) { ERR("out of memory\n"); ExFreePool(c); - ExFreePool(c->nonpaged); ExFreePool(c->chunk_item); return STATUS_INSUFFICIENT_RESOURCES; } @@ -3005,8 +3042,8 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { } else c->devices = NULL; - ExInitializeResourceLite(&c->nonpaged->lock); - ExInitializeResourceLite(&c->nonpaged->changed_extents_lock); + ExInitializeResourceLite(&c->lock); + ExInitializeResourceLite(&c->changed_extents_lock); InitializeListHead(&c->space); InitializeListHead(&c->space_size); @@ -3019,7 +3056,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { } } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) tp = next_tp; @@ -3094,7 +3131,7 @@ void protect_superblocks(device_extension* Vcb, chunk* c) { } } -static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) { +static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb, PIRP Irp) { LIST_ENTRY* le = Vcb->chunks.Flink; chunk* c; KEY searchkey; @@ -3113,7 +3150,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) { searchkey.obj_id = c->offset; searchkey.offset = c->chunk_item->size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3141,7 +3178,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) { // FIXME - make sure we free occasionally after doing one of these, or we // might use up a lot of memory with a big disk. - Status = load_free_space_cache(Vcb, c); + Status = load_free_space_cache(Vcb, c, Irp); if (!NT_SUCCESS(Status)) { ERR("load_free_space_cache returned %08x\n", Status); return Status; @@ -3253,12 +3290,24 @@ static NTSTATUS load_sys_chunks(device_extension* Vcb) { return STATUS_SUCCESS; } -static root* find_default_subvol(device_extension* Vcb) { +static root* find_default_subvol(device_extension* Vcb, PIRP Irp) { LIST_ENTRY* le; static char fn[] = "default"; static UINT32 crc32 = 0x8dbfc2d2; + if (Vcb->options.subvol_id != 0) { + le = Vcb->roots.Flink; + while (le != &Vcb->roots) { + root* r = CONTAINING_RECORD(le, root, list_entry); + + if (r->id == Vcb->options.subvol_id) + return r; + + le = le->Flink; + } + } + if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) { NTSTATUS Status; KEY searchkey; @@ -3269,7 +3318,7 @@ static root* find_default_subvol(device_extension* Vcb) { searchkey.obj_type = TYPE_DIR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -3334,7 +3383,7 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) { ULONG i; NTSTATUS Status; - Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors? + Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG); if (!Vcb->threads.threads) { @@ -3366,6 +3415,8 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) { } } + Vcb->threads.pending_jobs = 0; + return STATUS_SUCCESS; } @@ -3375,6 +3426,9 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) { threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads; + if (Vcb->threads.pending_jobs >= Vcb->threads.num_threads) + return FALSE; + if (Vcb->threads.threads[threadnum].quit) return FALSE; @@ -3388,6 +3442,8 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) { tj->Irp = Irp; + InterlockedIncrement(&Vcb->threads.pending_jobs); + ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock); KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE); @@ -3527,7 +3583,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { TRACE("btrfs magic found\n"); } - Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options); + Status = registry_load_volume_options(Vcb); if (!NT_SUCCESS(Status)) { ERR("registry_load_volume_options returned %08x\n", Status); goto exit; @@ -3564,10 +3620,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->readonly = TRUE; } + if (Vcb->options.readonly) + Vcb->readonly = TRUE; + Vcb->superblock.generation++; Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF; - Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG); + Vcb->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG); if (!Vcb->devices) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -3595,8 +3654,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->log_to_phys_loaded = FALSE; - Vcb->max_inline = Vcb->superblock.node_size / 2; - add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL); if (!Vcb->chunk_root) { @@ -3629,7 +3686,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { FsRtlNotifyInitializeSync(&Vcb->NotifySync); - Status = load_chunk_root(Vcb); + Status = load_chunk_root(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("load_chunk_root returned %08x\n", Status); goto exit; @@ -3663,29 +3720,31 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto exit; } - Status = look_for_roots(Vcb); + Status = look_for_roots(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("look_for_roots returned %08x\n", Status); goto exit; } - Status = find_chunk_usage(Vcb); - if (!NT_SUCCESS(Status)) { - ERR("find_chunk_usage returned %08x\n", Status); - goto exit; + if (!Vcb->readonly) { + Status = find_chunk_usage(Vcb, Irp); + if (!NT_SUCCESS(Status)) { + ERR("find_chunk_usage returned %08x\n", Status); + goto exit; + } } // We've already increased the generation by one if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) { WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation); - Status = clear_free_space_cache(Vcb); + Status = clear_free_space_cache(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("clear_free_space_cache returned %08x\n", Status); goto exit; } } - Vcb->volume_fcb = create_fcb(); + Vcb->volume_fcb = create_fcb(NonPagedPool); if (!Vcb->volume_fcb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -3695,7 +3754,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->volume_fcb->Vcb = Vcb; Vcb->volume_fcb->sd = NULL; - root_fcb = create_fcb(); + root_fcb = create_fcb(NonPagedPool); if (!root_fcb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -3711,7 +3770,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { WARN("root FCB = %p\n", root_fcb); #endif - root_fcb->subvol = find_default_subvol(Vcb); + root_fcb->subvol = find_default_subvol(Vcb, Irp); if (!root_fcb->subvol) { ERR("could not find top subvol\n"); @@ -3723,7 +3782,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto exit; @@ -3738,9 +3797,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (tp.item->size > 0) RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size)); - fcb_get_sd(root_fcb, NULL); + fcb_get_sd(root_fcb, NULL, Irp); - root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE); + root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp); Vcb->root_fileref = create_fileref(); if (!Vcb->root_fileref) { @@ -3764,18 +3823,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount); Vcb->root_file->FsContext = root_fcb; -#ifdef __REACTOS__ Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object; Vcb->root_file->Vpb = DeviceObject->Vpb; -#endif RtlZeroMemory(root_ccb, sizeof(ccb)); root_ccb->NodeType = BTRFS_NODE_TYPE_CCB; root_ccb->NodeSize = sizeof(ccb); -#ifndef __REACTOS__ - Vcb->root_file->FsContext = root_ccb; -#else Vcb->root_file->FsContext2 = root_ccb; _SEH2_TRY { @@ -3784,10 +3838,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = _SEH2_GetExceptionCode(); goto exit; } _SEH2_END; -#endif for (i = 0; i < Vcb->superblock.num_devices; i++) { - Status = find_disk_holes(Vcb, &Vcb->devices[i]); + Status = find_disk_holes(Vcb, &Vcb->devices[i], Irp); if (!NT_SUCCESS(Status)) { ERR("find_disk_holes returned %08x\n", Status); goto exit; @@ -3867,6 +3920,92 @@ exit: return Status; } +static NTSTATUS verify_volume(PDEVICE_OBJECT device) { + device_extension* Vcb = device->DeviceExtension; + ULONG cc, to_read; + IO_STATUS_BLOCK iosb; + NTSTATUS Status; + superblock* sb; + UINT32 crc32; + UINT64 i; + + if (Vcb->removing) + return STATUS_WRONG_VOLUME; + + Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) { + ERR("dev_ioctl returned %08x\n", Status); + return Status; + } + + to_read = sector_align(sizeof(superblock), device->SectorSize); + + sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG); + if (!sb) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = sync_read_phys(Vcb->Vpb->RealDevice, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("Failed to read superblock: %08x\n", Status); + ExFreePool(sb); + return Status; + } + + if (sb->magic != BTRFS_MAGIC) { + ERR("not a BTRFS volume\n"); + ExFreePool(sb); + return STATUS_WRONG_VOLUME; + } + + if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) { + ERR("different UUIDs\n"); + ExFreePool(sb); + return STATUS_WRONG_VOLUME; + } + + crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); + TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum)); + + if (crc32 != *((UINT32*)sb->checksum)) { + ERR("different UUIDs\n"); + ExFreePool(sb); + return STATUS_WRONG_VOLUME; + } + + ExFreePool(sb); + + for (i = 0; i < Vcb->superblock.num_devices; i++) { + if (Vcb->devices[i].removable) { + NTSTATUS Status; + ULONG cc; + IO_STATUS_BLOCK iosb; + + Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) { + ERR("dev_ioctl returned %08x\n", Status); + return Status; + } + + if (iosb.Information < sizeof(ULONG)) { + ERR("iosb.Information was too short\n"); + return STATUS_INTERNAL_ERROR; + } + + Vcb->devices[i].change_count = cc; + } + + Vcb->devices[i].devobj->Flags &= ~DO_VERIFY_VOLUME; + } + + Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME; + + return STATUS_SUCCESS; +} + static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IrpSp; NTSTATUS Status; @@ -3910,7 +4049,15 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, break; case IRP_MN_VERIFY_VOLUME: - FIXME("STUB: IRP_MN_VERIFY_VOLUME\n"); + TRACE("IRP_MN_VERIFY_VOLUME\n"); + + Status = verify_volume(DeviceObject); + + if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) { + uninit(Vcb, FALSE); +// Vcb->Vpb->Flags &= ~VPB_MOUNTED; + } + break; default: @@ -4136,8 +4283,7 @@ static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp Status = STATUS_SUCCESS; while (!IsListEmpty(&VcbList)) { - LIST_ENTRY* le = RemoveHeadList(&VcbList); - Vcb = CONTAINING_RECORD(le, device_extension, list_entry); + Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry); TRACE("shutting down Vcb %p\n", Vcb); diff --git a/reactos/drivers/filesystems/btrfs/btrfs.rc b/reactos/drivers/filesystems/btrfs/btrfs.rc index 3246f5dc389..4d7d89e8890 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs.rc +++ b/reactos/drivers/filesystems/btrfs/btrfs.rc @@ -75,7 +75,7 @@ BEGIN VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016" VALUE "OriginalFilename", "btrfs.sys" VALUE "ProductName", "WinBtrfs" - VALUE "ProductVersion", "0.5" + VALUE "ProductVersion", "0.6" END END BLOCK "VarFileInfo" diff --git a/reactos/drivers/filesystems/btrfs/btrfs_drv.h b/reactos/drivers/filesystems/btrfs/btrfs_drv.h index d0b62fadd5f..1b01d9204bd 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs_drv.h +++ b/reactos/drivers/filesystems/btrfs/btrfs_drv.h @@ -56,6 +56,7 @@ #define BTRFS_NODE_TYPE_FCB 0x2296 #define ALLOC_TAG 0x7442484D //'MHBt' +#define ALLOC_TAG_ZLIB 0x7A42484D //'MHBz' #define STDCALL __stdcall @@ -71,9 +72,10 @@ #define EA_REPARSE "system.reparse" #define EA_REPARSE_HASH 0x786f6167 -#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB - #define MAX_EXTENT_SIZE 0x8000000 // 128 MB +#define COMPRESSED_EXTENT_SIZE 0x20000 // 128 KB + +#define READ_AHEAD_GRANULARITY COMPRESSED_EXTENT_SIZE // really ought to be a multiple of COMPRESSED_EXTENT_SIZE #ifdef _MSC_VER #define try __try @@ -87,7 +89,7 @@ // #pragma pack(push, 1) -struct device_extension; +struct _device_extension; typedef struct { BTRFS_UUID fsuuid; @@ -176,6 +178,7 @@ typedef struct _fcb { BOOL ads; UINT32 adshash; + ULONG adsmaxlen; ANSI_STRING adsxattr; ANSI_STRING adsdata; @@ -344,11 +347,6 @@ typedef struct { LIST_ENTRY space; } device; -typedef struct { - ERESOURCE lock; - ERESOURCE changed_extents_lock; -} chunk_nonpaged; - typedef struct { CHUNK_ITEM* chunk_item; UINT32 size; @@ -361,7 +359,8 @@ typedef struct { LIST_ENTRY space_size; LIST_ENTRY deleting; LIST_ENTRY changed_extents; - chunk_nonpaged* nonpaged; + ERESOURCE lock; + ERESOURCE changed_extents_lock; BOOL created; LIST_ENTRY list_entry; @@ -424,10 +423,19 @@ typedef struct { ULONG num_threads; LONG next_thread; drv_thread* threads; + LONG pending_jobs; } drv_threads; typedef struct { BOOL ignore; + BOOL compress; + BOOL compress_force; + UINT8 compress_type; + BOOL readonly; + UINT32 zlib_level; + UINT32 flush_interval; + UINT32 max_inline; + UINT64 subvol_id; } mount_options; #define VCB_TYPE_VOLUME 1 @@ -471,7 +479,6 @@ typedef struct _device_extension { root* dev_root; root* uuid_root; BOOL log_to_phys_loaded; - UINT32 max_inline; LIST_ENTRY sys_chunks; LIST_ENTRY chunks; LIST_ENTRY chunks_changed; @@ -528,10 +535,10 @@ enum write_data_status { WriteDataStatus_Ignore }; -struct write_data_context; +struct _write_data_context; typedef struct { - struct write_data_context* context; + struct _write_data_context* context; UINT8* buf; BOOL need_free; device* device; @@ -541,7 +548,7 @@ typedef struct { LIST_ENTRY list_entry; } write_data_stripe; -typedef struct { +typedef struct _write_data_context { KEVENT Event; LIST_ENTRY stripes; LONG stripes_left; @@ -596,7 +603,7 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n startoff = off % (num_stripes * stripe_length); initoff = (off / (num_stripes * stripe_length)) * stripe_length; - *stripe = startoff / stripe_length; + *stripe = (UINT16)(startoff / stripe_length); *stripeoff = initoff + startoff - (*stripe * stripe_length); } @@ -604,19 +611,19 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid); UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment ); int keycmp(const KEY* key1, const KEY* key2); -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); +ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp); +BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp); void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line); void _free_fileref(file_ref* fr, 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, LIST_ENTRY* rollback); -NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback); -NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback); -fcb* create_fcb(); +BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp); +NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback); +fcb* create_fcb(POOL_TYPE pool_type); file_ref* create_fileref(); void protect_superblocks(device_extension* Vcb, chunk* c); BOOL is_top_level(PIRP Irp); -NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback); +NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback); void STDCALL uninit(device_extension* Vcb, BOOL flush); NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb); @@ -629,7 +636,7 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp); NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); void mark_fcb_dirty(fcb* fcb); void mark_fileref_dirty(file_ref* fileref); -NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback); +NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback); #ifdef _MSC_VER #define funcname __FUNCTION__ @@ -641,6 +648,13 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* #define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__) #define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__) +extern UINT32 mount_compress; +extern UINT32 mount_compress_force; +extern UINT32 mount_compress_type; +extern UINT32 mount_zlib_level; +extern UINT32 mount_flush_interval; +extern UINT32 mount_max_inline; + #ifdef _DEBUG extern BOOL log_started; @@ -696,34 +710,50 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod); // in crc32c.c UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen); +typedef struct { + LIST_ENTRY* list; + LIST_ENTRY* list_size; + UINT64 address; + UINT64 length; + chunk* chunk; +} rollback_space; + +typedef struct { + fcb* fcb; + extent* ext; +} rollback_extent; + enum rollback_type { ROLLBACK_INSERT_ITEM, ROLLBACK_DELETE_ITEM, ROLLBACK_INSERT_EXTENT, - ROLLBACK_DELETE_EXTENT + ROLLBACK_DELETE_EXTENT, + ROLLBACK_ADD_SPACE, + ROLLBACK_SUBTRACT_SPACE }; // in treefuncs.c -NTSTATUS 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); +NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, 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, PIRP Irp, 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, PIRP Irp, const char* func, const char* file, unsigned int line); void STDCALL free_trees(device_extension* Vcb); -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, LIST_ENTRY* rollback); +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, PIRP Irp, LIST_ENTRY* rollback); void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback); tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line); -NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line); -NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line); +NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line); +NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp, + const char* func, const char* file, unsigned int line); void clear_rollback(LIST_ENTRY* rollback); void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback); void free_trees_root(device_extension* Vcb, root* r); void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr); -#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 find_item(Vcb, r, tp, searchkey, ignore, Irp) _find_item(Vcb, r, tp, searchkey, ignore, Irp, funcname, __FILE__, __LINE__) +#define find_next_item(Vcb, tp, next_tp, ignore, Irp) _find_next_item(Vcb, tp, next_tp, ignore, Irp, funcname, __FILE__, __LINE__) +#define find_prev_item(Vcb, tp, prev_tp, ignore, Irp) _find_prev_item(Vcb, tp, prev_tp, ignore, Irp, funcname, __FILE__, __LINE__) #define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__) -#define load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__) -#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__) +#define load_tree(t, addr, r, pt, parent, Irp) _load_tree(t, addr, r, pt, parent, Irp, funcname, __FILE__, __LINE__) +#define do_load_tree(Vcb, th, r, t, td, loaded, Irp) _do_load_tree(Vcb, th, r, t, td, loaded, Irp, funcname, __FILE__, __LINE__) // in search.c void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes); @@ -734,51 +764,52 @@ void STDCALL free_cache(); extern CACHE_MANAGER_CALLBACKS* cache_callbacks; // in write.c -NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback); +NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback); NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write); NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback); -NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback); +NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback); NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback); -NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback); +NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback); void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list); -NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback); chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address); -chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback); -NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp); -NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp); +chunk* alloc_chunk(device_extension* Vcb, UINT64 flags); +NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c); +NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c); void free_write_data_stripes(write_data_context* wtc); -NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback); +NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback); NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); -void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback); +void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback); BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, - PIRP Irp, LIST_ENTRY* rollback); -NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback); + PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size); NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback); -void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback); NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, - signed long long count, BOOL no_csum, UINT64 new_size); + signed long long count, BOOL no_csum, UINT64 new_size, PIRP Irp); +NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback); // in dirctrl.c NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); +ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, 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, struct _fcb* parent); +void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp); // UINT32 STDCALL get_uid(); void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid); UINT32 sid_to_uid(PSID sid); +void uid_to_sid(UINT32 uid, PSID* sid); NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, 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, LIST_ENTRY* rollback); +NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback); BOOL has_open_children(file_ref* fileref); NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback); NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset); -NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr); +NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp); // in reparse.c NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen); @@ -787,15 +818,14 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp); // in create.c NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); -NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol, - UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8); NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr, - root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8); -NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed); -NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb); -NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb); + root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp); +NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp); +NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp); +NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp); void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock); -NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index); +NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp); +NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp); // in fsctl.c NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user); @@ -814,29 +844,29 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read); NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp); // in free-space.c -NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c); -NTSTATUS clear_free_space_cache(device_extension* Vcb); -NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback); -NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback); +NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp); +NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp); +NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback); NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size); void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func); -void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func); +void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func); void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func); -void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func); +void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func); #define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname) -#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname) +#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, NULL, rollback, funcname) #define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname) -#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname) +#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, NULL, rollback, funcname) // in extent-tree.c -NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback); -NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback); -NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback); -NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback); +NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback); +NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback); void decrease_chunk_usage(chunk* c, UINT64 delta); -NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback); -UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset); +NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback); +UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp); // in worker-thread.c void STDCALL worker_thread(void* context); @@ -847,7 +877,11 @@ void do_write_job(device_extension* Vcb, PIRP Irp); void STDCALL read_registry(PUNICODE_STRING regpath); NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid); NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid); -NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options); +NTSTATUS registry_load_volume_options(device_extension* Vcb); + +// in compress.c +NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen); +NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback); #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable) @@ -874,6 +908,23 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* head->Blink = item; } +static __inline BOOL write_fcb_compressed(fcb* fcb) { + // make sure we don't accidentally write the cache inodes or pagefile compressed + if (fcb->subvol->id == BTRFS_ROOT_ROOT || fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE) + return FALSE; + + if (fcb->Vcb->options.compress_force) + return TRUE; + + if (fcb->inode_item.flags & BTRFS_INODE_NOCOMPRESS) + return FALSE; + + if (fcb->inode_item.flags & BTRFS_INODE_COMPRESS || fcb->Vcb->options.compress) + return TRUE; + + return FALSE; +} + #ifdef DEBUG_FCB_REFCOUNTS #ifdef DEBUG_LONG_MESSAGES #define increase_fileref_refcount(fileref) {\ diff --git a/reactos/drivers/filesystems/btrfs/btrfsioctl.h b/reactos/drivers/filesystems/btrfs/btrfsioctl.h index 3071431932a..bc35ad0ad14 100644 --- a/reactos/drivers/filesystems/btrfs/btrfsioctl.h +++ b/reactos/drivers/filesystems/btrfs/btrfsioctl.h @@ -1,9 +1,13 @@ +// No copyright claimed in this file - do what you want with it. + #ifndef BTRFSIOCTL_H_DEFINED #define BTRFSIOCTL_H_DEFINED #define FSCTL_BTRFS_GET_FILE_IDS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define FSCTL_BTRFS_CREATE_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define FSCTL_BTRFS_CREATE_SNAPSHOT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82b, METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define FSCTL_BTRFS_GET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82c, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) +#define FSCTL_BTRFS_SET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82d, METHOD_IN_DIRECT, FILE_ANY_ACCESS) typedef struct { UINT64 subvol; @@ -17,4 +21,29 @@ typedef struct { WCHAR name[1]; } btrfs_create_snapshot; +typedef struct { + UINT64 subvol; + UINT64 inode; + BOOL top; + UINT8 type; + UINT32 st_uid; + UINT32 st_gid; + UINT32 st_mode; + UINT64 st_rdev; + UINT64 flags; + UINT32 inline_length; + UINT64 disk_size[3]; +} btrfs_inode_info; + +typedef struct { + UINT64 flags; + BOOL flags_changed; + UINT32 st_uid; + BOOL uid_changed; + UINT32 st_gid; + BOOL gid_changed; + UINT32 st_mode; + BOOL mode_changed; +} btrfs_set_inode_info; + #endif diff --git a/reactos/drivers/filesystems/btrfs/compress.c b/reactos/drivers/filesystems/btrfs/compress.c new file mode 100755 index 00000000000..7b7a00a241a --- /dev/null +++ b/reactos/drivers/filesystems/btrfs/compress.c @@ -0,0 +1,898 @@ +/* Copyright (c) Mark Harmstone 2016 + * Copyright (c) Reimar Doeffinger 2006 + * Copyright (c) Markus Oberhumer 1996 + * + * 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 . */ + +// Portion of the LZO decompression code here were cribbed from code in +// libavcodec, also under the LGPL. Thank you, Reimar Doeffinger. + +// The LZO compression code comes from v0.22 of lzo, written way back in +// 1996, and available here: +// https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/libs/lzo-0.22.tar.gz +// Modern versions of lzo are licensed under the GPL, but the very oldest +// versions are under the LGPL and hence okay to use here. + +#include "btrfs_drv.h" + +#define Z_SOLO +#define ZLIB_INTERNAL + +#ifndef __REACTOS__ +#include "zlib/zlib.h" +#include "zlib/inftrees.h" +#include "zlib/inflate.h" +#else +#include +#endif + +#define LINUX_PAGE_SIZE 4096 + +typedef struct { + UINT8* in; + UINT32 inlen; + UINT32 inpos; + UINT8* out; + UINT32 outlen; + UINT32 outpos; + BOOL error; + void* wrkmem; +} lzo_stream; + +#define LZO1X_MEM_COMPRESS ((UINT32) (16384L * sizeof(UINT8*))) + +#define M1_MAX_OFFSET 0x0400 +#define M2_MAX_OFFSET 0x0800 +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#define _DV2(p, shift1, shift2) (((( (UINT32)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0]) +#define DVAL_NEXT(dv, p) dv ^= p[-1]; dv = (((dv) >> 5) ^ ((UINT32)(p[2]) << (2*5))) +#define _DV(p, shift) _DV2(p, shift, shift) +#define DVAL_FIRST(dv, p) dv = _DV((p), 5) +#define _DINDEX(dv, p) ((40799u * (dv)) >> 5) +#define DINDEX(dv, p) (((_DINDEX(dv, p)) & 0x3fff) << 0) +#define UPDATE_D(dict, cycle, dv, p) dict[DINDEX(dv, p)] = (p) +#define UPDATE_I(dict, cycle, index, p) dict[index] = (p) + +#define LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, max_offset) \ + ((void*) m_pos < (void*) in || \ + (m_off = (UINT8*) ip - (UINT8*) m_pos) <= 0 || \ + m_off > max_offset) + +#define LZO_BYTE(x) ((unsigned char) (x)) + +static UINT8 lzo_nextbyte(lzo_stream* stream) { + UINT8 c; + + if (stream->inpos >= stream->inlen) { + stream->error = TRUE; + return 0; + } + + c = stream->in[stream->inpos]; + stream->inpos++; + + return c; +} + +static int lzo_len(lzo_stream* stream, int byte, int mask) { + int len = byte & mask; + + if (len == 0) { + while (!(byte = lzo_nextbyte(stream))) { + if (stream->error) return 0; + + len += 255; + } + + len += mask + byte; + } + + return len; +} + +static void lzo_copy(lzo_stream* stream, int len) { + if (stream->inpos + len > stream->inlen) { + stream->error = TRUE; + return; + } + + if (stream->outpos + len > stream->outlen) { + stream->error = TRUE; + return; + } + + do { + stream->out[stream->outpos] = stream->in[stream->inpos]; + stream->inpos++; + stream->outpos++; + len--; + } while (len > 0); +} + +static void lzo_copyback(lzo_stream* stream, int back, int len) { + if (stream->outpos < back) { + stream->error = TRUE; + return; + } + + if (stream->outpos + len > stream->outlen) { + stream->error = TRUE; + return; + } + + do { + stream->out[stream->outpos] = stream->out[stream->outpos - back]; + stream->outpos++; + len--; + } while (len > 0); +} + +static NTSTATUS do_lzo_decompress(lzo_stream* stream) { + UINT8 byte; + UINT32 len, back; + BOOL backcopy = FALSE; + + stream->error = FALSE; + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + + if (byte > 17) { + lzo_copy(stream, byte - 17); + if (stream->error) return STATUS_INTERNAL_ERROR; + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + + if (byte < 16) return STATUS_INTERNAL_ERROR; + } + + while (1) { + if (byte >> 4) { + backcopy = TRUE; + if (byte >> 6) { + len = (byte >> 5) - 1; + back = (lzo_nextbyte(stream) << 3) + ((byte >> 2) & 7) + 1; + if (stream->error) return STATUS_INTERNAL_ERROR; + } else if (byte >> 5) { + len = lzo_len(stream, byte, 31); + if (stream->error) return STATUS_INTERNAL_ERROR; + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + + back = (lzo_nextbyte(stream) << 6) + (byte >> 2) + 1; + if (stream->error) return STATUS_INTERNAL_ERROR; + } else { + len = lzo_len(stream, byte, 7); + if (stream->error) return STATUS_INTERNAL_ERROR; + + back = (1 << 14) + ((byte & 8) << 11); + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + + back += (lzo_nextbyte(stream) << 6) + (byte >> 2); + if (stream->error) return STATUS_INTERNAL_ERROR; + + if (back == (1 << 14)) { + if (len != 1) + return STATUS_INTERNAL_ERROR; + break; + } + } + } else if (backcopy) { + len = 0; + back = (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1; + if (stream->error) return STATUS_INTERNAL_ERROR; + } else { + len = lzo_len(stream, byte, 15); + if (stream->error) return STATUS_INTERNAL_ERROR; + + lzo_copy(stream, len + 3); + if (stream->error) return STATUS_INTERNAL_ERROR; + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + + if (byte >> 4) + continue; + + len = 1; + back = (1 << 11) + (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1; + if (stream->error) return STATUS_INTERNAL_ERROR; + + break; + } + + lzo_copyback(stream, back, len + 2); + if (stream->error) return STATUS_INTERNAL_ERROR; + + len = byte & 3; + + if (len) { + lzo_copy(stream, len); + if (stream->error) return STATUS_INTERNAL_ERROR; + } else + backcopy = !backcopy; + + byte = lzo_nextbyte(stream); + if (stream->error) return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) { + NTSTATUS Status; + UINT32 extlen, partlen, inoff, outoff; + lzo_stream stream; + + extlen = *((UINT32*)inbuf); + if (inlen < extlen) { + ERR("compressed extent was %llx, should have been at least %x\n", inlen, extlen); + return STATUS_INTERNAL_ERROR; + } + + inoff = sizeof(UINT32); + outoff = 0; + + do { + partlen = *(UINT32*)&inbuf[inoff]; + + if (partlen + inoff > inlen) { + ERR("overflow: %x + %x > %llx\n", partlen, inoff, inlen); + return STATUS_INTERNAL_ERROR; + } + + inoff += sizeof(UINT32); + + stream.in = &inbuf[inoff]; + stream.inlen = partlen; + stream.inpos = 0; + stream.out = &outbuf[outoff]; + stream.outlen = LINUX_PAGE_SIZE; + stream.outpos = 0; + + Status = do_lzo_decompress(&stream); + if (!NT_SUCCESS(Status)) { + ERR("do_lzo_decompress returned %08x\n", Status); + return Status; + } + + if (stream.outpos < stream.outlen) + RtlZeroMemory(&stream.out[stream.outpos], stream.outlen - stream.outpos); + + inoff += partlen; + outoff += stream.outlen; + + if (LINUX_PAGE_SIZE - (inoff % LINUX_PAGE_SIZE) < sizeof(UINT32)) + inoff = ((inoff / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE; + } while (inoff < extlen); + + return STATUS_SUCCESS; +} + +static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) { + return ExAllocatePoolWithTag(PagedPool, items * size, ALLOC_TAG_ZLIB); +} + +static void zlib_free(void* opaque, void* ptr) { + ExFreePool(ptr); +} + +static NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) { + z_stream c_stream; + int ret; + + c_stream.zalloc = zlib_alloc; + c_stream.zfree = zlib_free; + c_stream.opaque = (voidpf)0; + + ret = inflateInit(&c_stream); + + if (ret != Z_OK) { + ERR("inflateInit returned %08x\n", ret); + return STATUS_INTERNAL_ERROR; + } + + c_stream.next_in = inbuf; + c_stream.avail_in = inlen; + + c_stream.next_out = outbuf; + c_stream.avail_out = outlen; + + do { + ret = inflate(&c_stream, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + ERR("inflate returned %08x\n", ret); + inflateEnd(&c_stream); + return STATUS_INTERNAL_ERROR; + } + } while (ret != Z_STREAM_END); + + ret = inflateEnd(&c_stream); + + if (ret != Z_OK) { + ERR("inflateEnd returned %08x\n", ret); + return STATUS_INTERNAL_ERROR; + } + + // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace? + + return STATUS_SUCCESS; +} + +NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) { + if (type == BTRFS_COMPRESSION_ZLIB) + return zlib_decompress(inbuf, inlen, outbuf, outlen); + else if (type == BTRFS_COMPRESSION_LZO) + return lzo_decompress(inbuf, inlen, outbuf, outlen); + else { + ERR("unsupported compression type %x\n", type); + return STATUS_NOT_SUPPORTED; + } +} + +static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + NTSTATUS Status; + UINT8 compression; + UINT64 comp_length; + UINT8* comp_data; + UINT32 out_left; + LIST_ENTRY* le; + chunk* c; + z_stream c_stream; + int ret; + + comp_data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG); + if (!comp_data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + ExFreePool(comp_data); + return Status; + } + + c_stream.zalloc = zlib_alloc; + c_stream.zfree = zlib_free; + c_stream.opaque = (voidpf)0; + + ret = deflateInit(&c_stream, fcb->Vcb->options.zlib_level); + + if (ret != Z_OK) { + ERR("deflateInit returned %08x\n", ret); + ExFreePool(comp_data); + return STATUS_INTERNAL_ERROR; + } + + c_stream.avail_in = end_data - start_data; + c_stream.next_in = data; + c_stream.avail_out = end_data - start_data; + c_stream.next_out = comp_data; + + do { + ret = deflate(&c_stream, Z_FINISH); + + if (ret == Z_STREAM_ERROR) { + ERR("deflate returned %x\n", ret); + ExFreePool(comp_data); + return STATUS_INTERNAL_ERROR; + } + } while (c_stream.avail_in > 0 && c_stream.avail_out > 0); + + out_left = c_stream.avail_out; + + ret = deflateEnd(&c_stream); + + if (ret != Z_OK) { + ERR("deflateEnd returned %08x\n", ret); + ExFreePool(comp_data); + return STATUS_INTERNAL_ERROR; + } + + if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent + ExFreePool(comp_data); + + comp_length = end_data - start_data; + comp_data = data; + compression = BTRFS_COMPRESSION_NONE; + + *compressed = FALSE; + } else { + UINT32 cl; + + compression = BTRFS_COMPRESSION_ZLIB; + cl = end_data - start_data - out_left; + comp_length = sector_align(cl, fcb->Vcb->superblock.sector_size); + + RtlZeroMemory(comp_data + cl, comp_length - cl); + + *compressed = TRUE; + } + + ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); + + le = fcb->Vcb->chunks.Flink; + while (le != &fcb->Vcb->chunks) { + c = CONTAINING_RECORD(le, chunk, list_entry); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + + if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) { + if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) { + ExReleaseResourceLite(&c->lock); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + if (compression != BTRFS_COMPRESSION_NONE) + ExFreePool(comp_data); + + return STATUS_SUCCESS; + } + } + + ExReleaseResourceLite(&c->lock); + + le = le->Flink; + } + + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) { + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + + if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) { + if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) { + ExReleaseResourceLite(&c->lock); + + if (compression != BTRFS_COMPRESSION_NONE) + ExFreePool(comp_data); + + return STATUS_SUCCESS; + } + } + + ExReleaseResourceLite(&c->lock); + } else + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + WARN("couldn't find any data chunks with %llx bytes free\n", comp_length); + + return STATUS_DISK_FULL; +} + +static NTSTATUS lzo_do_compress(const UINT8* in, UINT32 in_len, UINT8* out, UINT32* out_len, void* wrkmem) { + const UINT8* ip; + UINT32 dv; + UINT8* op; + const UINT8* in_end = in + in_len; + const UINT8* ip_end = in + in_len - 9 - 4; + const UINT8* ii; + const UINT8** dict = (const UINT8**)wrkmem; + + op = out; + ip = in; + ii = ip; + + DVAL_FIRST(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++; + DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++; + DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++; + DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++; + + while (1) { + const UINT8* m_pos; + UINT32 m_len; + ptrdiff_t m_off; + UINT32 lit, dindex; + + dindex = DINDEX(dv, ip); + m_pos = dict[dindex]; + UPDATE_I(dict, cycle, dindex, ip); + + if (!LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, M4_MAX_OFFSET) && m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2]) { + lit = ip - ii; + m_pos += 3; + if (m_off <= M2_MAX_OFFSET) + goto match; + + if (lit == 3) { /* better compression, but slower */ + if (op - 2 <= out) + return STATUS_INTERNAL_ERROR; + + op[-2] |= LZO_BYTE(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + + if (*m_pos == ip[3]) + goto match; + } + + /* a literal */ + ++ip; + if (ip >= ip_end) + break; + DVAL_NEXT(dv, ip); + continue; + + /* a match */ +match: + /* store current literal run */ + if (lit > 0) { + UINT32 t = lit; + + if (t <= 3) { + if (op - 2 <= out) + return STATUS_INTERNAL_ERROR; + + op[-2] |= LZO_BYTE(t); + } else if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else { + UINT32 tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + + if (tt <= 0) + return STATUS_INTERNAL_ERROR; + + *op++ = LZO_BYTE(tt); + } + + do { + *op++ = *ii++; + } while (--t > 0); + } + + + /* code the match */ +code_match: + if (ii != ip) + return STATUS_INTERNAL_ERROR; + + ip += 3; + if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ || + *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) { + --ip; + m_len = ip - ii; + + if (m_len < 3 || m_len > 8) + return STATUS_INTERNAL_ERROR; + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); + } else if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { + m_off -= 0x4000; + + if (m_off <= 0 || m_off > 0x7fff) + return STATUS_INTERNAL_ERROR; + + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; + } + } else { + const UINT8* end; + end = in_end; + while (ip < end && *m_pos == *ip) + m_pos++, ip++; + m_len = (ip - ii); + + if (m_len < 3) + return STATUS_INTERNAL_ERROR; + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { + m_off -= 0x4000; + + if (m_off <= 0 || m_off > 0x7fff) + return STATUS_INTERNAL_ERROR; + + if (m_len <= 9) + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + else { + m_len -= 9; + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11)); +m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + + if (m_len <= 0) + return STATUS_INTERNAL_ERROR; + + *op++ = LZO_BYTE(m_len); + } + } + +m3_m4_offset: + *op++ = LZO_BYTE((m_off & 63) << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + + ii = ip; + if (ip >= ip_end) + break; + DVAL_FIRST(dv, ip); + } + + /* store final literal run */ + if (in_end - ii > 0) { + UINT32 t = in_end - ii; + + if (op == out && t <= 238) + *op++ = LZO_BYTE(17 + t); + else if (t <= 3) + op[-2] |= LZO_BYTE(t); + else if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else { + UINT32 tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + + if (tt <= 0) + return STATUS_INTERNAL_ERROR; + + *op++ = LZO_BYTE(tt); + } + + do { + *op++ = *ii++; + } while (--t > 0); + } + + *out_len = op - out; + + return STATUS_SUCCESS; +} + +static NTSTATUS lzo1x_1_compress(lzo_stream* stream) { + UINT8 *op = stream->out; + NTSTATUS Status = STATUS_SUCCESS; + + if (stream->inlen <= 0) + stream->outlen = 0; + else if (stream->inlen <= 9 + 4) { + *op++ = LZO_BYTE(17 + stream->inlen); + + stream->inpos = 0; + do { + *op++ = stream->in[stream->inpos]; + stream->inpos++; + } while (stream->inlen < stream->inpos); + stream->outlen = op - stream->out; + } else + Status = lzo_do_compress(stream->in, stream->inlen, stream->out, &stream->outlen, stream->wrkmem); + + if (Status == STATUS_SUCCESS) { + op = stream->out + stream->outlen; + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + stream->outlen += 3; + } + + return Status; +} + +static __inline UINT32 lzo_max_outlen(UINT32 inlen) { + return inlen + (inlen / 16) + 64 + 3; // formula comes from LZO.FAQ +} + +static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + NTSTATUS Status; + UINT8 compression; + UINT64 comp_length; + ULONG comp_data_len, num_pages, i; + UINT8* comp_data; + BOOL skip_compression = FALSE; + lzo_stream stream; + UINT32* out_size; + LIST_ENTRY* le; + chunk* c; + + num_pages = (sector_align(end_data - start_data, LINUX_PAGE_SIZE)) / LINUX_PAGE_SIZE; + + // Four-byte overall header + // Another four-byte header page + // Each page has a maximum size of lzo_max_outlen(LINUX_PAGE_SIZE) + // Plus another four bytes for possible padding + comp_data_len = sizeof(UINT32) + ((lzo_max_outlen(LINUX_PAGE_SIZE) + (2 * sizeof(UINT32))) * num_pages); + + comp_data = ExAllocatePoolWithTag(PagedPool, comp_data_len, ALLOC_TAG); + if (!comp_data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + stream.wrkmem = ExAllocatePoolWithTag(PagedPool, LZO1X_MEM_COMPRESS, ALLOC_TAG); + if (!stream.wrkmem) { + ERR("out of memory\n"); + ExFreePool(comp_data); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + ExFreePool(comp_data); + ExFreePool(stream.wrkmem); + return Status; + } + + out_size = (UINT32*)comp_data; + *out_size = sizeof(UINT32); + + stream.in = data; + stream.out = comp_data + (2 * sizeof(UINT32)); + + for (i = 0; i < num_pages; i++) { + UINT32* pagelen = (UINT32*)(stream.out - sizeof(UINT32)); + + stream.inlen = min(LINUX_PAGE_SIZE, end_data - start_data - (i * LINUX_PAGE_SIZE)); + + Status = lzo1x_1_compress(&stream); + if (!NT_SUCCESS(Status)) { + ERR("lzo1x_1_compress returned %08x\n", Status); + skip_compression = TRUE; + break; + } + + *pagelen = stream.outlen; + *out_size += stream.outlen + sizeof(UINT32); + + stream.in += LINUX_PAGE_SIZE; + stream.out += stream.outlen + sizeof(UINT32); + + if (LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE) < sizeof(UINT32)) { + RtlZeroMemory(stream.out, LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE)); + stream.out += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE); + *out_size += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE); + } + } + + ExFreePool(stream.wrkmem); + + if (skip_compression || *out_size >= end_data - start_data - fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent + ExFreePool(comp_data); + + comp_length = end_data - start_data; + comp_data = data; + compression = BTRFS_COMPRESSION_NONE; + + *compressed = FALSE; + } else { + compression = BTRFS_COMPRESSION_LZO; + comp_length = sector_align(*out_size, fcb->Vcb->superblock.sector_size); + + RtlZeroMemory(comp_data + *out_size, comp_length - *out_size); + + *compressed = TRUE; + } + + ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); + + le = fcb->Vcb->chunks.Flink; + while (le != &fcb->Vcb->chunks) { + c = CONTAINING_RECORD(le, chunk, list_entry); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + + if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) { + if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) { + ExReleaseResourceLite(&c->lock); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + if (compression != BTRFS_COMPRESSION_NONE) + ExFreePool(comp_data); + + return STATUS_SUCCESS; + } + } + + ExReleaseResourceLite(&c->lock); + + le = le->Flink; + } + + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) { + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + + if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) { + if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) { + ExReleaseResourceLite(&c->lock); + + if (compression != BTRFS_COMPRESSION_NONE) + ExFreePool(comp_data); + + return STATUS_SUCCESS; + } + } + + ExReleaseResourceLite(&c->lock); + } else + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + WARN("couldn't find any data chunks with %llx bytes free\n", comp_length); + + return STATUS_DISK_FULL; +} + +NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + UINT8 type; + + if (fcb->Vcb->options.compress_type != 0) + type = fcb->Vcb->options.compress_type; + else { + if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO) + type = BTRFS_COMPRESSION_LZO; + else + type = BTRFS_COMPRESSION_ZLIB; + } + + if (type == BTRFS_COMPRESSION_LZO) { + fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO; + return lzo_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback); + } else + return zlib_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback); +} diff --git a/reactos/drivers/filesystems/btrfs/create.c b/reactos/drivers/filesystems/btrfs/create.c index 39b6cf2630d..55768336fab 100644 --- a/reactos/drivers/filesystems/btrfs/create.c +++ b/reactos/drivers/filesystems/btrfs/create.c @@ -25,7 +25,7 @@ extern PDEVICE_OBJECT devobj; -static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) { +static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -35,7 +35,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode searchkey.obj_type = TYPE_INODE_REF; searchkey.offset = parinode; - Status = find_item(Vcb, r, &tp, &searchkey, FALSE); + Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -82,7 +82,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode return STATUS_NOT_FOUND; } -static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) { +static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -92,7 +92,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6 searchkey.obj_type = TYPE_INODE_EXTREF; searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length); - Status = find_item(Vcb, r, &tp, &searchkey, FALSE); + Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -139,7 +139,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6 return STATUS_NOT_FOUND; } -static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) { +static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -149,7 +149,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub searchkey.obj_type = TYPE_ROOT_REF; searchkey.offset = subvolid; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -181,7 +181,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub return STATUS_NOT_FOUND; } -static NTSTATUS load_index_list(fcb* fcb) { +static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; NTSTATUS Status; @@ -191,14 +191,14 @@ static NTSTATUS load_index_list(fcb* fcb) { searchkey.obj_type = TYPE_DIR_INDEX; searchkey.offset = 2; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; } if (keycmp(&tp.item->key, &searchkey) == -1) { - if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) { + if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) { 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); @@ -315,7 +315,7 @@ static NTSTATUS load_index_list(fcb* fcb) { } nextitem: - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -343,7 +343,7 @@ end: } static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type, - UINT64* pindex, PANSI_STRING utf8) { + UINT64* pindex, PANSI_STRING utf8, PIRP Irp) { LIST_ENTRY* le; NTSTATUS Status; UNICODE_STRING us; @@ -360,7 +360,7 @@ static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING fil ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE); if (!fr->fcb->index_loaded) { - Status = load_index_list(fr->fcb); + Status = load_index_list(fr->fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("load_index_list returned %08x\n", Status); goto end; @@ -472,8 +472,8 @@ end: return Status; } -NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, - root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) { +static NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, + root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8, PIRP Irp) { DIR_ITEM* di; KEY searchkey; traverse_ptr tp; @@ -487,7 +487,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR searchkey.obj_type = TYPE_DIR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -533,6 +533,8 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); } else { + ANSI_STRING nutf8; + us.Buffer = utf16; us.Length = us.MaximumLength = (USHORT)stringlen; @@ -591,17 +593,20 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR index = 0; if (fr->fcb->subvol != Vcb->root_root) { + nutf8.Buffer = di->name; + nutf8.Length = nutf8.MaximumLength = di->n; + if (di->key.obj_type == TYPE_ROOT_ITEM) { - Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index); + Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp); if (!NT_SUCCESS(Status)) { ERR("find_subvol_dir_index returned %08x\n", Status); return Status; } } else { - Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index); + Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp); if (!NT_SUCCESS(Status)) { if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) { - Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index); + Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp); if (!NT_SUCCESS(Status)) { ERR("find_file_dir_index_extref returned %08x\n", Status); @@ -651,7 +656,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR } byindex: - Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8); + Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8, Irp); if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { ERR("find_file_in_dir_index returned %08x\n", Status); return Status; @@ -660,7 +665,7 @@ byindex: return Status; } -fcb* create_fcb() { +fcb* create_fcb(POOL_TYPE pool_type) { fcb* fcb; fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG); @@ -742,7 +747,7 @@ file_ref* create_fileref() { } NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr, - root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) { + root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp) { char* fn; UINT32 crc32; ULONG utf8len; @@ -772,10 +777,10 @@ NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filenam crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len); TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32); - return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8); + return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, Irp); } -static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) { +static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) { NTSTATUS Status; ULONG utf8len; char* utf8; @@ -823,7 +828,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -876,7 +881,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, searchkey.offset = 0; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -959,7 +964,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, } } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -1133,7 +1138,7 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) { return deleted; } -static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) { +static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -1143,7 +1148,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return 0; @@ -1169,7 +1174,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 return ei->refcount; } -NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) { +NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -1199,7 +1204,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, } } - fcb = create_fcb(); + fcb = create_fcb(PagedPool); if (!fcb) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; @@ -1215,7 +1220,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); free_fcb(fcb); @@ -1248,15 +1253,15 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, fcb->type = BTRFS_TYPE_FILE; } - fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE); + fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp); - fcb_get_sd(fcb, parent); + fcb_get_sd(fcb, parent, Irp); if (fcb->type == BTRFS_TYPE_DIRECTORY) { UINT8* xattrdata; UINT16 xattrlen; - if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) { + if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen, Irp)) { fcb->reparse_xattr.Buffer = (char*)xattrdata; fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen; } @@ -1279,7 +1284,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, searchkey.obj_type = TYPE_EXTENT_DATA; searchkey.offset = 0; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); free_fcb(fcb); @@ -1312,8 +1317,11 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, return STATUS_INTERNAL_ERROR; } + if (ed2->address == 0 && ed2->size == 0) // sparse + goto nextitem; + if (ed2->size != 0) - unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1; + unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size, Irp) == 1; } ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); @@ -1340,7 +1348,8 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, InsertTailList(&fcb->extents, &ext->list_entry); } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); +nextitem: + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -1365,7 +1374,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, searchkey.obj_type = TYPE_INODE_REF; searchkey.offset = 0; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); free_fcb(fcb); @@ -1509,7 +1518,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, } } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -1524,10 +1533,13 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, } NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, - UINT32 streamhash, fcb* parent, fcb** pfcb) { + UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) { fcb* fcb; UINT8* xattrdata; - UINT16 xattrlen; + UINT16 xattrlen, overhead; + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; if (!IsListEmpty(&subvol->fcbs)) { LIST_ENTRY* le = subvol->fcbs.Flink; @@ -1553,13 +1565,13 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI } } - fcb = create_fcb(); + fcb = create_fcb(PagedPool); if (!fcb) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } - if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) { + if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) { ERR("get_xattr failed\n"); free_fcb(fcb); return STATUS_INTERNAL_ERROR; @@ -1574,6 +1586,35 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI fcb->adshash = streamhash; fcb->adsxattr = *xattr; + // find XATTR_ITEM overhead and hence calculate maximum length + + searchkey.obj_id = parent->inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = streamhash; + + Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp); + if (!NT_SUCCESS(Status)) { + ERR("find_item returned %08x\n", Status); + free_fcb(fcb); + return Status; + } + + if (keycmp(&tp.item->key, &searchkey)) { + ERR("error - could not find key for xattr\n"); + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } + + if (tp.item->size < xattrlen) { + ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen); + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } + + overhead = tp.item->size - xattrlen; + + fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead; + fcb->adsdata.Buffer = (char*)xattrdata; fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen; @@ -1623,7 +1664,7 @@ void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) { ExReleaseResourceLite(&parent->nonpaged->children_lock); } -NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) { +NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp) { UNICODE_STRING fnus2; file_ref *dir, *sf, *sf2; ULONG i, num_parts; @@ -1632,6 +1673,13 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu NTSTATUS Status; TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed); + +#ifdef DEBUG + if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { + ERR("fcb_lock not acquired exclusively\n"); + int3; + } +#endif if (Vcb->removing || Vcb->locked) return STATUS_ACCESS_DENIED; @@ -1743,7 +1791,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu // FIXME - check if already opened - if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) { + if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr, Irp)) { TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer); Status = STATUS_OBJECT_NAME_NOT_FOUND; @@ -1759,7 +1807,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu goto end; } - Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb); + Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb_stream returned %08x\n", Status); goto end; @@ -1810,7 +1858,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu UINT8 type; ANSI_STRING utf8; - Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8); + Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, Irp); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer); @@ -1823,19 +1871,19 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu fcb* fcb; ULONG strlen; - if (type != BTRFS_TYPE_DIRECTORY && !lastpart) { - WARN("passed path including file as subdirectory\n"); - - Status = STATUS_OBJECT_PATH_NOT_FOUND; - goto end; - } - - Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb); + Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); goto end; } + if (type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) { + WARN("passed path including file as subdirectory\n"); + free_fcb(fcb); + Status = STATUS_OBJECT_PATH_NOT_FOUND; + goto end; + } + sf2 = create_fileref(); if (!sf2) { ERR("out of memory\n"); @@ -1891,8 +1939,12 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu } } - if (i == num_parts - 1) + if (i == num_parts - 1) { + if (fn_offset) + *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer; + break; + } if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) { Status = STATUS_REPARSE; @@ -1912,8 +1964,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu *pfr = sf2; end: - ExReleaseResourceLite(&Vcb->fcb_lock); free_fileref(sf); + ExReleaseResourceLite(&Vcb->fcb_lock); end2: if (parts) @@ -1924,7 +1976,7 @@ end2: return Status; } -NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) { +NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) { KEY searchkey; traverse_ptr tp, prev_tp; NTSTATUS Status; @@ -1942,14 +1994,14 @@ NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) { searchkey.obj_type = TYPE_DIR_INDEX + 1; searchkey.offset = 0; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; } if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) { - if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE)) + if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE, Irp)) tp = prev_tp; } @@ -1979,6 +2031,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S LARGE_INTEGER time; BTRFS_TIME now; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; ULONG defda; file_ref* fileref; hardlink* hl; @@ -1990,7 +2043,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S if (!NT_SUCCESS(Status)) return Status; - utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG); + utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG); if (!utf8) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; @@ -2004,7 +2057,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S utf8[utf8len] = 0; - Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos); + Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); ExFreePool(utf8); @@ -2028,7 +2081,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S mark_fcb_dirty(parfileref->fcb); if (parfileref->fcb->subvol->lastinode == 0) - get_last_inode(Vcb, parfileref->fcb->subvol); + get_last_inode(Vcb, parfileref->fcb->subvol, Irp); inode = parfileref->fcb->subvol->lastinode + 1; @@ -2057,15 +2110,18 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S parfileref->fcb->subvol->lastinode++; - fcb = create_fcb(); + fcb = create_fcb(pool_type); if (!fcb) { ERR("out of memory\n"); ExFreePool(utf8); return STATUS_INSUFFICIENT_RESOURCES; } - + fcb->Vcb = Vcb; - + + if (IrpSp->Flags & SL_OPEN_PAGING_FILE) + fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE; + fcb->inode_item.generation = Vcb->superblock.generation; fcb->inode_item.transid = Vcb->superblock.generation; fcb->inode_item.st_size = 0; @@ -2090,12 +2146,19 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory } - // inherit nodatacow flag from parent directory - if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) { - fcb->inode_item.flags |= BTRFS_INODE_NODATACOW; + if (IrpSp->Flags & SL_OPEN_PAGING_FILE) { + fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS; + } else { + // inherit nodatacow flag from parent directory + if (parfileref->fcb->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; + } - if (type != BTRFS_TYPE_DIRECTORY) - fcb->inode_item.flags |= BTRFS_INODE_NODATASUM; + if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS) + fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; } fcb->Header.IsFastIoPossible = fast_io_possible(fcb); @@ -2120,16 +2183,24 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S if (!NT_SUCCESS(Status)) { ERR("fcb_get_new_sd returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return Status; } fcb->sd_dirty = TRUE; - hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG); if (!hl) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -2137,24 +2208,32 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S hl->index = dirpos; hl->utf8.Length = hl->utf8.MaximumLength = utf8len; - hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG); + hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG); if (!hl->utf8.Buffer) { ERR("out of memory\n"); ExFreePool(hl); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len); hl->name.Length = hl->name.MaximumLength = fpus->Length; - hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG); + hl->name.Buffer = ExAllocatePoolWithTag(pool_type, fpus->Length, ALLOC_TAG); if (!hl->name.Buffer) { ERR("out of memory\n"); ExFreePool(hl->utf8.Buffer); ExFreePool(hl); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -2165,7 +2244,11 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S fileref = create_fileref(); if (!fileref) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -2175,21 +2258,43 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len; fileref->utf8.Buffer = utf8; - fileref->filepart = *fpus; + fileref->filepart.Length = fileref->filepart.MaximumLength = fpus->Length; + + if (fileref->filepart.Length == 0) + fileref->filepart.Buffer = NULL; + else { + fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG); + + if (!fileref->filepart.Buffer) { + ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fileref->filepart.Buffer, fpus->Buffer, fpus->Length); + } Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); return Status; } - if (Irp->Overlay.AllocationSize.QuadPart > 0) { + if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) { Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback); if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); return Status; } } @@ -2204,29 +2309,8 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S fcb->subvol->root_item.ctime = now; fileref->parent = parfileref; - - ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE); - - if (IsListEmpty(&parfileref->children)) - InsertTailList(&parfileref->children, &fileref->list_entry); - else { - LIST_ENTRY* le = parfileref->children.Flink; - file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry); - - while (le != &parfileref->children) { - file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry); - - if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) { - InsertHeadList(&fr1->list_entry, &fileref->list_entry); - break; - } - - fr1 = fr2; - le = le->Flink; - } - } - - ExReleaseResourceLite(&parfileref->nonpaged->children_lock); + + insert_fileref_child(parfileref, fileref, TRUE); increase_fileref_refcount(parfileref); @@ -2243,21 +2327,250 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S return STATUS_SUCCESS; } +static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, + PIRP Irp, ULONG options, POOL_TYPE pool_type, LIST_ENTRY* rollback) { + file_ref *fileref, *newpar, *parfileref; + fcb* fcb; + static char xapref[] = "user."; + ULONG xapreflen = strlen(xapref), overhead; + LARGE_INTEGER time; + BTRFS_TIME now; + ULONG utf8len; + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; +#ifdef DEBUG_FCB_REFCOUNTS + LONG rc; +#endif + + TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer); + TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer); + + parfileref = *pparfileref; + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &newpar, fpus, parfileref, FALSE, NULL, NULL, Irp); + ExReleaseResource(&Vcb->fcb_lock); + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + UNICODE_STRING fpus2; + + if (!is_file_name_valid(fpus)) + return STATUS_OBJECT_NAME_INVALID; + + fpus2.Length = fpus2.MaximumLength = fpus->Length; + fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG); + + if (!fpus2.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length); + + Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback); + + if (!NT_SUCCESS(Status)) { + ERR("file_create2 returned %08x\n", Status); + ExFreePool(fpus2.Buffer); + return Status; + } + + send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); + send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + } else if (!NT_SUCCESS(Status)) { + ERR("open_fileref returned %08x\n", Status); + return Status; + } + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(parfileref); + ExReleaseResource(&Vcb->fcb_lock); + + parfileref = newpar; + *pparfileref = parfileref; + + if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) { + WARN("parent not file or symlink\n"); + return STATUS_INVALID_PARAMETER; + } + + if (options & FILE_DIRECTORY_FILE) { + WARN("tried to create directory as stream\n"); + return STATUS_INVALID_PARAMETER; + } + + fcb = create_fcb(pool_type); + if (!fcb) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + fcb->Vcb = Vcb; + + fcb->Header.IsFastIoPossible = fast_io_possible(fcb); + fcb->Header.AllocationSize.QuadPart = 0; + fcb->Header.FileSize.QuadPart = 0; + fcb->Header.ValidDataLength.QuadPart = 0; + +#ifdef DEBUG_FCB_REFCOUNTS + rc = InterlockedIncrement(&parfileref->fcb->refcount); + WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref)); +#else + InterlockedIncrement(&parfileref->fcb->refcount); +#endif + fcb->subvol = parfileref->fcb->subvol; + fcb->inode = parfileref->fcb->inode; + fcb->type = parfileref->fcb->type; + + fcb->ads = TRUE; + + Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length); + if (!NT_SUCCESS(Status)) { + ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return Status; + } + + fcb->adsxattr.Length = utf8len + xapreflen; + fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1; + fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG); + if (!fcb->adsxattr.Buffer) { + ERR("out of memory\n"); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen); + + Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length); + if (!NT_SUCCESS(Status)) { + ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return Status; + } + + 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); + + searchkey.obj_id = parfileref->fcb->inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = fcb->adshash; + + Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp); + if (!NT_SUCCESS(Status)) { + ERR("find_item returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return Status; + } + + if (!keycmp(&tp.item->key, &searchkey)) + overhead = tp.item->size; + else + overhead = 0; + + fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1); + + if (utf8len + xapreflen + overhead > fcb->adsmaxlen) { + WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + xapreflen, overhead, fcb->adsmaxlen); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_DISK_FULL; + } else + fcb->adsmaxlen -= overhead + utf8len + xapreflen; + + fileref = create_fileref(); + if (!fileref) { + ERR("out of memory\n"); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fcb(fcb); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; + } + + fileref->fcb = fcb; + + fileref->filepart.MaximumLength = fileref->filepart.Length = stream->Length; + fileref->filepart.Buffer = ExAllocatePoolWithTag(pool_type, fileref->filepart.MaximumLength, ALLOC_TAG); + if (!fileref->filepart.Buffer) { + ERR("out of memory\n"); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length); + + Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); + return Status; + } + + mark_fcb_dirty(fcb); + mark_fileref_dirty(fileref); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); + ExReleaseResource(&Vcb->fcb_lock); + + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + parfileref->fcb->inode_item.transid = Vcb->superblock.generation; + parfileref->fcb->inode_item.sequence++; + parfileref->fcb->inode_item.st_ctime = now; + + mark_fcb_dirty(parfileref->fcb); + + parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; + parfileref->fcb->subvol->root_item.ctime = now; + + fileref->parent = (struct _file_ref*)parfileref; + + insert_fileref_child(parfileref, fileref, TRUE); + + increase_fileref_refcount(parfileref); + + *pfileref = fileref; + + return STATUS_SUCCESS; +} + static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) { NTSTATUS Status; // fcb *fcb, *parfcb = NULL; file_ref *fileref, *parfileref = NULL, *related; - ULONG i, j; + ULONG i, j, fn_offset; // ULONG utf8len; ccb* ccb; static WCHAR datasuf[] = {':','$','D','A','T','A',0}; UNICODE_STRING dsus, fpus, stream; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool; PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState; #ifdef DEBUG_FCB_REFCOUNTS LONG oc; #endif - + TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options); if (Vcb->readonly) @@ -2274,7 +2587,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC } else related = NULL; - Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, Irp); + ExReleaseResource(&Vcb->fcb_lock); + if (!NT_SUCCESS(Status)) goto end; @@ -2296,7 +2612,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; } fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR); - fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG); + fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG); if (!fpus.Buffer) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -2308,6 +2624,8 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR)); fpus.Buffer[j - i + 1] = 0; + fn_offset = i; + if (fpus.Length > dsus.Length) { // check for :$DATA suffix UNICODE_STRING lb; @@ -2341,183 +2659,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC } if (stream.Length > 0) { - file_ref* newpar; - fcb* fcb; - static char xapref[] = "user."; - ULONG xapreflen = strlen(xapref); - LARGE_INTEGER time; - BTRFS_TIME now; - ULONG utf8len; -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc; -#endif - - TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer); - TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer); - - Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL); - - if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { - UNICODE_STRING fpus2; - - if (!is_file_name_valid(&fpus)) - return STATUS_OBJECT_NAME_INVALID; - - fpus2.Length = fpus2.MaximumLength = fpus.Length; - fpus2.Buffer = ExAllocatePoolWithTag(PagedPool, fpus2.MaximumLength, ALLOC_TAG); - - if (!fpus2.Buffer) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(fpus2.Buffer, fpus.Buffer, fpus2.Length); - - Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("file_create2 returned %08x\n", Status); - ExFreePool(fpus2.Buffer); - goto end; - } - - send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); - send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); - } else if (!NT_SUCCESS(Status)) { - ERR("open_fileref returned %08x\n", Status); - goto end; - } - - free_fileref(parfileref); - parfileref = newpar; - - if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->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(); - if (!fcb) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - fcb->Vcb = Vcb; - - fcb->Header.IsFastIoPossible = fast_io_possible(fcb); - fcb->Header.AllocationSize.QuadPart = 0; - fcb->Header.FileSize.QuadPart = 0; - fcb->Header.ValidDataLength.QuadPart = 0; - -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&parfileref->fcb->refcount); - WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref)); -#else - InterlockedIncrement(&parfileref->fcb->refcount); -#endif - fcb->subvol = parfileref->fcb->subvol; - fcb->inode = parfileref->fcb->inode; - fcb->type = parfileref->fcb->type; - - fcb->ads = TRUE; - - Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length); + Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, rollback); if (!NT_SUCCESS(Status)) { - ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status); - free_fcb(fcb); + ERR("create_stream returned %08x\n", 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); - if (!fcb->adsxattr.Buffer) { - ERR("out of memory\n"); - free_fcb(fcb); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen); - - Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length); - if (!NT_SUCCESS(Status)) { - ERR("RtlUnicodeToUTF8N 2 returned %08x\n", 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); - - fileref = create_fileref(); - if (!fileref) { - ERR("out of memory\n"); - free_fcb(fcb); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - fileref->fcb = fcb; - - fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length; - fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG); - if (!fileref->filepart.Buffer) { - ERR("out of memory\n"); - free_fileref(fileref); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length); - - Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); - if (!NT_SUCCESS(Status)) { - ERR("RtlUpcaseUnicodeString returned %08x\n", Status); - free_fileref(fileref); - goto end; - } - - mark_fcb_dirty(fcb); - - InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry); - InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); - - KeQuerySystemTime(&time); - win_time_to_unix(time, &now); - - parfileref->fcb->inode_item.transid = Vcb->superblock.generation; - parfileref->fcb->inode_item.sequence++; - parfileref->fcb->inode_item.st_ctime = now; - - mark_fcb_dirty(parfileref->fcb); - - parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; - parfileref->fcb->subvol->root_item.ctime = now; - - fileref->parent = (struct _file_ref*)parfileref; - - ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE); - InsertTailList(&parfileref->children, &fileref->list_entry); - ExReleaseResourceLite(&parfileref->nonpaged->children_lock); - - increase_fileref_refcount(parfileref); - - ExFreePool(fpus.Buffer); - fpus.Buffer = NULL; } else { if (!is_file_name_valid(&fpus)) { Status = STATUS_OBJECT_NAME_INVALID; @@ -2541,7 +2687,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC if (!ccb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto end; } @@ -2567,6 +2715,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC #endif FileObject->FsContext2 = ccb; + + if (fn_offset > 0) { + FileObject->FileName.Length -= fn_offset * sizeof(WCHAR); + RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length); + } FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object; @@ -2603,8 +2756,11 @@ end: ExFreePool(fpus.Buffer); end2: - if (parfileref) + if (parfileref) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(parfileref); + ExReleaseResource(&Vcb->fcb_lock); + } return Status; } @@ -2740,7 +2896,7 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) { if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) { ULONG size, bytes_read, i; - if (fcb->inode_item.st_size < sizeof(ULONG)) { + if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) { WARN("file was too short to be a reparse point\n"); return STATUS_INVALID_PARAMETER; } @@ -2861,6 +3017,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN device_extension* Vcb = DeviceObject->DeviceExtension; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); USHORT unparsed; + ULONG fn_offset = 0; file_ref *related, *fileref; #ifdef DEBUG_FCB_REFCOUNTS LONG oc; @@ -2927,8 +3084,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN goto exit; } - // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags - + if (Vcb->readonly && Stack->Parameters.Create.SecurityContext->DesiredAccess & + (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) { + Status = STATUS_MEDIA_WRITE_PROTECTED; + goto exit; + } + if (options & FILE_OPEN_BY_FILE_ID) { if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) { UINT64 inode; @@ -2942,8 +3103,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN fileref = related->parent; increase_fileref_refcount(fileref); Status = STATUS_SUCCESS; - } else - Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref); + } else { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp); + ExReleaseResource(&Vcb->fcb_lock); + } } else { WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n"); Status = STATUS_NOT_IMPLEMENTED; @@ -2955,7 +3119,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN goto exit; } - Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset, Irp); + ExReleaseResource(&Vcb->fcb_lock); } if (Status == STATUS_REPARSE) { @@ -2999,7 +3165,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if (RequestedDisposition == FILE_CREATE) { TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref)); Status = STATUS_OBJECT_NAME_COLLISION; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { @@ -3007,9 +3175,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n"); goto exit; } - } else { + } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) { TRACE("open_fileref returned %08x\n", Status); goto exit; + } else { + ERR("open_fileref returned %08x\n", Status); + goto exit; } if (NT_SUCCESS(Status)) { // file already exists @@ -3018,17 +3189,30 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) { if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) { Status = STATUS_ACCESS_DENIED; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } if (Vcb->readonly) { Status = STATUS_MEDIA_WRITE_PROTECTED; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } } + if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && Stack->Parameters.Create.SecurityContext->DesiredAccess & + (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) { + Status = STATUS_ACCESS_DENIED; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); + goto exit; + } + TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE"); sf = fileref; @@ -3057,39 +3241,24 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) { Status = STATUS_ACCESS_DENIED; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } } - if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) { - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); - free_fileref(fileref); - ExReleaseResourceLite(&Vcb->fcb_lock); - - Status = STATUS_FILE_IS_A_DIRECTORY; - goto exit; - } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) { - TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref)); - - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); - free_fileref(fileref); - ExReleaseResourceLite(&Vcb->fcb_lock); - - Status = STATUS_NOT_A_DIRECTORY; - goto exit; - } - if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) { Status = STATUS_CANNOT_DELETE; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } - if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) { - UINT8* data; + REPARSE_DATA_BUFFER* data; /* How reparse points work from the point of view of the filesystem appears to * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return @@ -3098,7 +3267,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer, * IopSymlinkProcessReparse will do the translation for us. */ - Status = get_reparse_block(fileref->fcb, &data); + Status = get_reparse_block(fileref->fcb, (UINT8**)&data); if (!NT_SUCCESS(Status)) { ERR("get_reparse_block returned %08x\n", Status); @@ -3109,7 +3278,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN } Status = STATUS_REPARSE; - RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG)); + Irp->IoStatus.Information = data->ReparseTag; + + if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\') + data->Reserved = sizeof(WCHAR); Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data; @@ -3119,6 +3291,26 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN goto exit; } + if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY) { + if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(fileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + + Status = STATUS_FILE_IS_A_DIRECTORY; + goto exit; + } + } else if (options & FILE_DIRECTORY_FILE) { + TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref)); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + free_fileref(fileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + + Status = STATUS_NOT_A_DIRECTORY; + goto exit; + } + if (fileref->fcb->open_count > 0) { Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE); @@ -3155,7 +3347,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { WARN("cannot overwrite readonly file\n"); Status = STATUS_ACCESS_DENIED; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } @@ -3168,10 +3362,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN // } // FIXME - make sure not ADS! - Status = truncate_file(fileref->fcb, fileref->fcb->inode_item.st_size, rollback); + Status = truncate_file(fileref->fcb, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } @@ -3180,7 +3376,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fileref); + ExReleaseResource(&Vcb->fcb_lock); goto exit; } } @@ -3192,7 +3390,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN oldatts = fileref->fcb->atts; defda = get_file_attributes(Vcb, &fileref->fcb->inode_item, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type, - fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE); + fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp); if (RequestedDisposition == FILE_SUPERSEDE) fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE; @@ -3249,6 +3447,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN FileObject->FsContext2 = ccb; + if (fn_offset > 0) { + FileObject->FileName.Length -= fn_offset * sizeof(WCHAR); + RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length); + } + FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object; if (NT_SUCCESS(Status)) { @@ -3295,6 +3498,53 @@ exit: return Status; } +NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) { + UINT64 i; + + for (i = 0; i < Vcb->superblock.num_devices; i++) { + if (Vcb->devices[i].removable) { + NTSTATUS Status; + ULONG cc; + IO_STATUS_BLOCK iosb; + + Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) { + ERR("dev_ioctl returned %08x\n", Status); + return Status; + } + + if (iosb.Information < sizeof(ULONG)) { + ERR("iosb.Information was too short\n"); + return STATUS_INTERNAL_ERROR; + } + + if (cc != Vcb->devices[i].change_count) { + PDEVICE_OBJECT dev; + + Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME; + + dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread); + IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL); + + if (!dev) { + dev = IoGetDeviceToVerify(PsGetCurrentThread()); + IoSetDeviceToVerify(PsGetCurrentThread(), NULL); + } + + dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL; + + if (dev) + IoVerifyVolume(dev, FALSE); + + return STATUS_VERIFY_REQUIRED; + } + } + } + + return STATUS_SUCCESS; +} + NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; @@ -3321,6 +3571,13 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { } Vcb = DeviceObject->DeviceExtension; + + Status = verify_vcb(Vcb, Irp); + if (!NT_SUCCESS(Status)) { + ERR("verify_vcb returned %08x\n", Status); + goto exit; + } + ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE); locked = TRUE; diff --git a/reactos/drivers/filesystems/btrfs/dirctrl.c b/reactos/drivers/filesystems/btrfs/dirctrl.c index 972bc6f1b10..5cc54e971a4 100644 --- a/reactos/drivers/filesystems/btrfs/dirctrl.c +++ b/reactos/drivers/filesystems/btrfs/dirctrl.c @@ -32,7 +32,7 @@ typedef struct { enum DirEntryType dir_entry_type; } dir_entry; -static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) { +ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) { fcb* fcb; ULONG tag = 0, br; NTSTATUS Status; @@ -49,7 +49,7 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 return 0; ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); - Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb); + Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); ExReleaseResourceLite(&Vcb->fcb_lock); @@ -78,7 +78,9 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 end: ExReleaseResourceLite(fcb->Header.Resource); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); return tag; } @@ -154,7 +156,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -177,7 +179,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L BOOL dotfile = de->namelen > 1 && de->name[0] == '.'; - atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp); } } @@ -238,12 +240,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L 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->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); 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 = atts; fbdi->FileNameLength = stringlen; - fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); + fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp); fbdi->ShortNameLength = 0; // fibdi->ShortName[12]; @@ -277,7 +279,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L 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->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); 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 = atts; @@ -313,12 +315,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L 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->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); 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 = atts; ffdi->FileNameLength = stringlen; - ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); + ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp); Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen); @@ -353,12 +355,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L 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->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); 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 = atts; fibdi->FileNameLength = stringlen; - fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); + fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp); fibdi->ShortNameLength = 0; // fibdi->ShortName[12]; fibdi->FileId.QuadPart = inode; @@ -428,7 +430,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L return STATUS_NO_MORE_FILES; } -static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) { +static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; DIR_ITEM* di; @@ -520,14 +522,14 @@ static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_en searchkey.obj_type = TYPE_DIR_INDEX; searchkey.offset = *offset; - Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; } if (keycmp(&tp.item->key, &searchkey) == -1) { - if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE)) + if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp)) tp = next_tp; } @@ -766,7 +768,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } newoffset = ccb->query_dir_offset; - Status = next_dir_entry(fileref, &newoffset, &de); + Status = next_dir_entry(fileref, &newoffset, &de, Irp); if (!NT_SUCCESS(Status)) { if (Status == STATUS_NO_MORE_FILES && initial) @@ -845,7 +847,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ExFreePool(us.Buffer); if (!found) { - Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8); + Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8, Irp); if (!NT_SUCCESS(Status)) { Status = STATUS_NO_SUCH_FILE; @@ -901,7 +903,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ExFreePool(de.name); newoffset = ccb->query_dir_offset; - Status = next_dir_entry(fileref, &newoffset, &de); + Status = next_dir_entry(fileref, &newoffset, &de, Irp); ExFreePool(uni_fn); if (NT_SUCCESS(Status)) { @@ -976,7 +978,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNICODE_STRING di_uni_fn; newoffset = ccb->query_dir_offset; - Status = next_dir_entry(fileref, &newoffset, &de); + Status = next_dir_entry(fileref, &newoffset, &de, Irp); if (NT_SUCCESS(Status)) { if (has_wildcard) { ULONG stringlen; diff --git a/reactos/drivers/filesystems/btrfs/extent-tree.c b/reactos/drivers/filesystems/btrfs/extent-tree.c index 2bc760e6781..992b33d601d 100644 --- a/reactos/drivers/filesystems/btrfs/extent-tree.c +++ b/reactos/drivers/filesystems/btrfs/extent-tree.c @@ -91,7 +91,7 @@ static UINT64 get_extent_hash(UINT8 type, void* data) { } } -static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, LIST_ENTRY* rollback) { +static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; @@ -113,7 +113,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -153,7 +153,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, *ptr = type; RtlCopyMemory(ptr + 1, data, datalen); - if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -184,13 +184,13 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(ei); return STATUS_INTERNAL_ERROR; } - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -265,7 +265,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -335,7 +335,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -352,7 +352,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, searchkey.obj_type = type; searchkey.offset = offset; - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -381,7 +381,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp2, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -394,7 +394,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -408,7 +408,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG); RtlCopyMemory(data2, data, datalen); - if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -421,7 +421,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -429,7 +429,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, return STATUS_SUCCESS; } -NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) { +NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) { EXTENT_DATA_REF edr; edr.root = root; @@ -437,7 +437,7 @@ NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UI edr.offset = offset; edr.count = refcount; - return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, rollback); + return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback); } void decrease_chunk_usage(chunk* c, UINT64 delta) { @@ -447,7 +447,7 @@ void decrease_chunk_usage(chunk* c, UINT64 delta) { } static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, - UINT8 level, UINT64 parent, LIST_ENTRY* rollback) { + UINT8 level, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; NTSTATUS Status; traverse_ptr tp, tp2; @@ -464,7 +464,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -498,13 +498,13 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(ei); return STATUS_INTERNAL_ERROR; } - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -604,7 +604,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -643,7 +643,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -670,7 +670,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, searchkey.obj_type = type; searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data); - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -717,7 +717,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, newedr->count -= edr->count; - if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -736,7 +736,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -775,7 +775,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -810,7 +810,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -823,7 +823,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, } NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, - UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) { + UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) { EXTENT_DATA_REF edr; edr.root = root; @@ -831,20 +831,20 @@ NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UI edr.offset = offset; edr.count = refcount; - return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback); + return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, Irp, rollback); } -NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback) { +NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) { SHARED_DATA_REF sdr; sdr.offset = treeaddr; sdr.count = 1; - return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, rollback); + return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, Irp, rollback); } -NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback) { - return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, rollback); +NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback) { + return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, Irp, rollback); } typedef struct { @@ -918,7 +918,7 @@ static NTSTATUS add_data_extent_ref(LIST_ENTRY* extent_refs, UINT64 tree_id, UIN return STATUS_SUCCESS; } -static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, LIST_ENTRY* rollback) { +static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY *le, *next_le; UINT64 refcount; ULONG inline_len; @@ -1005,7 +1005,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN le = le->Flink; } - if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); ExFreePool(ei); return STATUS_INTERNAL_ERROR; @@ -1017,7 +1017,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN while (le != extent_refs) { extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry); - if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); return STATUS_INTERNAL_ERROR; } @@ -1081,7 +1081,7 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre return STATUS_SUCCESS; } -NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) { +NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp, next_tp; BOOL b; @@ -1092,7 +1092,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1114,7 +1114,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_EXTENT_REF_V0; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1123,7 +1123,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s InitializeListHead(&extent_refs); do { - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { Status = populate_extent_refs_from_tree(Vcb, tp.item->key.offset, address, &extent_refs); @@ -1143,7 +1143,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s } } while (b); - Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, rollback); + Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("construct_extent_item returned %08x\n", Status); free_extent_refs(&extent_refs); @@ -1155,7 +1155,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s return STATUS_SUCCESS; } -UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset) { +UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; @@ -1166,14 +1166,14 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return 0; } if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("could not find address %llx in extent tree\n", address); + TRACE("could not find address %llx in extent tree\n", address); return 0; } @@ -1249,7 +1249,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_EXTENT_DATA_REF; searchkey.offset = get_extent_data_ref_hash2(root, objid, offset); - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return 0; @@ -1272,7 +1272,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_EXTENT_REF_V0; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return 0; @@ -1281,7 +1281,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s do { traverse_ptr next_tp; - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { if (tp.item->size >= sizeof(EXTENT_REF_V0)) { @@ -1335,7 +1335,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s searchkey.obj_type = TYPE_SHARED_DATA_REF; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return 0; @@ -1344,7 +1344,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s do { traverse_ptr next_tp; - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { if (tp.item->size >= sizeof(SHARED_DATA_REF)) { diff --git a/reactos/drivers/filesystems/btrfs/fastio.c b/reactos/drivers/filesystems/btrfs/fastio.c index 682489a3806..2b0608d7a86 100644 --- a/reactos/drivers/filesystems/btrfs/fastio.c +++ b/reactos/drivers/filesystems/btrfs/fastio.c @@ -67,7 +67,7 @@ static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wa fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime); fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime); fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime); - fbi->ChangeTime.QuadPart = 0; + fbi->ChangeTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_ctime); fbi->FileAttributes = fcb->atts; IoStatus->Status = STATUS_SUCCESS; @@ -212,9 +212,22 @@ static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, 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 NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) { + fcb* fcb; + + TRACE("(%p, %llx, %p, %p)\n", FileObject, EndingOffset->QuadPart, ResourceToRelease, DeviceObject); + + fcb = FileObject->FsContext; + + if (!fcb) + return STATUS_INVALID_PARAMETER; + + *ResourceToRelease = fcb->Header.PagingIoResource; + + if (!ExAcquireResourceSharedLite(*ResourceToRelease, FALSE)) + return STATUS_CANT_WAIT; + + return STATUS_SUCCESS; } 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){ diff --git a/reactos/drivers/filesystems/btrfs/fileinfo.c b/reactos/drivers/filesystems/btrfs/fileinfo.c index 4d0e323a830..092ed287098 100644 --- a/reactos/drivers/filesystems/btrfs/fileinfo.c +++ b/reactos/drivers/filesystems/btrfs/fileinfo.c @@ -24,7 +24,7 @@ #endif #endif -static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us); +static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp); static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; @@ -65,7 +65,7 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P LARGE_INTEGER time; BTRFS_TIME now; - defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE); + defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp); if (fcb->type == BTRFS_TYPE_DIRECTORY) fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; @@ -91,10 +91,37 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; } -// FIXME - CreationTime -// FIXME - LastAccessTime -// FIXME - LastWriteTime -// FIXME - ChangeTime + if (fbi->CreationTime.QuadPart == -1) { + FIXME("FIXME - support CreationTime == -1\n"); // FIXME - set ccb flag + } else if (fbi->CreationTime.QuadPart != 0) { + win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime); + inode_item_changed = TRUE; + filter |= FILE_NOTIFY_CHANGE_CREATION; + } + + if (fbi->LastAccessTime.QuadPart == -1) { + FIXME("FIXME - support LastAccessTime == -1\n"); // FIXME - set ccb flag + } else if (fbi->LastAccessTime.QuadPart != 0) { + win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime); + inode_item_changed = TRUE; + filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + } + + if (fbi->LastWriteTime.QuadPart == -1) { + FIXME("FIXME - support LastWriteTime == -1\n"); // FIXME - set ccb flag + } else if (fbi->LastWriteTime.QuadPart != 0) { + win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime); + inode_item_changed = TRUE; + filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + } + + if (fbi->ChangeTime.QuadPart == -1) { + FIXME("FIXME - support ChangeTime == -1\n"); // FIXME - set ccb flag + } else if (fbi->ChangeTime.QuadPart != 0) { + win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime); + inode_item_changed = TRUE; + // no filter for this + } if (inode_item_changed) { fcb->inode_item.transid = Vcb->superblock.generation; @@ -171,7 +198,7 @@ end: return Status; } -static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) { +static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; INODE_EXTREF* ier; @@ -181,7 +208,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino searchkey.obj_type = TYPE_INODE_EXTREF; searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length); - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -214,7 +241,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) { + if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); return STATUS_INTERNAL_ERROR; } @@ -230,7 +257,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino 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, rollback)) { + if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); return STATUS_INTERNAL_ERROR; } @@ -239,7 +266,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino return STATUS_SUCCESS; } -NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) { +NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; INODE_REF* ir; @@ -249,7 +276,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 searchkey.obj_type = TYPE_INODE_REF; searchkey.offset = parinode; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -263,7 +290,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 if (irsize > maxlen) { if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) { TRACE("INODE_REF too long, creating INODE_EXTREF\n"); - return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback); + return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, Irp, rollback); } else { ERR("item would be too long (%u > %u)\n", irsize, maxlen); return STATUS_INTERNAL_ERROR; @@ -286,7 +313,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) { + if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); return STATUS_INTERNAL_ERROR; } @@ -301,7 +328,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 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, rollback)) { + if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); return STATUS_INTERNAL_ERROR; } @@ -332,18 +359,19 @@ BOOL has_open_children(file_ref* fileref) { } static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { + device_extension* Vcb = oldfcb->Vcb; fcb* fcb; LIST_ENTRY* le; // FIXME - we can skip a lot of this if the inode is about to be deleted - fcb = create_fcb(); + fcb = create_fcb(PagedPool); // FIXME - what if we duplicate the paging file? if (!fcb) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } - fcb->Vcb = oldfcb->Vcb; + fcb->Vcb = Vcb; fcb->Header.IsFastIoPossible = fast_io_possible(fcb); fcb->Header.AllocationSize = oldfcb->Header.AllocationSize; @@ -355,6 +383,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (oldfcb->ads) { fcb->ads = TRUE; fcb->adshash = oldfcb->adshash; + fcb->adsmaxlen = oldfcb->adsmaxlen; if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) { fcb->adsxattr.Length = oldfcb->adsxattr.Length; @@ -363,7 +392,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!fcb->adsxattr.Buffer) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -377,7 +410,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!fcb->adsdata.Buffer) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -393,7 +430,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG); if (!fcb->sd) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -411,7 +452,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!ext2) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -423,7 +468,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!ext2->data) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -448,7 +497,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!hl2) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -461,7 +514,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { if (!hl2->name.Buffer) { ERR("out of memory\n"); ExFreePool(hl2); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -474,7 +531,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { ERR("out of memory\n"); ExFreePool(hl2->name.Buffer); ExFreePool(hl2); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } @@ -493,41 +554,16 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG); if (!fcb->reparse_xattr.Buffer) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length); } - - if (oldfcb->ads) { - fcb->ads = TRUE; - fcb->adshash = oldfcb->adshash; - - if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) { - fcb->adsxattr.Length = fcb->adsxattr.MaximumLength = oldfcb->adsxattr.Length; - fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG); - if (!fcb->adsxattr.Buffer) { - ERR("out of memory\n"); - free_fcb(fcb); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length); - } - - if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) { - fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length; - fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG); - if (!fcb->adsdata.Buffer) { - ERR("out of memory\n"); - free_fcb(fcb); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length); - } - } end: *pfcb = fcb; @@ -543,7 +579,7 @@ typedef struct _move_entry { LIST_ENTRY list_entry; } move_entry; -static NTSTATUS add_children_to_move_list(move_entry* me) { +static NTSTATUS add_children_to_move_list(move_entry* me, PIRP Irp) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; @@ -586,7 +622,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = 0; - Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -652,8 +688,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { RtlCopyMemory(xattr.Buffer, xa->name, xa->n); xattr.Buffer[xa->n] = 0; + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); Status = open_fcb_stream(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, &xattr, - tp.item->key.offset, me->fileref->fcb, &fcb); + tp.item->key.offset, me->fileref->fcb, &fcb, Irp); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + if (!NT_SUCCESS(Status)) { ERR("open_fcb_stream returned %08x\n", Status); ExFreePool(xattr.Buffer); @@ -663,7 +702,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { fr = create_fileref(); if (!fr) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } @@ -673,7 +716,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen); if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -681,14 +728,22 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!fr->filepart.Buffer) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, &xa->name[xapreflen], xa->n - xapreflen); if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -697,7 +752,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -710,7 +769,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!me) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -730,7 +793,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { } while (len > 0); } - b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -744,7 +807,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { searchkey.obj_type = TYPE_DIR_INDEX; searchkey.offset = 2; - Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -842,7 +905,9 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { inode = di->key.obj_id; } - Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb); + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); + Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb, Irp); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); @@ -855,7 +920,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; ExFreePool(utf8.Buffer); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fcb(fcb); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -865,7 +934,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length); if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -873,7 +946,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!fr->filepart.Buffer) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -881,7 +958,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -891,7 +972,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -909,7 +994,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { if (!me) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); + goto end; } @@ -927,7 +1016,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) { } } - b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -945,7 +1034,7 @@ end: return Status; } -static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, LIST_ENTRY* rollback) { +static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; LIST_ENTRY move_list, *le; move_entry* me; @@ -980,7 +1069,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE); if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) { - Status = add_children_to_move_list(me); + Status = add_children_to_move_list(me, Irp); if (!NT_SUCCESS(Status)) { ERR("add_children_to_move_list returned %08x\n", Status); @@ -1031,14 +1120,14 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ LIST_ENTRY* le2; if (destdir->fcb->subvol->lastinode == 0) - get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol); + get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol, Irp); me->fileref->fcb->subvol = destdir->fcb->subvol; me->fileref->fcb->inode = ++destdir->fcb->subvol->lastinode; // FIXME - do proper function for this me->fileref->fcb->inode_item.st_nlink = 1; defda = get_file_attributes(me->fileref->fcb->Vcb, &me->fileref->fcb->inode_item, me->fileref->fcb->subvol, me->fileref->fcb->inode, - me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE); + me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE, Irp); me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd; me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts; @@ -1060,7 +1149,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ ERR("get_chunk_from_address(%llx) failed\n", ed2->address); } else { Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode, - ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); + ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); if (!NT_SUCCESS(Status)) { ERR("update_changed_extent_ref returned %08x\n", Status); @@ -1221,13 +1310,15 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ if (!me->parent) { RemoveEntryList(&me->fileref->list_entry); + ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE); free_fileref(me->fileref->parent); + ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock); me->fileref->parent = destdir; increase_fileref_refcount(destdir); - Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index); + Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); goto end; @@ -1249,7 +1340,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ me->fileref->fcb->subvol->root_item.num_references++; if (!me->dummyfileref->fcb->ads) { - Status = delete_fileref(me->dummyfileref, NULL, rollback); + Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); goto end; @@ -1303,7 +1394,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ me = CONTAINING_RECORD(le, move_entry, list_entry); if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) { - Status = delete_fileref(me->dummyfileref, NULL, rollback); + Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); goto end; @@ -1326,23 +1417,36 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_ end: while (!IsListEmpty(&move_list)) { + device_extension* Vcb; + le = RemoveHeadList(&move_list); me = CONTAINING_RECORD(le, move_entry, list_entry); + Vcb = me->fileref->fcb->Vcb; - if (me->dummyfcb) + if (me->dummyfcb) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(me->dummyfcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + } - if (me->dummyfileref) + if (me->dummyfileref) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(me->dummyfileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + } + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(me->fileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + ExFreePool(me); } return Status; } -static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) { +static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer; fcb *fcb = FileObject->FsContext; ccb* ccb = FileObject->FsContext2; @@ -1360,11 +1464,8 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, InitializeListHead(&rollback); - // FIXME - check fri length - // FIXME - don't ignore fri->RootDirectory - TRACE("tfo = %p\n", tfo); - TRACE("ReplaceIfExists = %u\n", ReplaceIfExists); + TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists); TRACE("RootDirectory = %p\n", fri->RootDirectory); TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName); @@ -1379,6 +1480,12 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, } else { LONG i; + while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\')) + fnlen--; + + if (fnlen == 0) + return STATUS_INVALID_PARAMETER; + for (i = fnlen - 1; i >= 0; i--) { if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') { fn = &fri->FileName[i+1]; @@ -1425,13 +1532,15 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, increase_fileref_refcount(related); } - Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp); + ExReleaseResourceLite(&Vcb->fcb_lock); if (NT_SUCCESS(Status)) { TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref)); if (fileref != oldfileref && !oldfileref->deleted) { - if (!ReplaceIfExists) { + if (!IrpSp->Parameters.SetFile.ReplaceIfExists) { Status = STATUS_OBJECT_NAME_COLLISION; goto end; } else if ((oldfileref->fcb->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) { @@ -1447,14 +1556,18 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, } } - if (fileref == oldfileref || !oldfileref->deleted) { + if (fileref == oldfileref || oldfileref->deleted) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(oldfileref); + ExReleaseResourceLite(&Vcb->fcb_lock); oldfileref = NULL; } } if (!related) { - Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp); + ExReleaseResourceLite(&Vcb->fcb_lock); if (!NT_SUCCESS(Status)) { ERR("open_fileref returned %08x\n", Status); @@ -1469,8 +1582,21 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, } if (oldfileref) { - // FIXME - check we have permissions for this - Status = delete_fileref(oldfileref, NULL, &rollback); + ACCESS_MASK access; + SECURITY_SUBJECT_CONTEXT subjcont; + + SeCaptureSubjectContext(&subjcont); + + if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL, + IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { + SeReleaseSubjectContext(&subjcont); + WARN("SeAccessCheck failed, returning %08x\n", Status); + goto end; + } + + SeReleaseSubjectContext(&subjcont); + + Status = delete_fileref(oldfileref, NULL, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); goto end; @@ -1478,7 +1604,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, } if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) { - Status = move_across_subvols(fileref, related, &utf8, &fnus, &rollback); + Status = move_across_subvols(fileref, related, &utf8, &fnus, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("move_across_subvols returned %08x\n", Status); } @@ -1593,7 +1719,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY) fr2->fcb->fileref = fr2; - Status = fcb_get_last_dir_index(related->fcb, &index); + Status = fcb_get_last_dir_index(related->fcb, &index, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); goto end; @@ -1726,7 +1852,9 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, fr2->parent->fcb->inode_item.st_ctime = now; fr2->parent->fcb->inode_item.st_mtime = now; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr2); + ExReleaseResourceLite(&Vcb->fcb_lock); mark_fcb_dirty(fr2->parent->fcb); @@ -1738,14 +1866,23 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, Status = STATUS_SUCCESS; end: - if (oldfileref) + if (oldfileref) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(oldfileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + } - if (!NT_SUCCESS(Status) && related) + if (!NT_SUCCESS(Status) && related) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(related); + ExReleaseResourceLite(&Vcb->fcb_lock); + } - if (!NT_SUCCESS(Status) && fr2) + if (!NT_SUCCESS(Status) && fr2) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr2); + ExReleaseResourceLite(&Vcb->fcb_lock); + } if (NT_SUCCESS(Status)) clear_rollback(&rollback); @@ -1778,39 +1915,12 @@ NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT6 fcb->adsdata.Length = end; } else if (end > fcb->adsdata.Length) { -// UINT16 maxlen; - TRACE("extending stream to %llx bytes\n", end); -// -// // 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; -// -// Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// ERR("error - could not find key for xattr\n"); -// return STATUS_INTERNAL_ERROR; -// } -// -// if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) { -// ERR("get_xattr failed\n"); -// return STATUS_INTERNAL_ERROR; -// } -// -// maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead -// -// if (end > maxlen) { -// ERR("error - xattr too long (%llu > %u)\n", end, maxlen); -// return STATUS_DISK_FULL; -// } + + if (end > fcb->adsmaxlen) { + ERR("error - xattr too long (%llu > %u)\n", end, fcb->adsmaxlen); + return STATUS_DISK_FULL; + } if (end > fcb->adsdata.MaximumLength) { char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG); @@ -1913,7 +2023,7 @@ static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart); - Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, &rollback); + Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("error - truncate_file failed\n"); goto end; @@ -2031,6 +2141,12 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF tfofcb = tfo->FsContext; parfcb = tfofcb; + while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\')) + fnlen--; + + if (fnlen == 0) + return STATUS_INVALID_PARAMETER; + for (i = fnlen - 1; i >= 0; i--) { if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') { fn = &fli->FileName[i+1]; @@ -2083,7 +2199,9 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF increase_fileref_refcount(related); } - Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp); + ExReleaseResourceLite(&Vcb->fcb_lock); if (NT_SUCCESS(Status)) { if (!oldfileref->deleted) { @@ -2107,13 +2225,17 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF goto end; } } else { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(oldfileref); + ExReleaseResourceLite(&Vcb->fcb_lock); oldfileref = NULL; } } if (!related) { - Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp); + ExReleaseResourceLite(&Vcb->fcb_lock); if (!NT_SUCCESS(Status)) { ERR("open_fileref returned %08x\n", Status); @@ -2150,14 +2272,14 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF SeReleaseSubjectContext(&subjcont); - Status = delete_fileref(oldfileref, NULL, &rollback); + Status = delete_fileref(oldfileref, NULL, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); goto end; } } - Status = fcb_get_last_dir_index(related->fcb, &index); + Status = fcb_get_last_dir_index(related->fcb, &index, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); goto end; @@ -2259,14 +2381,23 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF Status = STATUS_SUCCESS; end: - if (oldfileref) + if (oldfileref) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(oldfileref); + ExReleaseResourceLite(&Vcb->fcb_lock); + } - if (!NT_SUCCESS(Status) && related) + if (!NT_SUCCESS(Status) && related) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(related); + ExReleaseResourceLite(&Vcb->fcb_lock); + } - if (!NT_SUCCESS(Status) && fr2) + if (!NT_SUCCESS(Status) && fr2) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr2); + ExReleaseResourceLite(&Vcb->fcb_lock); + } if (NT_SUCCESS(Status)) clear_rollback(&rollback); @@ -2296,7 +2427,12 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp goto exit; } - if (Vcb->readonly) { + if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) { + Status = STATUS_ACCESS_DENIED; + goto end; + } + + if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) { Status = STATUS_MEDIA_WRITE_PROTECTED; goto end; } @@ -2313,7 +2449,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp goto end; } - if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) { + if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) { Status = STATUS_ACCESS_DENIED; goto end; } @@ -2408,7 +2544,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp 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); + Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); break; case FileValidDataLengthInformation: @@ -2457,7 +2593,7 @@ static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* f 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->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); if (fcb->ads) { if (!fileref || !fileref->parent) { @@ -2496,7 +2632,7 @@ static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_ 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; + fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); if (fcb->ads) { fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; @@ -2776,7 +2912,7 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni return STATUS_SUCCESS; } -static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, LONG* length) { +static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, PIRP Irp, LONG* length) { *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); if (fcb->ads) { @@ -2789,7 +2925,10 @@ static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_IN } else ati->FileAttributes = fcb->atts; - ati->ReparseTag = 0; // FIXME + if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + ati->ReparseTag = 0; + else + ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, Irp); return STATUS_SUCCESS; } @@ -2801,7 +2940,7 @@ typedef struct { LIST_ENTRY list_entry; } stream_info; -static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) { +static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, PIRP Irp, LONG* length) { ULONG reqsize; LIST_ENTRY streamlist, *le; FILE_STREAM_INFORMATION *entry, *lastentry; @@ -2832,7 +2971,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = 0; - Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -2916,7 +3055,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* } } - b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -3086,7 +3225,7 @@ typedef struct { LIST_ENTRY list_entry; } name_bit; -static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) { +static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol, PIRP Irp) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -3109,7 +3248,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) { searchkey.obj_type = TYPE_ROOT_BACKREF; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3156,7 +3295,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) { // FIXME - recursion - Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath); + Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath, Irp); if (!NT_SUCCESS(Status)) { ERR("get_inode_dir_path returned %08x\n", Status); return Status; @@ -3206,7 +3345,7 @@ end: return Status; } -static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us) { +static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp) { KEY searchkey; NTSTATUS Status; UINT64 in; @@ -3222,7 +3361,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i // FIXME - start with subvol prefix if (!subvol->path.Buffer) { - Status = get_subvol_path(Vcb, subvol); + Status = get_subvol_path(Vcb, subvol, Irp); if (!NT_SUCCESS(Status)) { ERR("get_subvol_path returned %08x\n", Status); return Status; @@ -3234,7 +3373,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i searchkey.obj_type = TYPE_INODE_EXTREF; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -3359,13 +3498,13 @@ end: return Status; } -NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr) { +NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) { NTSTATUS Status; fcb* fcb; hardlink* hl; file_ref *parfr, *fr; - Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb); + Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); return Status; @@ -3390,7 +3529,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode if (hl->parent == inode) // root of subvol parfr = NULL; else { - Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr); + Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08x\n", Status); free_fcb(fcb); @@ -3453,7 +3592,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode } #ifndef __REACTOS__ -static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, LONG* length) { +static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) { NTSTATUS Status; LIST_ENTRY* le; ULONG bytes_needed; @@ -3517,7 +3656,7 @@ static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fl TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer); - Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr); + Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fileref_by_inode returned %08x\n", Status); @@ -3707,7 +3846,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec goto exit; } - Status = fill_in_file_attribute_information(ati, fcb, fileref, &length); + Status = fill_in_file_attribute_information(ati, fcb, fileref, Irp, &length); break; } @@ -3841,7 +3980,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileStreamInformation\n"); - Status = fill_in_file_stream_information(fsi, fileref, &length); + Status = fill_in_file_stream_information(fsi, fileref, Irp, &length); break; } @@ -3853,7 +3992,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileHardLinkInformation\n"); - Status = fill_in_hard_link_information(fli, fileref, &length); + Status = fill_in_hard_link_information(fli, fileref, Irp, &length); break; } diff --git a/reactos/drivers/filesystems/btrfs/flushthread.c b/reactos/drivers/filesystems/btrfs/flushthread.c index 16795eb1a84..48eabad0ce3 100644 --- a/reactos/drivers/filesystems/btrfs/flushthread.c +++ b/reactos/drivers/filesystems/btrfs/flushthread.c @@ -17,8 +17,6 @@ #include "btrfs_drv.h" -#define INTERVAL 15000 // in milliseconds - static void do_flush(device_extension* Vcb) { LIST_ENTRY rollback; @@ -28,8 +26,8 @@ static void do_flush(device_extension* Vcb) { ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->need_write) - do_write(Vcb, &rollback); + if (Vcb->need_write && !Vcb->readonly) + do_write(Vcb, NULL, &rollback); free_trees(Vcb); @@ -49,7 +47,7 @@ void STDCALL flush_thread(void* context) { KeInitializeTimer(&Vcb->flush_thread_timer); - due_time.QuadPart = -INTERVAL * 10000; + due_time.QuadPart = (UINT64)Vcb->options.flush_interval * -10000000; KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL); diff --git a/reactos/drivers/filesystems/btrfs/free-space.c b/reactos/drivers/filesystems/btrfs/free-space.c index 5f583c194a5..6982ae0919e 100644 --- a/reactos/drivers/filesystems/btrfs/free-space.c +++ b/reactos/drivers/filesystems/btrfs/free-space.c @@ -23,11 +23,11 @@ // #define DEBUG_SPACE_LISTS -static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* rollback) { +static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; fcb* fcb; - Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb); + Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); return Status; @@ -36,7 +36,7 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS fcb->dirty = TRUE; if (fcb->inode_item.st_size > 0) { - Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback); + Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("excise_extents returned %08x\n", Status); return Status; @@ -45,14 +45,14 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS fcb->deleted = TRUE; - flush_fcb(fcb, FALSE, rollback); + flush_fcb(fcb, FALSE, Irp, rollback); free_fcb(fcb); return STATUS_SUCCESS; } -NTSTATUS clear_free_space_cache(device_extension* Vcb) { +NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp) { KEY searchkey; traverse_ptr tp, next_tp; NTSTATUS Status; @@ -65,7 +65,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) { searchkey.obj_type = 0; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -86,7 +86,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) { else { LIST_ENTRY* le; - Status = remove_free_space_inode(Vcb, fsi->key.obj_id, &rollback); + Status = remove_free_space_inode(Vcb, fsi->key.obj_id, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("remove_free_space_inode for (%llx,%x,%llx) returned %08x\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset, Status); @@ -109,7 +109,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) { WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM)); } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) tp = next_tp; } while (b); @@ -245,7 +245,7 @@ static void order_space_entry(space* s, LIST_ENTRY* list_size) { InsertTailList(list_size, &s->list_entry_size); } -static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { +static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) { KEY searchkey; traverse_ptr tp; FREE_SPACE_ITEM* fsi; @@ -266,7 +266,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -293,10 +293,10 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { num_entries = fsi->num_entries; num_bitmaps = fsi->num_bitmaps; - Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache); + Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); - return Status; + return STATUS_NOT_FOUND; } if (c->cache->inode_item.st_size == 0) { @@ -329,7 +329,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { free_fcb(c->cache); c->cache = NULL; - return Status; + return STATUS_NOT_FOUND; } if (size > c->cache->inode_item.st_size) @@ -446,7 +446,7 @@ clearcache: InitializeListHead(&rollback); - Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, &rollback); + Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("excise_extents returned %08x\n", Status); do_rollback(Vcb, &rollback); @@ -463,7 +463,7 @@ clearcache: return STATUS_NOT_FOUND; } -NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { +NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) { traverse_ptr tp, next_tp; KEY searchkey; UINT64 lastaddr; @@ -473,7 +473,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { // LIST_ENTRY* le; if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) { - Status = load_stored_free_space_cache(Vcb, c); + Status = load_stored_free_space_cache(Vcb, c, Irp); if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) { ERR("load_stored_free_space_cache returned %08x\n", Status); @@ -489,7 +489,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -525,7 +525,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { lastaddr = tp.item->key.obj_id + tp.item->key.offset; } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) tp = next_tp; } while (b); @@ -568,48 +568,52 @@ static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ flags = fcb->Vcb->data_flags; - ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); while (le != &fcb->Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) { + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&fcb->Vcb->chunk_lock); return STATUS_SUCCESS; } } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le->Flink; } - if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + if ((c = alloc_chunk(fcb->Vcb, flags))) { + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); - ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) { + ExReleaseResourceLite(&c->lock); return STATUS_SUCCESS; } } - ExReleaseResourceLite(&c->nonpaged->lock); - } - - ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + ExReleaseResourceLite(&c->lock); + } else + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); WARN("couldn't find any data chunks with %llx bytes free\n", length); return STATUS_DISK_FULL; } -static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, LIST_ENTRY* rollback) { +static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le; NTSTATUS Status; UINT64 num_entries, new_cache_size, i; @@ -669,7 +673,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan // create new inode - c->cache = create_fcb(); + c->cache = create_fcb(PagedPool); if (!c->cache) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; @@ -691,7 +695,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan c->cache->subvol = Vcb->root_root; if (Vcb->root_root->lastinode == 0) - get_last_inode(Vcb, Vcb->root_root); + get_last_inode(Vcb, Vcb->root_root, Irp); c->cache->inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101; @@ -712,9 +716,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); + ExFreePool(fsi); + free_fcb(c->cache); + c->cache = NULL; return Status; } @@ -725,7 +732,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan fsi->key.obj_type = TYPE_INODE_ITEM; fsi->key.offset = 0; - if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); free_fcb(c->cache); c->cache = NULL; @@ -746,7 +753,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan Vcb->root_root->lastinode = c->cache->inode; - flush_fcb(c->cache, TRUE, rollback); + flush_fcb(c->cache, TRUE, Irp, rollback); *changed = TRUE; } else if (new_cache_size > c->cache->inode_item.st_size) { @@ -764,7 +771,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -795,7 +802,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan c->cache->inode_item.st_size = new_cache_size; c->cache->inode_item.st_blocks = new_cache_size; - flush_fcb(c->cache, TRUE, rollback); + flush_fcb(c->cache, TRUE, Irp, rollback); *changed = TRUE; } else { @@ -808,7 +815,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -820,10 +827,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); RtlCopyMemory(ii, &c->cache->inode_item, sizeof(INODE_ITEM)); - if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } + + *changed = TRUE; } else { if (tp.item->size < sizeof(INODE_ITEM)) { ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM)); @@ -837,7 +846,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -861,7 +870,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan return STATUS_SUCCESS; } -NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) { +NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le = Vcb->chunks_changed.Flink; NTSTATUS Status; @@ -871,9 +880,9 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba BOOL b; chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); - Status = allocate_cache_chunk(Vcb, c, &b, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + Status = allocate_cache_chunk(Vcb, c, &b, Irp, rollback); + ExReleaseResourceLite(&c->lock); if (b) *changed = TRUE; @@ -889,7 +898,25 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba return STATUS_SUCCESS; } -void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { +static void add_rollback_space(LIST_ENTRY* rollback, BOOL add, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c) { + rollback_space* rs; + + rs = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_space), ALLOC_TAG); + if (!rs) { + ERR("out of memory\n"); + return; + } + + rs->list = list; + rs->list_size = list_size; + rs->address = address; + rs->length = length; + rs->chunk = c; + + add_rollback(rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs); +} + +void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) { LIST_ENTRY* le; space *s, *s2; @@ -912,7 +939,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U if (list_size) InsertTailList(list_size, &s->list_entry_size); - // FIXME - insert rollback entry + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, length, c); return; } @@ -928,9 +956,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U // new entry envelops old one completely if (address <= s2->address && address + length >= s2->address + s2->size) { if (address < s2->address) { + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c); + s2->size += s2->address - address; s2->address = address; - // FIXME - insert rollback while (s2->list_entry.Blink != list) { space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry); @@ -951,8 +981,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U } if (length > s2->size) { + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c); + s2->size = length; - // FIXME - insert rollback while (s2->list_entry.Flink != list) { space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry); @@ -981,9 +1013,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U // new entry overlaps start of old one if (address < s2->address && address + length >= s2->address) { + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c); + s2->size += s2->address - address; s2->address = address; - // FIXME - insert rollback while (s2->list_entry.Blink != list) { space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry); @@ -1012,8 +1046,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U // new entry overlaps end of old one if (address <= s2->address + s2->size && address + length > s2->address + s2->size) { + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c); + s2->size = address + length - s2->address; - // FIXME - insert rollback while (s2->list_entry.Flink != list) { space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry); @@ -1048,7 +1084,9 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U return; } - // FIXME - insert rollback + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, length, c); + s->address = address; s->size = length; InsertHeadList(s2->list_entry.Blink, &s->list_entry); @@ -1065,7 +1103,6 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U // check if contiguous with last entry if (s2->address + s2->size == address) { s2->size += length; - // FIXME - insert rollback if (list_size) { RemoveEntryList(&s2->list_entry_size); @@ -1090,7 +1127,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U if (list_size) order_space_entry(s, list_size); - // FIXME - insert rollback + if (rollback) + add_rollback_space(rollback, TRUE, list, list_size, address, length, c); } static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) { @@ -1108,7 +1146,7 @@ static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, } } -static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) { +static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; @@ -1159,7 +1197,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* c->cache->inode_item.sequence++; c->cache->inode_item.st_ctime = *now; - flush_fcb(c->cache, TRUE, rollback); + flush_fcb(c->cache, TRUE, Irp, rollback); // update free_space item @@ -1167,7 +1205,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1211,9 +1249,9 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* // write cache - Status = do_nocow_write(Vcb, c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback); + Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback); if (!NT_SUCCESS(Status)) { - ERR("do_nocow_write returned %08x\n", Status); + ERR("do_write_file returned %08x\n", Status); return Status; } @@ -1222,7 +1260,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* return STATUS_SUCCESS; } -NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) { +NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le = Vcb->chunks_changed.Flink; NTSTATUS Status; chunk* c; @@ -1235,9 +1273,9 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) { while (le != &Vcb->chunks_changed) { c = CONTAINING_RECORD(le, chunk, list_entry_changed); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); - Status = update_chunk_cache(Vcb, c, &now, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); + Status = update_chunk_cache(Vcb, c, &now, Irp, rollback); + ExReleaseResourceLite(&c->lock); if (!NT_SUCCESS(Status)) { ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status); @@ -1260,10 +1298,10 @@ void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 addr if (!c->list_entry_changed.Flink) InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed); - _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, rollback, func); + _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func); } -void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { +void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) { LIST_ENTRY *le, *le2; space *s, *s2; @@ -1283,7 +1321,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre return; if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely - // FIXME - insert rollback + if (rollback) + add_rollback_space(rollback, FALSE, list, list_size, s2->address, s2->size, c); + RemoveEntryList(&s2->list_entry); if (list_size) @@ -1292,7 +1332,8 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre ExFreePool(s2); } else if (address + length > s2->address && address + length < s2->address + s2->size) { if (address > s2->address) { // cut out hole - // FIXME - insert rollback + if (rollback) + add_rollback_space(rollback, FALSE, list, list_size, address, length, c); s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); @@ -1316,9 +1357,11 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre return; } else { // remove start of entry + if (rollback) + add_rollback_space(rollback, FALSE, list, list_size, s2->address, address + length - s2->address, c); + s2->size -= address + length - s2->address; s2->address = address + length; - // FIXME - insert rollback if (list_size) { RemoveEntryList(&s2->list_entry_size); @@ -1326,7 +1369,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre } } } else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry - // FIXME - insert rollback + if (rollback) + add_rollback_space(rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c); + s2->size = address - s2->address; if (list_size) { @@ -1347,5 +1392,5 @@ void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 if (!c->list_entry_changed.Flink) InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed); - _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, rollback, func); + _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func); } diff --git a/reactos/drivers/filesystems/btrfs/fsctl.c b/reactos/drivers/filesystems/btrfs/fsctl.c index 27c1644235e..aa42414d17d 100644 --- a/reactos/drivers/filesystems/btrfs/fsctl.c +++ b/reactos/drivers/filesystems/btrfs/fsctl.c @@ -27,6 +27,8 @@ #define DOTDOT ".." +#define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?) + extern LIST_ENTRY VcbList; extern ERESOURCE global_loading_lock; @@ -68,7 +70,7 @@ static void get_uuid(BTRFS_UUID* uuid) { } } -static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) { +static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) { UINT8* buf; NTSTATUS Status; write_data_context* wtc; @@ -90,7 +92,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL); + Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, Irp); if (!NT_SUCCESS(Status)) { ERR("read_data returned %08x\n", Status); goto end; @@ -104,7 +106,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub t.header.level = th->level; t.header.tree_id = t.root->id; - Status = get_tree_new_address(Vcb, &t, rollback); + Status = get_tree_new_address(Vcb, &t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("get_tree_new_address returned %08x\n", Status); goto end; @@ -143,7 +145,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0]; if (ed2->size != 0) { // not sparse - Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback); + Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("increase_extent_refcount_data returned %08x\n", Status); @@ -159,7 +161,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub internal_node* in = (internal_node*)&th[1]; for (i = 0; i < th->num_items; i++) { - Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, rollback); + Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("snapshot_tree_copy returned %08x\n", Status); @@ -178,7 +180,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub wtc->tree = TRUE; wtc->stripes_left = 0; - Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL); + Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL); if (!NT_SUCCESS(Status)) { ERR("write_data returned %08x\n", Status); goto end; @@ -243,7 +245,7 @@ static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) { } } -static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8, PUNICODE_STRING name) { +static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, PIRP Irp) { LIST_ENTRY rollback; UINT64 id; NTSTATUS Status; @@ -272,8 +274,6 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f InitializeListHead(&rollback); - ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - // flush open files on this subvol flush_subvol_fcbs(subvol, &rollback); @@ -281,7 +281,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f // flush metadata if (Vcb->need_write) - do_write(Vcb, &rollback); + do_write(Vcb, Irp, &rollback); free_trees(Vcb); @@ -292,10 +292,10 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f // create new root if (Vcb->root_root->lastinode == 0) - get_last_inode(Vcb, Vcb->root_root); + get_last_inode(Vcb, Vcb->root_root, Irp); id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101; - Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback); + Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("create_root returned %08x\n", Status); @@ -307,7 +307,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f TRACE("uuid root doesn't exist, creating it\n"); - Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback); + Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("create_root returned %08x\n", Status); @@ -333,12 +333,12 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f searchkey.obj_type = TYPE_SUBVOL_UUID; RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64)); - Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp); } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key)); *root_num = r->id; - if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) { + if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(root_num); Status = STATUS_INTERNAL_ERROR; @@ -349,13 +349,13 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f searchkey.obj_type = TYPE_ROOT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; } - Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback); + Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("snapshot_tree_copy returned %08x\n", Status); goto end; @@ -405,7 +405,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f searchkey.obj_type = 0; searchkey.offset = 0; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -433,14 +433,14 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length); - Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb); + Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, Irp); if (!NT_SUCCESS(Status)) { ERR("open_fcb returned %08x\n", Status); free_fileref(fr); goto end; } - Status = fcb_get_last_dir_index(fcb, &dirpos); + Status = fcb_get_last_dir_index(fcb, &dirpos, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); free_fileref(fr); @@ -514,7 +514,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f le = le->Flink; } - do_write(Vcb, &rollback); + do_write(Vcb, Irp, &rollback); free_trees(Vcb); @@ -526,12 +526,10 @@ end: else do_rollback(Vcb, &rollback); - ExReleaseResourceLite(&Vcb->tree_lock); - return Status; } -static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) { +static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { PFILE_OBJECT subvol_obj; NTSTATUS Status; btrfs_create_snapshot* bcs = data; @@ -539,10 +537,9 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, ANSI_STRING utf8; UNICODE_STRING nameus; ULONG len; - UINT32 crc32; fcb* fcb; ccb* ccb; - file_ref* fileref; + file_ref *fileref, *fr2; if (length < offsetof(btrfs_create_snapshot, name)) return STATUS_INVALID_PARAMETER; @@ -606,24 +603,29 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status); goto end2; } + + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); + + // no need for fcb_lock as we have tree_lock exclusively + Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp); - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length); - - Status = find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL); - if (NT_SUCCESS(Status)) { - WARN("file already exists\n"); - Status = STATUS_OBJECT_NAME_COLLISION; - goto end2; + if (!fr2->deleted) { + WARN("file already exists\n"); + free_fileref(fr2); + Status = STATUS_OBJECT_NAME_COLLISION; + goto end3; + } else + free_fileref(fr2); } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { - ERR("find_file_in_dir_with_crc32 returned %08x\n", Status); - goto end2; + ERR("open_fileref returned %08x\n", Status); + goto end3; } Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL); if (!NT_SUCCESS(Status)) { ERR("ObReferenceObjectByHandle returned %08x\n", Status); - goto end2; + goto end3; } subvol_fcb = subvol_obj->FsContext; @@ -651,12 +653,13 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, goto end; } - Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8, &nameus); + Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, Irp); if (NT_SUCCESS(Status)) { file_ref* fr; - Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL); + Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL, NULL, Irp); + if (!NT_SUCCESS(Status)) { ERR("open_fileref returned %08x\n", Status); Status = STATUS_SUCCESS; @@ -669,13 +672,16 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, end: ObDereferenceObject(subvol_obj); +end3: + ExReleaseResourceLite(&Vcb->tree_lock); + end2: ExFreePool(utf8.Buffer); return Status; } -static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) { +static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length, PIRP Irp) { fcb *fcb, *rootfcb; ccb* ccb; file_ref* fileref; @@ -689,7 +695,6 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC UNICODE_STRING nameus; ANSI_STRING utf8; UINT64 dirpos; - UINT32 crc32; INODE_REF* ir; KEY searchkey; traverse_ptr tp; @@ -697,7 +702,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC PSID owner; BOOLEAN defaulted; UINT64* root_num; - file_ref* fr; + file_ref *fr = NULL, *fr2; fcb = FileObject->FsContext; if (!fcb) { @@ -773,26 +778,29 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC InitializeListHead(&rollback); - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length); - - Status = find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL); + // no need for fcb_lock as we have tree_lock exclusively + Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp); if (NT_SUCCESS(Status)) { - WARN("file already exists\n"); - Status = STATUS_OBJECT_NAME_COLLISION; - goto end; + if (!fr2->deleted) { + WARN("file already exists\n"); + free_fileref(fr2); + Status = STATUS_OBJECT_NAME_COLLISION; + goto end; + } else + free_fileref(fr2); } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { - ERR("find_file_in_dir_with_crc32 returned %08x\n", Status); + ERR("open_fileref returned %08x\n", Status); goto end; } if (Vcb->root_root->lastinode == 0) - get_last_inode(Vcb, Vcb->root_root); + get_last_inode(Vcb, Vcb->root_root, Irp); // FIXME - make sure rollback removes new roots from internal structures id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101; - Status = create_root(Vcb, id, &r, FALSE, 0, &rollback); + Status = create_root(Vcb, id, &r, FALSE, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("create_root returned %08x\n", Status); @@ -806,7 +814,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC TRACE("uuid root doesn't exist, creating it\n"); - Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback); + Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("create_root returned %08x\n", Status); @@ -832,12 +840,12 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC searchkey.obj_type = TYPE_SUBVOL_UUID; RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64)); - Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp); } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key)); *root_num = r->id; - if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) { + if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) { ERR("insert_tree_item failed\n"); Status = STATUS_INTERNAL_ERROR; goto end; @@ -859,7 +867,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC // add .. inode to new subvol - rootfcb = create_fcb(); + rootfcb = create_fcb(PagedPool); if (!rootfcb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -879,7 +887,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now; rootfcb->inode_item.st_gid = GID_NOBODY; // FIXME? - rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE); + rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE, Irp); SeCaptureSubjectContext(&subjcont); @@ -901,13 +909,15 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); rootfcb->inode_item.st_uid = UID_NOBODY; } else { - rootfcb->inode_item.st_uid = sid_to_uid(&owner); + rootfcb->inode_item.st_uid = sid_to_uid(owner); } rootfcb->sd_dirty = TRUE; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); InsertTailList(&r->fcbs, &rootfcb->list_entry); InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all); + ExReleaseResourceLite(&Vcb->fcb_lock); rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb); rootfcb->Header.AllocationSize.QuadPart = 0; @@ -930,7 +940,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC ir->n = strlen(DOTDOT); RtlCopyMemory(ir->name, DOTDOT, ir->n); - if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, &rollback)) { + if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp, &rollback)) { ERR("insert_tree_item failed\n"); Status = STATUS_INTERNAL_ERROR; goto end; @@ -941,7 +951,11 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC fr = create_fileref(); if (!fr) { ERR("out of memory\n"); + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fcb(rootfcb); + ExReleaseResourceLite(&Vcb->fcb_lock); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } @@ -950,10 +964,12 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC mark_fcb_dirty(rootfcb); - Status = fcb_get_last_dir_index(fcb, &dirpos); + Status = fcb_get_last_dir_index(fcb, &dirpos, Irp); if (!NT_SUCCESS(Status)) { ERR("fcb_get_last_dir_index returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&Vcb->fcb_lock); goto end; } @@ -964,7 +980,9 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG); if (!fr->filepart.Buffer) { ERR("out of memory\n"); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&Vcb->fcb_lock); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } @@ -974,7 +992,9 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&Vcb->fcb_lock); goto end; } @@ -1022,8 +1042,204 @@ end: } end2: - if (fr) + if (fr) { + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); free_fileref(fr); + ExReleaseResourceLite(&Vcb->fcb_lock); + } + + return Status; +} + +static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) { + btrfs_inode_info* bii = data; + fcb* fcb; + ccb* ccb; + + if (length < sizeof(btrfs_inode_info)) + return STATUS_BUFFER_OVERFLOW; + + if (!FileObject) + return STATUS_INVALID_PARAMETER; + + fcb = FileObject->FsContext; + + if (!fcb) + return STATUS_INVALID_PARAMETER; + + ccb = FileObject->FsContext2; + + if (!ccb) + return STATUS_INVALID_PARAMETER; + + if (!(ccb->access & FILE_READ_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); + + bii->subvol = fcb->subvol->id; + bii->inode = fcb->inode; + bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE; + bii->type = fcb->type; + bii->st_uid = fcb->inode_item.st_uid; + bii->st_gid = fcb->inode_item.st_gid; + bii->st_mode = fcb->inode_item.st_mode; + bii->st_rdev = fcb->inode_item.st_rdev; + bii->flags = fcb->inode_item.flags; + + bii->inline_length = 0; + bii->disk_size[0] = 0; + bii->disk_size[1] = 0; + bii->disk_size[2] = 0; + + if (fcb->type != BTRFS_TYPE_DIRECTORY) { + LIST_ENTRY* le; + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext->ignore) { + if (ext->data->type == EXTENT_TYPE_INLINE) { + bii->inline_length += ext->data->decoded_size; + } else { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data; + + // FIXME - compressed extents with a hole in them are counted more than once + if (ed2->size != 0) { + if (ext->data->compression == BTRFS_COMPRESSION_NONE) { + bii->disk_size[0] += ed2->num_bytes; + } else if (ext->data->compression == BTRFS_COMPRESSION_ZLIB) { + bii->disk_size[1] += ed2->size; + } else if (ext->data->compression == BTRFS_COMPRESSION_LZO) { + bii->disk_size[2] += ed2->size; + } + } + } + } + + le = le->Flink; + } + } + + ExReleaseResourceLite(fcb->Header.Resource); + + return STATUS_SUCCESS; +} + +static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) { + btrfs_set_inode_info* bsii = data; + NTSTATUS Status; + fcb* fcb; + ccb* ccb; + + if (length < sizeof(btrfs_set_inode_info)) + return STATUS_BUFFER_OVERFLOW; + + if (!FileObject) + return STATUS_INVALID_PARAMETER; + + fcb = FileObject->FsContext; + + if (!fcb) + return STATUS_INVALID_PARAMETER; + + ccb = FileObject->FsContext2; + + if (!ccb) + return STATUS_INVALID_PARAMETER; + + if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + if (bsii->mode_changed && !(ccb->access & WRITE_DAC)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + if ((bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_OWNER)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + if (bsii->flags_changed) { + if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && + (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) { + WARN("trying to change nocow flag on non-empty file\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + fcb->inode_item.flags = bsii->flags; + + if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW) + fcb->inode_item.flags |= BTRFS_INODE_NODATASUM; + else + fcb->inode_item.flags &= ~(UINT64)BTRFS_INODE_NODATASUM; + } + + if (bsii->mode_changed) { + UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; + + fcb->inode_item.st_mode &= ~allowed; + fcb->inode_item.st_mode |= bsii->st_mode & allowed; + } + + if (bsii->uid_changed) { + PSID sid; + SECURITY_INFORMATION secinfo; + SECURITY_DESCRIPTOR sd; + void* oldsd; + + fcb->inode_item.st_uid = bsii->st_uid; + + uid_to_sid(bsii->st_uid, &sid); + + Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) { + ERR("RtlCreateSecurityDescriptor returned %08x\n", Status); + goto end; + } + + Status = RtlSetOwnerSecurityDescriptor(&sd, sid, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status); + goto end; + } + + oldsd = fcb->sd; + + secinfo = OWNER_SECURITY_INFORMATION; + Status = SeSetSecurityDescriptorInfoEx(NULL, &secinfo, &sd, (void**)&fcb->sd, SEF_AVOID_PRIVILEGE_CHECK, PagedPool, IoGetFileObjectGenericMapping()); + + if (!NT_SUCCESS(Status)) { + ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status); + goto end; + } + + ExFreePool(oldsd); + + fcb->sd_dirty = TRUE; + + send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED); + } + + if (bsii->gid_changed) + fcb->inode_item.st_gid = bsii->st_gid; + + if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) + mark_fcb_dirty(fcb); + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(fcb->Header.Resource); return Status; } @@ -1084,7 +1300,7 @@ static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT File return STATUS_SUCCESS; } -static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) { +static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { FILE_SET_SPARSE_BUFFER* fssb = data; NTSTATUS Status; BOOL set; @@ -1146,7 +1362,7 @@ static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* fcb->atts_changed = TRUE; defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, - fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE); + fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp); fcb->atts_deleted = defda == fcb->atts; } @@ -1164,173 +1380,58 @@ end: } static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { - LIST_ENTRY* le; - - le = fcb->extents.Flink; - - while (le != &fcb->extents) { - LIST_ENTRY* le2 = le->Flink; - extent* ext = CONTAINING_RECORD(le, extent, list_entry); + NTSTATUS Status; + BOOL compress = write_fcb_compressed(fcb); + UINT64 start_data, end_data; + UINT8* data; - if (!ext->ignore) { - EXTENT_DATA* ed = ext->data; - EXTENT_DATA2* ed2; - UINT64 len; - - if (ext->datalen < sizeof(EXTENT_DATA)) { - ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } - - if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - ed2 = (EXTENT_DATA2*)ed->data; - } - - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - if (ext->offset < start + length && ext->offset + len >= start) { - if (ed->compression != BTRFS_COMPRESSION_NONE) { - FIXME("FIXME - compression not supported at present\n"); - return STATUS_NOT_SUPPORTED; - } - - if (ed->encryption != BTRFS_ENCRYPTION_NONE) { - WARN("encryption not supported (type %x)\n", ext->offset, ed->encryption); - return STATUS_NOT_SUPPORTED; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("other encodings not supported\n"); - return STATUS_NOT_SUPPORTED; - } - - // We can ignore prealloc and sparse extents - they're already counted as zeroed - - if (ed->type == EXTENT_TYPE_INLINE) { - extent* ext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - EXTENT_DATA* data; - UINT64 s2, e2; - - if (!ext2) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - data = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - - if (!data) { - ERR("out of memory\n"); - ExFreePool(ext2); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(data, ext->data, ext->datalen); - - s2 = max(ext->offset, start); - e2 = min(ext->offset + ed->decoded_size, start + length); - RtlZeroMemory((UINT8*)data + sizeof(EXTENT_DATA) - 1 + s2 - ext->offset, e2 - s2); - - ext2->offset = ext->offset; - ext2->data = data; - ext2->datalen = ext->datalen; - ext2->unique = ext->unique; - ext2->ignore = FALSE; - - InsertHeadList(&ext->list_entry, &ext2->list_entry); - remove_fcb_extent(ext, rollback); - } else if (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0) { - NTSTATUS Status; - BOOL nocow = fcb->inode_item.flags & BTRFS_INODE_NODATACOW && ext->unique; - UINT64 s1 = max(ext->offset, start); - UINT64 e1 = min(ext->offset + len, start + length); - UINT64 s2 = (s1 / Vcb->superblock.sector_size) * Vcb->superblock.sector_size; - UINT64 e2 = sector_align(e1, Vcb->superblock.sector_size); - UINT8* data; - - data = ExAllocatePoolWithTag(PagedPool, e2 - s2, ALLOC_TAG); - if (!data) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - Status = read_file(fcb, data, s2, e2 - s2, NULL, Irp); - if (!NT_SUCCESS(Status)) { - ERR("read_file returned %08x\n", Status); - ExFreePool(data); - return Status; - } - - RtlZeroMemory(data + s1 - s2, e1 - s1); - - if (nocow) { - UINT64 writeaddr = ed2->address + ed2->offset + s2 - ext->offset; + if (compress) { + start_data = start & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1); + end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE), + sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size)); + } else { + start_data = start & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1); + end_data = sector_align(start + length, fcb->Vcb->superblock.sector_size); + } - Status = write_data_complete(Vcb, writeaddr, data, e2 - s2, Irp); - if (!NT_SUCCESS(Status)) { - ERR("write_data_complete returned %08x\n", Status); - ExFreePool(data); - return Status; - } - - if (changed_sector_list) { - unsigned int i; - changed_sector* sc; - - sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); - if (!sc) { - ERR("out of memory\n"); - ExFreePool(data); - return STATUS_INSUFFICIENT_RESOURCES; - } - - sc->ol.key = writeaddr; - sc->length = (e2 - s2) / Vcb->superblock.sector_size; - sc->deleted = FALSE; - - sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG); - if (!sc->checksums) { - ERR("out of memory\n"); - ExFreePool(sc); - ExFreePool(data); - return STATUS_INSUFFICIENT_RESOURCES; - } - - for (i = 0; i < sc->length; i++) { - sc->checksums[i] = ~calc_crc32c(0xffffffff, data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size); - } - - insert_into_ordered_list(changed_sector_list, &sc->ol); - } - - ExFreePool(data); - } else { - Status = excise_extents(Vcb, fcb, s2, e2, rollback); - if (!NT_SUCCESS(Status)) { - ERR("excise_extents returned %08x\n", Status); - ExFreePool(data); - return Status; - } - - Status = insert_extent(Vcb, fcb, s2, e2 - s2, data, changed_sector_list, Irp, rollback); - if (!NT_SUCCESS(Status)) { - ERR("insert_extent returned %08x\n", Status); - ExFreePool(data); - return Status; - } - - ExFreePool(data); - } - } - } else if (ext->offset >= start + length) - return STATUS_SUCCESS; + data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG); + if (!data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(data, end_data - start_data); + + if (start > start_data || start + length < end_data) { + Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp); + + if (!NT_SUCCESS(Status)) { + ERR("read_file returned %08x\n", Status); + ExFreePool(data); + return Status; } + } + + RtlZeroMemory(data + start - start_data, length); + + if (compress) { + Status = write_compressed(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback); - le = le2; + ExFreePool(data); + + if (!NT_SUCCESS(Status)) { + ERR("write_compressed returned %08x\n", Status); + return Status; + } + } else { + Status = do_write_file(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback); + + ExFreePool(data); + + if (!NT_SUCCESS(Status)) { + ERR("do_write_file returned %08x\n", Status); + return Status; + } } return STATUS_SUCCESS; @@ -1350,8 +1451,6 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo BOOL nocsum; IO_STATUS_BLOCK iosb; - // FIXME - check permissions - if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION)) return STATUS_INVALID_PARAMETER; @@ -1373,7 +1472,18 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo } ccb = FileObject->FsContext2; - fileref = ccb ? ccb->fileref : NULL; + + if (!ccb) { + ERR("ccb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!(ccb->access & FILE_WRITE_DATA)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + fileref = ccb->fileref; if (!fileref) { ERR("fileref was NULL\n"); @@ -1457,7 +1567,7 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo } if (end < fzdi->BeyondFinalZero.QuadPart) { - Status = zero_data(Vcb, fcb, fzdi->BeyondFinalZero.QuadPart, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback); + Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("zero_data returned %08x\n", Status); goto end; @@ -1465,17 +1575,11 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo } if (end > start) { - Status = excise_extents(Vcb, fcb, start, end, &rollback); + Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("excise_extents returned %08x\n", Status); goto end; } - - Status = insert_sparse_extent(fcb, start, end - start, &rollback); - if (!NT_SUCCESS(Status)) { - ERR("insert_sparse_extent returned %08x\n", Status); - goto end; - } } } } @@ -1524,8 +1628,7 @@ static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FIL LIST_ENTRY* le; FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf; ULONG i = 0; - BOOL sparse, finish_off; - UINT64 nonsparse_run_start; + UINT64 last_start, last_end; TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n"); @@ -1566,51 +1669,41 @@ static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FIL le = fcb->extents.Flink; - sparse = FALSE; - nonsparse_run_start = 0xffffffffffffffff; - finish_off = TRUE; + last_start = 0; + last_end = 0; while (le != &fcb->extents) { extent* ext = CONTAINING_RECORD(le, extent, list_entry); if (!ext->ignore) { - EXTENT_DATA2* ed2 = ext->data->type == EXTENT_TYPE_REGULAR ? (EXTENT_DATA2*)ext->data->data : NULL; + EXTENT_DATA2* ed2 = (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->data->data : NULL; + UINT64 len = ed2 ? ed2->num_bytes : ext->data->decoded_size; - if (ed2 && ed2->size == 0) { // sparse - if (!sparse) { - if (nonsparse_run_start < ext->offset) { - if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) { - ranges[i].FileOffset.QuadPart = nonsparse_run_start; - ranges[i].Length.QuadPart = ext->offset - nonsparse_run_start; - i++; - } else { - Status = STATUS_BUFFER_TOO_SMALL; - goto end; - } + if (ext->offset > last_end) { // first extent after a hole + if (last_end > last_start) { + if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) { + ranges[i].FileOffset.QuadPart = last_start; + ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start; + i++; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + goto end; } - - if (ext->offset > inbuf->FileOffset.QuadPart + inbuf->Length.QuadPart) { - finish_off = FALSE; - break; - } - - sparse = TRUE; - } - } else { // not sparse - if (sparse) { - sparse = FALSE; - nonsparse_run_start = ext->offset; } + + last_start = ext->offset; } + + last_end = ext->offset + len; } le = le->Flink; } - if (finish_off && nonsparse_run_start < fcb->inode_item.st_size) { + if (last_end > last_start) { if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) { - ranges[i].FileOffset.QuadPart = nonsparse_run_start; - ranges[i].Length.QuadPart = fcb->inode_item.st_size - nonsparse_run_start; + ranges[i].FileOffset.QuadPart = last_start; + ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start; i++; } else { Status = STATUS_BUFFER_TOO_SMALL; @@ -1710,8 +1803,8 @@ static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) { flush_fcb_caches(Vcb); - if (Vcb->need_write) - do_write(Vcb, &rollback); + if (Vcb->need_write && !Vcb->readonly) + do_write(Vcb, Irp, &rollback); free_trees(Vcb); @@ -1817,7 +1910,7 @@ static NTSTATUS invalidate_volumes(PIRP Irp) { while (le != &VcbList) { device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); - if (Vcb->Vpb->RealDevice == devobj) { + if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) { if (Vcb->Vpb == devobj->Vpb) { KIRQL irql; PVPB newvpb; @@ -1851,8 +1944,8 @@ static NTSTATUS invalidate_volumes(PIRP Irp) { flush_fcb_caches(Vcb); - if (Vcb->need_write) - do_write(Vcb, &rollback); + if (Vcb->need_write && !Vcb->readonly) + do_write(Vcb, Irp, &rollback); free_trees(Vcb); @@ -1896,6 +1989,33 @@ end: return Status; } +static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG* volstate; + + if (Irp->AssociatedIrp.SystemBuffer) { + volstate = Irp->AssociatedIrp.SystemBuffer; + } else if (Irp->MdlAddress != NULL) { + volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority); + + if (!volstate) + return STATUS_INSUFFICIENT_RESOURCES; + } else + return STATUS_INVALID_USER_BUFFER; + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) + return STATUS_INVALID_PARAMETER; + + *volstate = 0; + + if (IrpSp->FileObject->FsContext != Vcb->volume_fcb) + return STATUS_INVALID_PARAMETER; + + Irp->IoStatus.Information = sizeof(ULONG); + + return STATUS_SUCCESS; +} + NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS Status; @@ -2028,8 +2148,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_IS_VOLUME_DIRTY: - WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp); break; case FSCTL_ALLOW_EXTENDED_DASD_IO: @@ -2097,7 +2216,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL case FSCTL_SET_SPARSE: Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, - IrpSp->Parameters.FileSystemControl.InputBufferLength); + IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); break; case FSCTL_SET_ZERO_DATA: @@ -2372,11 +2491,19 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_BTRFS_CREATE_SUBVOL: - Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); + Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp); break; case FSCTL_BTRFS_CREATE_SNAPSHOT: - Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); + Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp); + break; + + case FSCTL_BTRFS_GET_INODE_INFO: + Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); + break; + + case FSCTL_BTRFS_SET_INODE_INFO: + Status = set_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); break; default: diff --git a/reactos/drivers/filesystems/btrfs/pnp.c b/reactos/drivers/filesystems/btrfs/pnp.c index c63f097c096..c84f4997fb3 100644 --- a/reactos/drivers/filesystems/btrfs/pnp.c +++ b/reactos/drivers/filesystems/btrfs/pnp.c @@ -1,3 +1,20 @@ +/* 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" struct pnp_context; @@ -176,8 +193,8 @@ static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->need_write) - do_write(Vcb, &rollback); + if (Vcb->need_write && !Vcb->readonly) + do_write(Vcb, Irp, &rollback); clear_rollback(&rollback); @@ -219,7 +236,14 @@ static NTSTATUS pnp_start_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) { - FIXME("STUB\n"); + device_extension* Vcb = DeviceObject->DeviceExtension; + + TRACE("(%p, %p)\n", DeviceObject, Irp); + + if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { + uninit(Vcb, FALSE); + Vcb->Vpb->Flags &= ~VPB_MOUNTED; + } return STATUS_SUCCESS; } diff --git a/reactos/drivers/filesystems/btrfs/read.c b/reactos/drivers/filesystems/btrfs/read.c index 3d50d0a5443..869f97ad753 100644 --- a/reactos/drivers/filesystems/btrfs/read.c +++ b/reactos/drivers/filesystems/btrfs/read.c @@ -1,3 +1,20 @@ +/* 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 read_data_status { @@ -159,6 +176,12 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI UINT64 *stripestart = NULL, *stripeend = NULL; UINT16 startoffstripe; + Status = verify_vcb(Vcb, Irp); + if (!NT_SUCCESS(Status)) { + ERR("verify_vcb returned %08x\n", Status); + return Status; + } + if (Vcb->log_to_phys_loaded) { chunk* c = get_chunk_from_address(Vcb, addr); @@ -433,7 +456,23 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI for (i = 0; i < ci->num_stripes; i++) { if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) { - IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj); + if (Irp && context->stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) { + PDEVICE_OBJECT dev; + + dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread); + IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL); + + if (!dev) { + dev = IoGetDeviceToVerify(PsGetCurrentThread()); + IoSetDeviceToVerify(PsGetCurrentThread(), NULL); + } + + dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL; + + if (dev) + IoVerifyVolume(dev, FALSE); + } +// IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj); Status = context->stripes[i].iosb.Status; goto exit; @@ -702,7 +741,7 @@ static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG l return Status; } -static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length) { +static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) { NTSTATUS Status; KEY searchkey; traverse_ptr tp, next_tp; @@ -713,7 +752,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 searchkey.obj_type = TYPE_EXTENT_CSUM; searchkey.offset = start; - Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -742,7 +781,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 break; } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); + b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp); if (b) tp = next_tp; @@ -756,7 +795,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 return STATUS_SUCCESS; } -static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum) { +static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum, PIRP Irp) { UINT32* csum = NULL; NTSTATUS Status; UINT64 end; @@ -827,7 +866,7 @@ static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UI runlength = RtlFindFirstRunClear(&bmp, &index); while (runlength != 0) { - Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength); + Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength, Irp); if (!NT_SUCCESS(Status)) { ERR("load_csum_from_disk returned %08x\n", Status); goto end; @@ -854,72 +893,23 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U NTSTATUS Status; EXTENT_DATA* ed; UINT64 bytes_read = 0; + UINT64 last_end; LIST_ENTRY* le; TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr); if (pbr) *pbr = 0; + + if (start >= fcb->inode_item.st_size) { + WARN("Tried to read beyond end of file\n"); + Status = STATUS_END_OF_FILE; + goto exit; + } le = fcb->extents.Flink; - - while (le != &fcb->extents) { - extent* ext = CONTAINING_RECORD(le, extent, list_entry); - - if (!ext->ignore) { - if (ext->offset == start) - break; - else if (ext->offset > start) { - LIST_ENTRY* le2 = le->Blink; - - ext = NULL; - - while (le2 != &fcb->extents) { - extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry); - - if (!ext2->ignore) { - le = le2; - ext = ext2; - break; - } - - le2 = le2->Blink; - } - - if (!ext) { - ERR("first extent was after offset\n"); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } else - break; - } - } - - le = le->Flink; - } - - if (le == &fcb->extents) { - LIST_ENTRY* le2 = le->Blink; - extent* ext = NULL; - while (le2 != &fcb->extents) { - extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry); - - if (!ext2->ignore) { - le = le2; - ext = ext2; - break; - } - - le2 = le2->Blink; - } - - if (!ext) { - ERR("could not find extent\n"); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - } + last_end = start; while (le != &fcb->extents) { UINT64 len; @@ -929,33 +919,25 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U if (!ext->ignore) { ed = ext->data; - if (ext->datalen < sizeof(EXTENT_DATA)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - Status = STATUS_INTERNAL_ERROR; - goto exit; + ed2 = (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ed->data : NULL; + + len = ed2 ? ed2->num_bytes : ed->decoded_size; + + if (ext->offset + len <= start) { + last_end = ext->offset + len; + goto nextitem; } - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - Status = STATUS_INTERNAL_ERROR; - goto exit; + if (ext->offset > last_end && ext->offset > start + bytes_read) { + UINT32 read = min(length, ext->offset - max(start, last_end)); + + RtlZeroMemory(data + bytes_read, read); + bytes_read += read; + length -= read; } - ed2 = (EXTENT_DATA2*)ed->data; - - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - if (ext->offset + len < start) { - ERR("Tried to read beyond end of file\n"); - Status = STATUS_END_OF_FILE; - goto exit; - } - - if (ed->compression != BTRFS_COMPRESSION_NONE) { - FIXME("FIXME - compression not yet supported\n"); - Status = STATUS_NOT_IMPLEMENTED; - goto exit; - } + if (length == 0 || ext->offset > start + bytes_read + length) + break; if (ed->encryption != BTRFS_ENCRYPTION_NONE) { WARN("Encryption not supported\n"); @@ -979,6 +961,8 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U RtlCopyMemory(data + bytes_read, &ed->data[off], read); + // FIXME - can we have compressed inline extents? + bytes_read += read; length -= read; break; @@ -989,16 +973,13 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U UINT64 off = start + bytes_read - ext->offset; UINT32 to_read, read; UINT8* buf; + UINT32 *csum, bumpoff = 0; + UINT64 addr; read = len - off; if (read > length) read = length; - if (ed2->address == 0) { - RtlZeroMemory(data + bytes_read, read); - } else { - UINT32 *csum, bumpoff = 0; - UINT64 addr; - + if (ed->compression == BTRFS_COMPRESSION_NONE) { addr = ed2->address + ed2->offset + off; to_read = sector_align(read, fcb->Vcb->superblock.sector_size); @@ -1007,41 +988,71 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U addr -= bumpoff; to_read = sector_align(read + bumpoff, fcb->Vcb->superblock.sector_size); } + } else { + addr = ed2->address; + to_read = sector_align(ed2->size, fcb->Vcb->superblock.sector_size); + } + + buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG); + + if (!buf) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { + Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum, Irp); - buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG); + if (!NT_SUCCESS(Status)) { + ERR("load_csum returned %08x\n", Status); + ExFreePool(buf); + goto exit; + } + } else + csum = NULL; + + Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp); + if (!NT_SUCCESS(Status)) { + ERR("read_data returned %08x\n", Status); + ExFreePool(buf); + goto exit; + } + + if (ed->compression == BTRFS_COMPRESSION_NONE) { + RtlCopyMemory(data + bytes_read, buf + bumpoff, read); + } else { + UINT8* decomp = NULL; - if (!buf) { + // FIXME - don't mess around with decomp if we're reading the whole extent + + decomp = ExAllocatePoolWithTag(PagedPool, ed->decoded_size, ALLOC_TAG); + if (!decomp) { ERR("out of memory\n"); + ExFreePool(buf); Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } - if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { - Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum); - - if (!NT_SUCCESS(Status)) { - ERR("load_csum returned %08x\n", Status); - ExFreePool(buf); - goto exit; - } - } else - csum = NULL; + Status = decompress(ed->compression, buf, ed2->size, decomp, ed->decoded_size); - Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp); if (!NT_SUCCESS(Status)) { - ERR("read_data returned %08x\n", Status); + ERR("decompress returned %08x\n", Status); ExFreePool(buf); + ExFreePool(decomp); goto exit; } - RtlCopyMemory(data + bytes_read, buf + bumpoff, read); + RtlCopyMemory(data + bytes_read, decomp + ed2->offset + off, min(read, ed2->num_bytes - off)); - ExFreePool(buf); - - if (csum) - ExFreePool(csum); + ExFreePool(decomp); } + ExFreePool(buf); + + if (csum) + ExFreePool(csum); + bytes_read += read; length -= read; @@ -1068,14 +1079,26 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U Status = STATUS_NOT_IMPLEMENTED; goto exit; } + + last_end = ext->offset + len; if (length == 0) break; } +nextitem: le = le->Flink; } + if (length > 0 && start + bytes_read < fcb->inode_item.st_size) { + UINT32 read = min(fcb->inode_item.st_size - start - bytes_read, length); + + RtlZeroMemory(data + bytes_read, read); + + bytes_read += read; + length -= read; + } + Status = STATUS_SUCCESS; if (pbr) *pbr = bytes_read; @@ -1131,7 +1154,7 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read) { } if (start >= fcb->Header.ValidDataLength.QuadPart) { - length = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart; + length = min(length, min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart); RtlZeroMemory(data, length); Irp->IoStatus.Information = *bytes_read = length; return STATUS_SUCCESS; @@ -1233,7 +1256,7 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { BOOL top_level; fcb* fcb; ccb* ccb; - BOOL tree_lock = FALSE, fcb_lock = FALSE; + BOOL tree_lock = FALSE, fcb_lock = FALSE, pagefile; FsRtlEnterFileSystem(); @@ -1266,6 +1289,12 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto exit; } + if (fcb == Vcb->volume_fcb) { + TRACE("not allowing read of volume FCB\n"); + Status = STATUS_INVALID_PARAMETER; + goto exit; + } + ccb = FileObject->FsContext2; if (!ccb) { @@ -1280,14 +1309,18 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto exit; } + pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && Irp->Flags & IRP_PAGING_IO; + if (Irp->Flags & IRP_NOCACHE) { - if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) { - Status = STATUS_PENDING; - IoMarkIrpPending(Irp); - goto exit; + if (!pagefile) { + if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) { + Status = STATUS_PENDING; + IoMarkIrpPending(Irp); + goto exit; + } + + tree_lock = TRUE; } - - tree_lock = TRUE; if (!ExAcquireResourceSharedLite(fcb->Header.Resource, IoIsOperationSynchronous(Irp))) { Status = STATUS_PENDING; diff --git a/reactos/drivers/filesystems/btrfs/registry.c b/reactos/drivers/filesystems/btrfs/registry.c index a959c70ea4d..171969019b0 100644 --- a/reactos/drivers/filesystems/btrfs/registry.c +++ b/reactos/drivers/filesystems/btrfs/registry.c @@ -1,20 +1,48 @@ +/* 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" extern UNICODE_STRING log_device, log_file, registry_path; static WCHAR option_mounted[] = L"Mounted"; -static WCHAR option_ignore[] = L"Ignore"; #define hex_digit(c) ((c) >= 0 && (c) <= 9) ? ((c) + '0') : ((c) - 10 + 'a') -NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) { - UNICODE_STRING path, ignoreus; +NTSTATUS registry_load_volume_options(device_extension* Vcb) { + BTRFS_UUID* uuid = &Vcb->superblock.uuid; + mount_options* options = &Vcb->options; + UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus, + maxinlineus, subvolidus; OBJECT_ATTRIBUTES oa; NTSTATUS Status; ULONG i, j, kvfilen, index, retlen; KEY_VALUE_FULL_INFORMATION* kvfi = NULL; HANDLE h; + options->compress = mount_compress; + options->compress_force = mount_compress_force; + options->compress_type = mount_compress_type > BTRFS_COMPRESSION_LZO ? 0 : mount_compress_type; + options->readonly = FALSE; + options->zlib_level = mount_zlib_level; + options->flush_interval = mount_flush_interval; + options->max_inline = min(mount_max_inline, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1); + options->subvol_id = 0; + path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); @@ -62,8 +90,15 @@ NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) index = 0; - ignoreus.Buffer = option_ignore; - ignoreus.Length = ignoreus.MaximumLength = wcslen(option_ignore) * sizeof(WCHAR); + RtlInitUnicodeString(&ignoreus, L"Ignore"); + RtlInitUnicodeString(&compressus, L"Compress"); + RtlInitUnicodeString(&compressforceus, L"CompressForce"); + RtlInitUnicodeString(&compresstypeus, L"CompressType"); + RtlInitUnicodeString(&readonlyus, L"Readonly"); + RtlInitUnicodeString(&zliblevelus, L"ZlibLevel"); + RtlInitUnicodeString(&flushintervalus, L"FlushInterval"); + RtlInitUnicodeString(&maxinlineus, L"MaxInline"); + RtlInitUnicodeString(&subvolidus, L"SubvolId"); do { Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen); @@ -80,12 +115,53 @@ NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); options->ignore = *val != 0 ? TRUE : FALSE; + } else if (FsRtlAreNamesEqual(&compressus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->compress = *val != 0 ? TRUE : FALSE; + } else if (FsRtlAreNamesEqual(&compressforceus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->compress_force = *val != 0 ? TRUE : FALSE; + } else if (FsRtlAreNamesEqual(&compresstypeus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->compress_type = *val > BTRFS_COMPRESSION_LZO ? 0 : *val; + } else if (FsRtlAreNamesEqual(&readonlyus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->readonly = *val != 0 ? TRUE : FALSE; + } else if (FsRtlAreNamesEqual(&zliblevelus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->zlib_level = *val; + } else if (FsRtlAreNamesEqual(&flushintervalus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->flush_interval = *val; + } else if (FsRtlAreNamesEqual(&maxinlineus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->max_inline = min(*val, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1); + } else if (FsRtlAreNamesEqual(&subvolidus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_QWORD) { + UINT64* val = (UINT64*)((UINT8*)kvfi + kvfi->DataOffset); + + options->subvol_id = *val; } } else if (Status != STATUS_NO_MORE_ENTRIES) { ERR("ZwEnumerateValueKey returned %08x\n", Status); goto end2; } } while (NT_SUCCESS(Status)); + + if (!options->compress && options->compress_force) + options->compress = TRUE; + + if (options->zlib_level > 9) + options->zlib_level = 9; + + if (options->flush_interval == 0) + options->flush_interval = mount_flush_interval; Status = STATUS_SUCCESS; @@ -472,38 +548,13 @@ static void read_mappings(PUNICODE_STRING regpath) { ExFreePool(path); } -void STDCALL read_registry(PUNICODE_STRING regpath) { -#ifndef __REACTOS__ - UNICODE_STRING us; -#endif - OBJECT_ATTRIBUTES oa; - NTSTATUS Status; - HANDLE h; - ULONG dispos; -#ifndef __REACTOS__ +static void get_registry_value(HANDLE h, WCHAR* string, ULONG type, void* val, ULONG size) { ULONG kvfilen; KEY_VALUE_FULL_INFORMATION* kvfi; -#endif + UNICODE_STRING us; + NTSTATUS Status; -#ifndef __REACTOS__ - static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log"; -#endif - - read_mappings(regpath); - - InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); - - if (!NT_SUCCESS(Status)) { - ERR("ZwCreateKey returned %08x\n", Status); - return; - } - - reset_subkeys(h, regpath); - -#ifdef _DEBUG - RtlInitUnicodeString(&us, L"DebugLogLevel"); + RtlInitUnicodeString(&us, string); kvfi = NULL; kvfilen = 0; @@ -521,31 +572,75 @@ void STDCALL read_registry(PUNICODE_STRING regpath) { Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); if (NT_SUCCESS(Status)) { - if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) { - RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32)); + if (kvfi->Type == type && kvfi->DataLength >= size) { + RtlCopyMemory(val, ((UINT8*)kvfi) + kvfi->DataOffset, size); } else { Status = ZwDeleteValueKey(h, &us); if (!NT_SUCCESS(Status)) { ERR("ZwDeleteValueKey returned %08x\n", Status); } - Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level)); + Status = ZwSetValueKey(h, &us, 0, type, val, size); if (!NT_SUCCESS(Status)) { - ERR("ZwSetValueKey reutrned %08x\n", Status); + ERR("ZwSetValueKey returned %08x\n", Status); } } } ExFreePool(kvfi); } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { - Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level)); + Status = ZwSetValueKey(h, &us, 0, type, val, size); if (!NT_SUCCESS(Status)) { - ERR("ZwSetValueKey reutrned %08x\n", Status); + ERR("ZwSetValueKey returned %08x\n", Status); } } else { ERR("ZwQueryValueKey returned %08x\n", Status); } +} + +void STDCALL read_registry(PUNICODE_STRING regpath) { +#ifndef __REACTOS__ + UNICODE_STRING us; +#endif + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + HANDLE h; + ULONG dispos; +#ifndef __REACTOS__ + ULONG kvfilen; + KEY_VALUE_FULL_INFORMATION* kvfi; +#endif + +#ifndef __REACTOS__ + static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log"; +#endif + + read_mappings(regpath); + + InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); + + if (!NT_SUCCESS(Status)) { + ERR("ZwCreateKey returned %08x\n", Status); + return; + } + + reset_subkeys(h, regpath); + + get_registry_value(h, L"Compress", REG_DWORD, &mount_compress, sizeof(mount_compress)); + get_registry_value(h, L"CompressForce", REG_DWORD, &mount_compress_force, sizeof(mount_compress_force)); + get_registry_value(h, L"CompressType", REG_DWORD, &mount_compress_type, sizeof(mount_compress_type)); + get_registry_value(h, L"ZlibLevel", REG_DWORD, &mount_zlib_level, sizeof(mount_zlib_level)); + get_registry_value(h, L"FlushInterval", REG_DWORD, &mount_flush_interval, sizeof(mount_flush_interval)); + get_registry_value(h, L"MaxInline", REG_DWORD, &mount_max_inline, sizeof(mount_max_inline)); + + if (mount_flush_interval == 0) + mount_flush_interval = 1; + +#ifdef _DEBUG + get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level)); RtlInitUnicodeString(&us, L"LogDevice"); diff --git a/reactos/drivers/filesystems/btrfs/reparse.c b/reactos/drivers/filesystems/btrfs/reparse.c index e281dc3f61f..0aa0e26de07 100644 --- a/reactos/drivers/filesystems/btrfs/reparse.c +++ b/reactos/drivers/filesystems/btrfs/reparse.c @@ -26,8 +26,6 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, char* data; NTSTATUS Status; - // FIXME - check permissions - TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen); ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); @@ -134,6 +132,7 @@ end: static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) { NTSTATUS Status; ULONG minlen; + ULONG tlength; UNICODE_STRING subname; ANSI_STRING target; LARGE_INTEGER offset, time; @@ -155,7 +154,7 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rd fileref->fcb->inode_item.st_mode |= __S_IFLNK; - Status = truncate_file(fileref->fcb, 0, rollback); + Status = truncate_file(fileref->fcb, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08x\n", Status); return Status; @@ -187,7 +186,8 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rd } offset.QuadPart = 0; - Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, FALSE, TRUE, + tlength = target.Length; + Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, &tlength, FALSE, TRUE, TRUE, FALSE, rollback); ExFreePool(target.Buffer); @@ -221,9 +221,6 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ULONG tag; LIST_ENTRY rollback; - // FIXME - send notification if this succeeds? The attributes will have changed. - // FIXME - check permissions - TRACE("(%p, %p)\n", DeviceObject, Irp); InitializeListHead(&rollback); @@ -241,6 +238,13 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { return STATUS_INVALID_PARAMETER; } + // It isn't documented what permissions FSCTL_SET_REPARSE_POINT needs, but CreateSymbolicLinkW + // creates a file with FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE. + if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + fileref = ccb->fileref; if (!fileref) { @@ -300,7 +304,7 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = STATUS_SUCCESS; } else { // otherwise, store as file data - Status = truncate_file(fcb, 0, &rollback); + Status = truncate_file(fcb, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08x\n", Status); goto end; @@ -356,8 +360,6 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { file_ref* fileref; LIST_ENTRY rollback; - // FIXME - check permissions - TRACE("(%p, %p)\n", DeviceObject, Irp); InitializeListHead(&rollback); @@ -368,13 +370,25 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } fcb = FileObject->FsContext; + + if (!fcb) { + ERR("fcb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + ccb = FileObject->FsContext2; - fileref = ccb ? ccb->fileref : NULL; - ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); - ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + if (!ccb) { + ERR("ccb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } - TRACE("%S\n", file_desc(FileObject)); + if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + fileref = ccb->fileref; if (!fileref) { ERR("fileref was NULL\n"); @@ -382,6 +396,11 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto end; } + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + TRACE("%S\n", file_desc(FileObject)); + if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) { ERR("buffer was too short\n"); Status = STATUS_INVALID_PARAMETER; @@ -433,7 +452,7 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { // FIXME - do we need to check that the reparse tags match? - Status = truncate_file(fcb, 0, &rollback); + Status = truncate_file(fcb, 0, Irp, &rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08x\n", Status); goto end; diff --git a/reactos/drivers/filesystems/btrfs/security.c b/reactos/drivers/filesystems/btrfs/security.c index 9b6b0cf06fd..b2d0d788e03 100644 --- a/reactos/drivers/filesystems/btrfs/security.c +++ b/reactos/drivers/filesystems/btrfs/security.c @@ -201,6 +201,7 @@ void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) { um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG); if (!um) { ERR("out of memory\n"); + ExFreePool(sid); return; } @@ -210,7 +211,7 @@ void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) { InsertTailList(&uid_map_list, &um->listentry); } -static void uid_to_sid(UINT32 uid, PSID* sid) { +void uid_to_sid(UINT32 uid, PSID* sid) { LIST_ENTRY* le; uid_map* um; sid_header* sh; @@ -400,12 +401,12 @@ static ACL* load_default_acl() { // } // } -static BOOL get_sd_from_xattr(fcb* fcb) { +static BOOL get_sd_from_xattr(fcb* fcb, PIRP Irp) { 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)) + if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen, Irp)) return FALSE; TRACE("using xattr " EA_NTACL " for security descriptor\n"); @@ -654,12 +655,12 @@ end: ExFreePool(groupsid); } -void fcb_get_sd(fcb* fcb, struct _fcb* parent) { +void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp) { NTSTATUS Status; PSID usersid = NULL, groupsid = NULL; SECURITY_SUBJECT_CONTEXT subjcont; - if (get_sd_from_xattr(fcb)) + if (get_sd_from_xattr(fcb, Irp)) return; if (!parent) { @@ -833,8 +834,7 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi fcb = fileref->parent->fcb; else { ERR("could not find parent fcb for stream\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; + return STATUS_INTERNAL_ERROR; } } @@ -985,7 +985,7 @@ NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) { ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); fcb->inode_item.st_uid = UID_NOBODY; } else { - fcb->inode_item.st_uid = sid_to_uid(&owner); + fcb->inode_item.st_uid = sid_to_uid(owner); } return STATUS_SUCCESS; diff --git a/reactos/drivers/filesystems/btrfs/treefuncs.c b/reactos/drivers/filesystems/btrfs/treefuncs.c index d3a63df0fb5..c9c988bca17 100644 --- a/reactos/drivers/filesystems/btrfs/treefuncs.c +++ b/reactos/drivers/filesystems/btrfs/treefuncs.c @@ -25,7 +25,7 @@ typedef struct { LIST_ENTRY list_entry; } rollback_item; -NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line) { +NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line) { UINT8* buf; NTSTATUS Status; tree_header* th; @@ -42,7 +42,7 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, NULL); + Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, Irp); if (!NT_SUCCESS(Status)) { ERR("read_data returned 0x%08x\n", Status); ExFreePool(buf); @@ -288,7 +288,8 @@ static tree* free_tree2(tree* t, const char* func, const char* file, unsigned in return NULL; } -NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line) { +NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp, + const char* func, const char* file, unsigned int line) { // KIRQL irql; // tree_holder_nonpaged* thnp = th->nonpaged; BOOL ret; @@ -325,7 +326,7 @@ NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, if (!th->tree) { NTSTATUS Status; - Status = _load_tree(Vcb, th->address, r, &th->tree, t, func, file, line); + Status = _load_tree(Vcb, th->address, r, &th->tree, t, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("load_tree returned %08x\n", Status); ExReleaseResourceLite(&r->nonpaged->load_tree_lock); @@ -389,7 +390,8 @@ static __inline tree_data* next_item(tree* t, tree_data* td) { return CONTAINING_RECORD(le, tree_data, list_entry); } -static NTSTATUS 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) { +static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, + const char* func, const char* file, unsigned int line) { int cmp; tree_data *td, *lasttd; @@ -399,7 +401,7 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver td = first_item(t); lasttd = NULL; - if (!td) return STATUS_INTERNAL_ERROR; + if (!td) return STATUS_NOT_FOUND; do { cmp = keycmp(searchkey, &td->key); @@ -437,7 +439,7 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver oldtp.tree = t; oldtp.item = td; - while (_find_prev_item(Vcb, &oldtp, tp, TRUE, func, file, line)) { + while (_find_prev_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) { if (!tp->item->ignore) return STATUS_SUCCESS; @@ -449,14 +451,14 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver oldtp.tree = t; oldtp.item = td; - while (_find_next_item(Vcb, &oldtp, tp, TRUE, func, file, line)) { + while (_find_next_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) { if (!tp->item->ignore) return STATUS_SUCCESS; oldtp = *tp; } - return STATUS_INTERNAL_ERROR; + return STATUS_NOT_FOUND; } else { tp->tree = t; tp->item = td; @@ -472,24 +474,24 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver } if (!td) - return STATUS_INTERNAL_ERROR; + return STATUS_NOT_FOUND; // 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); - Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return Status; } - Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line); + Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line); return Status; } } -NTSTATUS 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) { +NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line) { NTSTATUS Status; BOOL loaded; // KIRQL irql; @@ -497,15 +499,15 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey); if (!r->treeholder.tree) { - Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return Status; } } - Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line); - if (!NT_SUCCESS(Status)) { + Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line); + if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) { ERR("find_item_in_tree returned %08x\n", Status); } @@ -519,7 +521,8 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co return Status; } -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_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp, + const char* func, const char* file, unsigned int line) { tree* t; tree_data *td, *next; NTSTATUS Status; @@ -563,7 +566,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav if (!t) return FALSE; - Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return FALSE; @@ -576,7 +579,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav fi = first_item(t); - Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return FALSE; @@ -592,7 +595,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav traverse_ptr ntp2; BOOL b; - while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) { + while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, Irp, func, file, line))) { *next_tp = ntp2; if (!next_tp->item->ignore) @@ -622,7 +625,8 @@ static __inline tree_data* last_item(tree* t) { 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) { +BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp, + const char* func, const char* file, unsigned int line) { tree* t; tree_data* td; NTSTATUS Status; @@ -649,7 +653,7 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav td = prev_item(t->parent, t->paritem); - Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return FALSE; @@ -662,7 +666,7 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav li = last_item(t); - Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, func, file, line); + Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp, func, file, line); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return FALSE; @@ -793,7 +797,7 @@ void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) { InsertTailList(rollback, &ri->list_entry); } -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, LIST_ENTRY* rollback) { +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, PIRP Irp, LIST_ENTRY* rollback) { traverse_ptr tp; KEY searchkey; int cmp; @@ -820,13 +824,13 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN searchkey.obj_type = obj_type; searchkey.offset = offset; - Status = find_item(Vcb, r, &tp, &searchkey, TRUE); - if (!NT_SUCCESS(Status)) { + Status = find_item(Vcb, r, &tp, &searchkey, TRUE, Irp); + if (Status == STATUS_NOT_FOUND) { if (r) { if (!r->treeholder.tree) { BOOL loaded; - Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded); + Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); @@ -845,6 +849,9 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN ERR("error: find_item returned %08x\n", Status); goto end; } + } else if (!NT_SUCCESS(Status)) { + ERR("find_item returned %08x\n", Status); + goto end; } TRACE("tp.item = %p\n", tp.item); @@ -855,6 +862,7 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN 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); + int3; goto end; } } else @@ -970,10 +978,10 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR 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 (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { - ERR("ERROR - tree_lock not held exclusively\n"); - int3; - } +// if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { +// ERR("ERROR - tree_lock not held exclusively\n"); +// int3; +// } 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); @@ -1025,6 +1033,10 @@ void clear_rollback(LIST_ENTRY* rollback) { switch (ri->type) { case ROLLBACK_INSERT_ITEM: case ROLLBACK_DELETE_ITEM: + case ROLLBACK_ADD_SPACE: + case ROLLBACK_SUBTRACT_SPACE: + case ROLLBACK_INSERT_EXTENT: + case ROLLBACK_DELETE_EXTENT: ExFreePool(ri->ptr); break; @@ -1037,6 +1049,7 @@ void clear_rollback(LIST_ENTRY* rollback) { } void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) { + NTSTATUS Status; rollback_item* ri; while (!IsListEmpty(rollback)) { @@ -1082,17 +1095,105 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) { case ROLLBACK_INSERT_EXTENT: { - extent* ext = ri->ptr; + rollback_extent* re = ri->ptr; - ext->ignore = TRUE; + re->ext->ignore = TRUE; + + if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data; + + if (ed2->size != 0) { + chunk* c = get_chunk_from_address(Vcb, ed2->address); + + if (c) { + Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id, + re->fcb->inode, re->ext->offset - ed2->offset, -1, + re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL); + + if (!NT_SUCCESS(Status)) + ERR("update_changed_extent_ref returned %08x\n", Status); + } + + re->fcb->inode_item.st_blocks -= ed2->num_bytes; + } + } + + ExFreePool(re); break; } case ROLLBACK_DELETE_EXTENT: { - extent* ext = ri->ptr; + rollback_extent* re = ri->ptr; + + re->ext->ignore = FALSE; + + if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data; + + if (ed2->size != 0) { + chunk* c = get_chunk_from_address(Vcb, ed2->address); + + if (c) { + Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id, + re->fcb->inode, re->ext->offset - ed2->offset, 1, + re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL); + + if (!NT_SUCCESS(Status)) + ERR("update_changed_extent_ref returned %08x\n", Status); + } + + re->fcb->inode_item.st_blocks += ed2->num_bytes; + } + } + + ExFreePool(re); + break; + } + + case ROLLBACK_ADD_SPACE: + case ROLLBACK_SUBTRACT_SPACE: + { + rollback_space* rs = ri->ptr; + + if (rs->chunk) + ExAcquireResourceExclusiveLite(&rs->chunk->lock, TRUE); + + if (ri->type == ROLLBACK_ADD_SPACE) + space_list_subtract2(rs->list, rs->list_size, rs->address, rs->length, NULL); + else + space_list_add2(rs->list, rs->list_size, rs->address, rs->length, NULL); + + if (rs->chunk) { + LIST_ENTRY* le2 = le->Blink; + + while (le2 != rollback) { + LIST_ENTRY* le3 = le2->Blink; + rollback_item* ri2 = CONTAINING_RECORD(le2, rollback_item, list_entry); + + if (ri2->type == ROLLBACK_ADD_SPACE || ri2->type == ROLLBACK_SUBTRACT_SPACE) { + rollback_space* rs2 = ri2->ptr; + + if (rs2->chunk == rs->chunk) { + if (ri2->type == ROLLBACK_ADD_SPACE) + space_list_subtract2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL); + else + space_list_add2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL); + + ExFreePool(rs2); + RemoveEntryList(&ri2->list_entry); + ExFreePool(ri2); + } + } + + le2 = le3; + } + + ExReleaseResourceLite(&rs->chunk->lock); + } + + ExFreePool(rs); - ext->ignore = FALSE; break; } } diff --git a/reactos/drivers/filesystems/btrfs/worker-thread.c b/reactos/drivers/filesystems/btrfs/worker-thread.c index 84c289374dc..2895fb4ba05 100644 --- a/reactos/drivers/filesystems/btrfs/worker-thread.c +++ b/reactos/drivers/filesystems/btrfs/worker-thread.c @@ -1,3 +1,20 @@ +/* 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" void do_read_job(PIRP Irp) { @@ -78,6 +95,7 @@ void STDCALL worker_thread(void* context) { while (TRUE) { LIST_ENTRY* le; + device_extension* Vcb = thread->DeviceObject->DeviceExtension; KeAcquireSpinLock(&thread->spin_lock, &irql); @@ -91,6 +109,7 @@ void STDCALL worker_thread(void* context) { KeReleaseSpinLock(&thread->spin_lock, irql); + InterlockedDecrement(&Vcb->threads.pending_jobs); do_job(thread, le); } diff --git a/reactos/drivers/filesystems/btrfs/write.c b/reactos/drivers/filesystems/btrfs/write.c index 7df8f5ef596..9e14b21a17b 100644 --- a/reactos/drivers/filesystems/btrfs/write.c +++ b/reactos/drivers/filesystems/btrfs/write.c @@ -49,7 +49,8 @@ typedef struct { // static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len); static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr); -static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback); +static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback); +static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback); static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { write_context* context = conptr; @@ -274,7 +275,7 @@ static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) { return lastaddr; } -static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTRY* rollback) { +static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; DEV_ITEM* di; @@ -284,7 +285,7 @@ static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTR searchkey.obj_type = TYPE_DEV_ITEM; searchkey.offset = device->devitem.dev_id; - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -305,7 +306,7 @@ static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTR 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, rollback)) { + if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -483,7 +484,7 @@ static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UI return TRUE; } -chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { +chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) { UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor; UINT64 total_size = 0, i, logaddr; UINT16 type, num_stripes, sub_stripes, max_stripes, min_stripes; @@ -501,6 +502,8 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { } TRACE("total_size = %llx\n", total_size); + // We purposely check for DATA first - mixed blocks have the same size + // as DATA ones. if (flags & BLOCK_FLAG_DATA) { max_stripe_size = 0x40000000; // 1 GB max_chunk_size = 10 * max_stripe_size; @@ -523,12 +526,12 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { if (flags & BLOCK_FLAG_DUPLICATE) { min_stripes = 2; max_stripes = 2; - sub_stripes = 1; + sub_stripes = 0; type = BLOCK_FLAG_DUPLICATE; } else if (flags & BLOCK_FLAG_RAID0) { min_stripes = 2; max_stripes = Vcb->superblock.num_devices; - sub_stripes = 1; + sub_stripes = 0; type = BLOCK_FLAG_RAID0; } else if (flags & BLOCK_FLAG_RAID1) { min_stripes = 2; @@ -585,22 +588,14 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { goto end; } - c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG); + c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG); if (!c) { ERR("out of memory\n"); goto end; } - c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG); - if (!c->nonpaged) { - ERR("out of memory\n"); - goto end; - } - - // add CHUNK_ITEM to tree 3 - cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE)); - c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG); + c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG); if (!c->chunk_item) { ERR("out of memory\n"); goto end; @@ -640,7 +635,7 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { c->chunk_item->num_stripes = num_stripes; c->chunk_item->sub_stripes = sub_stripes; - c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG); + c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG); if (!c->devices) { ERR("out of memory\n"); goto end; @@ -673,10 +668,10 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { InitializeListHead(&c->deleting); InitializeListHead(&c->changed_extents); - ExInitializeResourceLite(&c->nonpaged->lock); - ExInitializeResourceLite(&c->nonpaged->changed_extents_lock); + ExInitializeResourceLite(&c->lock); + ExInitializeResourceLite(&c->changed_extents_lock); - s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); + s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG); if (!s) { ERR("out of memory\n"); goto end; @@ -692,7 +687,7 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { for (i = 0; i < num_stripes; i++) { stripes[i].device->devitem.bytes_used += stripe_size; - space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, rollback); + space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL); } success = TRUE; @@ -734,10 +729,9 @@ end: return success ? c : NULL; } -NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp) { +NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c) { NTSTATUS Status; UINT32 i; - chunk* c; CHUNK_ITEM_STRIPE* cis; write_data_stripe* stripe; UINT64 *stripestart = NULL, *stripeend = NULL; @@ -746,10 +740,12 @@ NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, B TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length); - c = get_chunk_from_address(Vcb, address); if (!c) { - ERR("could not get chunk for address %llx\n", address); - return STATUS_INTERNAL_ERROR; + c = get_chunk_from_address(Vcb, address); + if (!c) { + ERR("could not get chunk for address %llx\n", address); + return STATUS_INTERNAL_ERROR; + } } if (c->chunk_item->type & BLOCK_FLAG_RAID5) { @@ -973,7 +969,7 @@ NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, B stripe->Irp = NULL; stripe->buf = NULL; } else { - stripe->context = (struct write_data_context*)wtc; + stripe->context = (struct _write_data_context*)wtc; stripe->buf = stripedata[i]; stripe->need_free = need_free2; stripe->device = c->devices[i]; @@ -1046,7 +1042,7 @@ end: return Status; } -NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp) { +NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c) { write_data_context* wtc; NTSTATUS Status; @@ -1061,7 +1057,7 @@ NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void wtc->tree = FALSE; wtc->stripes_left = 0; - Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp); + Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp, c); if (!NT_SUCCESS(Status)) { ERR("write_data returned %08x\n", Status); free_write_data_stripes(wtc); @@ -1123,13 +1119,13 @@ static void clean_space_cache(device_extension* Vcb) { while (!IsListEmpty(&Vcb->chunks_changed)) { c = CONTAINING_RECORD(Vcb->chunks_changed.Flink, chunk, list_entry_changed); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); clean_space_cache_chunk(Vcb, c); RemoveEntryList(&c->list_entry_changed); c->list_entry_changed.Flink = NULL; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); } } @@ -1173,7 +1169,6 @@ static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) { static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) { UINT8 level; LIST_ENTRY* le; - NTSTATUS Status; for (level = 0; level <= 255; level++) { BOOL nothing_found = TRUE; @@ -1194,46 +1189,6 @@ static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) { TRACE("adding tree %p (level %x)\n", t->parent, t->header.level); t->parent->write = TRUE; - } else if (t->root != Vcb->chunk_root && t->root != Vcb->root_root) { - KEY searchkey; - traverse_ptr tp; - - searchkey.obj_id = t->root->id; - searchkey.obj_type = TYPE_ROOT_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - 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); - 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); - if (!ri) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - if (tp.item->size > 0) - 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, rollback); - - if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - return STATUS_INTERNAL_ERROR; - } - } else { - tp.tree->write = TRUE; - } } } @@ -1248,37 +1203,13 @@ static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) { } static void add_parents_to_cache(device_extension* Vcb, tree* t) { - KEY searchkey; - traverse_ptr tp; - NTSTATUS Status; - while (t->parent) { t = t->parent; t->write = 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; - - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - 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); - return; - } - - tp.tree->write = TRUE; } -static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, LIST_ENTRY* rollback) { +static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, PIRP Irp, LIST_ENTRY* rollback) { EXTENT_ITEM_SKINNY_METADATA* eism; traverse_ptr insert_tp; @@ -1294,24 +1225,24 @@ static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 eism->type = TYPE_TREE_BLOCK_REF; eism->tbr.offset = root_id; - if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(eism); return FALSE; } - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); add_parents_to_cache(Vcb, insert_tp.tree); return TRUE; } -static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, LIST_ENTRY* rollback) { +static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, PIRP Irp, LIST_ENTRY* rollback) { UINT64 address; EXTENT_ITEM_TREE2* eit2; traverse_ptr insert_tp; @@ -1322,7 +1253,7 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i return FALSE; if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) { - BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, rollback); + BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, Irp, rollback); if (b) *new_address = address; @@ -1352,17 +1283,17 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i // } // #endif - if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(eit2); return FALSE; } - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); add_parents_to_cache(Vcb, insert_tp.tree); @@ -1371,13 +1302,19 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i return TRUE; } -NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) { +NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { chunk *origchunk = NULL, *c; LIST_ENTRY* le; UINT64 flags = t->flags, addr; - if (flags == 0) - flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE; + if (flags == 0) { + if (t->root->id == BTRFS_ROOT_CHUNK) + flags = BLOCK_FLAG_SYSTEM | BLOCK_FLAG_DUPLICATE; + else if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) + flags = BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA; + else + flags = BLOCK_FLAG_METADATA | BLOCK_FLAG_DUPLICATE; + } // TRACE("flags = %x\n", (UINT32)wt->flags); @@ -1395,7 +1332,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba if (t->has_address) { origchunk = get_chunk_from_address(Vcb, t->header.address); - if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, rollback)) { + if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, Irp, rollback)) { t->new_address = addr; t->has_new_address = TRUE; return STATUS_SUCCESS; @@ -1408,11 +1345,11 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba while (le != &Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) { - if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); + if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) { + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&Vcb->chunk_lock); t->new_address = addr; t->has_new_address = TRUE; @@ -1420,18 +1357,18 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba } } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le->Flink; } // allocate new chunk if necessary - if ((c = alloc_chunk(Vcb, flags, rollback))) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + if ((c = alloc_chunk(Vcb, flags))) { + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) { - if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); + if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) { + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&Vcb->chunk_lock); t->new_address = addr; t->has_new_address = TRUE; @@ -1439,7 +1376,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba } } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); } ExReleaseResourceLite(&Vcb->chunk_lock); @@ -1449,7 +1386,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba return STATUS_DISK_FULL; } -static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) { +static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; chunk* c; @@ -1459,7 +1396,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre searchkey.obj_type = TYPE_METADATA_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return FALSE; @@ -1480,13 +1417,13 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre c = get_chunk_from_address(Vcb, address); if (c) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); decrease_chunk_usage(c, Vcb->superblock.node_size); space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); } else ERR("could not find chunk for address %llx\n", address); @@ -1526,7 +1463,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre // } // } -static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, LIST_ENTRY* rollback) { +static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp, tp2, insert_tp; EXTENT_REF_V0* erv0; @@ -1538,7 +1475,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* searchkey.obj_type = TYPE_EXTENT_REF_V0; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return; @@ -1553,7 +1490,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = Vcb->superblock.node_size; - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return; @@ -1588,7 +1525,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* 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, rollback)) { + 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, Irp, rollback)) { ERR("insert_tree_item failed\n"); return; } @@ -1608,7 +1545,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* 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, rollback)) { + 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, Irp, rollback)) { ERR("insert_tree_item failed\n"); return; } @@ -1619,7 +1556,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* add_parents_to_cache(Vcb, tp2.tree); } -static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) { +static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; EXTENT_ITEM* ei; @@ -1632,7 +1569,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* 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, rollback)) { + if (reduce_tree_extent_skinny(Vcb, address, t, Irp, rollback)) { return STATUS_SUCCESS; } } @@ -1641,7 +1578,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = Vcb->superblock.node_size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1684,7 +1621,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* searchkey.obj_type = TYPE_EXTENT_REF_V0; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1708,7 +1645,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* if (!td->ignore && !td->inserted) { if (t->header.level > 0) { - convert_old_tree_extent(Vcb, td, t, rollback); + convert_old_tree_extent(Vcb, td, t, Irp, rollback); } else if (td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA)) { EXTENT_DATA* ed = (EXTENT_DATA*)td->data; @@ -1717,7 +1654,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* 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, rollback); + convert_old_data_extent(Vcb, ed2->address, ed2->size, Irp, rollback); } } } @@ -1730,20 +1667,20 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* c = get_chunk_from_address(Vcb, address); if (c) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); decrease_chunk_usage(c, tp.item->key.offset); space_list_add(Vcb, c, TRUE, address, tp.item->key.offset, rollback); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); } else ERR("could not find chunk for address %llx\n", address); return STATUS_SUCCESS; } -static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le; NTSTATUS Status; @@ -1756,7 +1693,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac if (t->write && !t->has_new_address) { chunk* c; - Status = get_tree_new_address(Vcb, t, rollback); + Status = get_tree_new_address(Vcb, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("get_tree_new_address returned %08x\n", Status); return Status; @@ -1765,7 +1702,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac TRACE("allocated extent %llx\n", t->new_address); if (t->has_address) { - Status = reduce_tree_extent(Vcb, t->header.address, t, rollback); + Status = reduce_tree_extent(Vcb, t->header.address, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -1789,7 +1726,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac return STATUS_SUCCESS; } -static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS update_root_root(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le; NTSTATUS Status; @@ -1808,7 +1745,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_ROOT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -1816,6 +1753,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { 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); + int3; return STATUS_INTERNAL_ERROR; } @@ -1838,7 +1776,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -1852,7 +1790,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { le = le->Flink; } - Status = update_chunk_caches(Vcb, rollback); + Status = update_chunk_caches(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_chunk_caches returned %08x\n", Status); return Status; @@ -1945,7 +1883,7 @@ void free_write_data_stripes(write_data_context* wtc) { } } -static NTSTATUS write_trees(device_extension* Vcb) { +static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) { UINT8 level; UINT8 *data, *body; UINT32 crc32; @@ -1996,7 +1934,7 @@ static NTSTATUS write_trees(device_extension* Vcb) { searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = Vcb->superblock.node_size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -2193,7 +2131,7 @@ static NTSTATUS write_trees(device_extension* Vcb) { *((UINT32*)data) = crc32; TRACE("setting crc32 to %08x\n", crc32); - Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL); + Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL, NULL); if (!NT_SUCCESS(Status)) { ERR("write_data returned %08x\n", Status); goto end; @@ -2240,7 +2178,7 @@ end: return Status; } -static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb) { +static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb, PIRP Irp) { KEY searchkey; traverse_ptr tp; @@ -2258,7 +2196,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s searchkey.obj_type = TYPE_ROOT_ITEM; searchkey.offset = 0xffffffffffffffff; - if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) { + if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; @@ -2270,7 +2208,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s searchkey.obj_id = BTRFS_ROOT_FSTREE; - if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) { + if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; @@ -2282,7 +2220,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s searchkey.obj_id = BTRFS_ROOT_DEVTREE; - if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) { + if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; @@ -2294,7 +2232,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s searchkey.obj_id = BTRFS_ROOT_CHECKSUM; - if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) { + if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; @@ -2309,7 +2247,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s sb->num_devices = Vcb->superblock.num_devices; } -static NTSTATUS write_superblocks(device_extension* Vcb) { +static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) { UINT64 i; NTSTATUS Status; LIST_ENTRY* le; @@ -2338,7 +2276,7 @@ static NTSTATUS write_superblocks(device_extension* Vcb) { RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup)); } - update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1]); + update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp); for (i = 0; i < Vcb->superblock.num_devices; i++) { if (Vcb->devices[i].devobj) { @@ -2353,7 +2291,7 @@ static NTSTATUS write_superblocks(device_extension* Vcb) { return STATUS_SUCCESS; } -static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, LIST_ENTRY* rollback) { +static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY *le, *le2; NTSTATUS Status; UINT64 old_size; @@ -2382,7 +2320,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex old_size = ce->old_count > 0 ? ce->old_size : ce->size; if (cer->edr.count > old_count) { - Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, rollback); + Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("increase_extent_refcount_data returned %08x\n", Status); @@ -2390,7 +2328,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex } } else if (cer->edr.count < old_count) { Status = decrease_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, - old_count - cer->edr.count, rollback); + old_count - cer->edr.count, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("decrease_extent_refcount_data returned %08x\n", Status); @@ -2407,7 +2345,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = ce->old_size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -2430,7 +2368,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex } else data = NULL; - if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -2484,7 +2422,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex return STATUS_SUCCESS; } -static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY *le = Vcb->chunks.Flink, *le2; chunk* c; KEY searchkey; @@ -2500,17 +2438,17 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) while (le != &Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); le2 = c->changed_extents.Flink; while (le2 != &c->changed_extents) { LIST_ENTRY* le3 = le2->Flink; changed_extent* ce = CONTAINING_RECORD(le2, changed_extent, list_entry); - Status = flush_changed_extent(Vcb, c, ce, rollback); + Status = flush_changed_extent(Vcb, c, ce, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("flush_changed_extent returned %08x\n", Status); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } @@ -2524,10 +2462,10 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; searchkey.offset = c->chunk_item->size; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } @@ -2535,14 +2473,14 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); int3; Status = STATUS_INTERNAL_ERROR; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) { ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM)); Status = STATUS_INTERNAL_ERROR; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } @@ -2550,7 +2488,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) if (!bgi) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } @@ -2561,11 +2499,11 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(bgi); Status = STATUS_INTERNAL_ERROR; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } @@ -2580,13 +2518,13 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) FIXME("RAID5 not yet supported\n"); ExFreePool(bgi); Status = STATUS_INTERNAL_ERROR; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) { FIXME("RAID6 not yet supported\n"); ExFreePool(bgi); Status = STATUS_INTERNAL_ERROR; - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); goto end; } else { // SINGLE Vcb->superblock.bytes_used += c->used - c->oldused; @@ -2597,7 +2535,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) c->oldused = c->used; } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le->Flink; } @@ -2605,7 +2543,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) if (flushed_extents) { ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); if (!IsListEmpty(&Vcb->sector_checksums)) { - update_checksum_tree(Vcb, rollback); + update_checksum_tree(Vcb, Irp, rollback); } ExReleaseResourceLite(&Vcb->checksum_lock); } @@ -2923,7 +2861,7 @@ static NTSTATUS STDCALL split_tree(device_extension* Vcb, tree* t) { return STATUS_SUCCESS; } -static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) { +static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le; tree_data* nextparitem = NULL; NTSTATUS Status; @@ -2956,7 +2894,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE); - Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded); + Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded, NULL); if (!NT_SUCCESS(Status)) { ERR("do_load_tree returned %08x\n", Status); return Status; @@ -3006,14 +2944,14 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* next_tree->size = 0; if (next_tree->has_new_address) { // delete associated EXTENT_ITEM - Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, rollback); + Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); return Status; } } else if (next_tree->has_address) { - Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, rollback); + Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -3107,7 +3045,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* return STATUS_SUCCESS; } -static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, LIST_ENTRY* rollback) { +static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -3117,7 +3055,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* searchkey.obj_type = TYPE_METADATA_ITEM; searchkey.offset = t->header.level; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3140,7 +3078,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(eism); return STATUS_INTERNAL_ERROR; @@ -3154,7 +3092,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3181,7 +3119,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* eit->level = level; - if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(eit); return STATUS_INTERNAL_ERROR; @@ -3195,7 +3133,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* return STATUS_INTERNAL_ERROR; } -static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS STDCALL do_splits(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { // LIST_ENTRY *le, *le2; // write_tree* wt; // tree_data* td; @@ -3249,7 +3187,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { t->root->root_item.bytes_used -= Vcb->superblock.node_size; if (t->has_new_address) { // delete associated EXTENT_ITEM - Status = reduce_tree_extent(Vcb, t->new_address, t, rollback); + Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -3258,7 +3196,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { t->has_new_address = FALSE; } else if (t->has_address) { - Status = reduce_tree_extent(Vcb,t->header.address, t, rollback); + Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -3281,7 +3219,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { free_tree(t); } else if (t->header.level != 0) { if (t->has_new_address) { - Status = update_extent_level(Vcb, t->new_address, t, 0, rollback); + Status = update_extent_level(Vcb, t->new_address, t, 0, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_extent_level returned %08x\n", Status); @@ -3324,7 +3262,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { t = CONTAINING_RECORD(le, tree, list_entry); if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size) { - Status = try_tree_amalgamate(Vcb, t, rollback); + Status = try_tree_amalgamate(Vcb, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("try_tree_amalgamate returned %08x\n", Status); return Status; @@ -3362,7 +3300,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { TRACE("deleting top-level tree in root %llx with one item\n", t->root->id); if (t->has_new_address) { // delete associated EXTENT_ITEM - Status = reduce_tree_extent(Vcb, t->new_address, t, rollback); + Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -3371,7 +3309,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { t->has_new_address = FALSE; } else if (t->has_address) { - Status = reduce_tree_extent(Vcb,t->header.address, t, rollback); + Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent returned %08x\n", Status); @@ -3385,7 +3323,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { KEY searchkey = {0,0,0}; traverse_ptr tp; - Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3416,12 +3354,12 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } -static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, LIST_ENTRY* rollback) { +static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; if (level > 0) { if (!th->tree) { - Status = load_tree(Vcb, th->address, r, &th->tree, NULL); + Status = load_tree(Vcb, th->address, r, &th->tree, NULL, NULL); if (!NT_SUCCESS(Status)) { ERR("load_tree(%llx) returned %08x\n", th->address, Status); @@ -3436,7 +3374,7 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); if (!td->ignore) { - Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, rollback); + Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("remove_root_extents returned %08x\n", Status); @@ -3450,7 +3388,7 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* } if (!th->tree || th->tree->has_address) { - Status = reduce_tree_extent(Vcb, th->address, NULL, rollback); + Status = reduce_tree_extent(Vcb, th->address, NULL, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("reduce_tree_extent(%llx) returned %08x\n", th->address, Status); @@ -3461,12 +3399,12 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* return STATUS_SUCCESS; } -static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) { +static NTSTATUS drop_root(device_extension* Vcb, root* r, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; - Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, rollback); + Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("remove_root_extents returned %08x\n", Status); return Status; @@ -3479,7 +3417,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64)); if (searchkey.obj_id != 0 || searchkey.offset != 0) { - Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { WARN("find_item returned %08x\n", Status); } else { @@ -3497,7 +3435,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) searchkey.obj_type = TYPE_ROOT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("find_item returned %08x\n", Status); return Status; @@ -3515,7 +3453,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) return STATUS_SUCCESS; } -static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS drop_roots(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2; NTSTATUS Status; @@ -3524,7 +3462,7 @@ static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) { le2 = le->Flink; - Status = drop_root(Vcb, r, rollback); + Status = drop_root(Vcb, r, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("drop_root(%llx) returned %08x\n", r->id, Status); return Status; @@ -3536,7 +3474,7 @@ static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } -static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) { +static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) { CHUNK_ITEM* ci; CHUNK_ITEM_STRIPE* cis; BLOCK_GROUP_ITEM* bgi; @@ -3551,7 +3489,7 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba RtlCopyMemory(ci, c->chunk_item, c->size); - if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(ci); return STATUS_INTERNAL_ERROR; @@ -3577,7 +3515,7 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba bgi->chunk_tree = 0x100; bgi->flags = c->chunk_item->type; - if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(bgi); return STATUS_INSUFFICIENT_RESOURCES; @@ -3609,14 +3547,14 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba de->length = c->chunk_item->size / factor; de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid; - if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(de); return STATUS_INTERNAL_ERROR; } // FIXME - no point in calling this twice for the same device - Status = update_dev_item(Vcb, c->devices[i], rollback); + Status = update_dev_item(Vcb, c->devices[i], Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_dev_item returned %08x\n", Status); return Status; @@ -3651,7 +3589,7 @@ static void remove_from_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 ob } } -static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) { +static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; KEY searchkey; traverse_ptr tp; @@ -3664,7 +3602,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback if (c->cache) { c->cache->deleted = TRUE; - flush_fcb(c->cache, TRUE, rollback); + flush_fcb(c->cache, TRUE, Irp, rollback); free_fcb(c->cache); @@ -3672,7 +3610,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback searchkey.obj_type = 0; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3698,7 +3636,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback searchkey.obj_type = TYPE_DEV_EXTENT; searchkey.offset = cis[i].offset; - Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3734,7 +3672,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback searchkey.obj_type = TYPE_DEV_ITEM; searchkey.offset = c->devices[i]->devitem.dev_id; - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3755,7 +3693,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM)); - if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); return STATUS_INTERNAL_ERROR; } @@ -3773,7 +3711,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback searchkey.obj_type = TYPE_CHUNK_ITEM; searchkey.offset = c->offset; - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3789,7 +3727,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -3826,16 +3764,15 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback ExFreePool(s); } - ExDeleteResourceLite(&c->nonpaged->lock); - ExDeleteResourceLite(&c->nonpaged->changed_extents_lock); - ExFreePool(c->nonpaged); + ExDeleteResourceLite(&c->lock); + ExDeleteResourceLite(&c->changed_extents_lock); ExFreePool(c); return STATUS_SUCCESS; } -static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS update_chunks(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY *le = Vcb->chunks_changed.Flink, *le2; NTSTATUS Status; UINT64 used_minus_cache; @@ -3849,7 +3786,7 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) { le2 = le->Flink; - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); used_minus_cache = c->used; @@ -3887,25 +3824,25 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) { } if (used_minus_cache == 0) { - Status = drop_chunk(Vcb, c, rollback); + Status = drop_chunk(Vcb, c, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("drop_chunk returned %08x\n", Status); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&Vcb->chunk_lock); return Status; } } else if (c->created) { - Status = create_chunk(Vcb, c, rollback); + Status = create_chunk(Vcb, c, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("create_chunk returned %08x\n", Status); - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&Vcb->chunk_lock); return Status; } } if (used_minus_cache > 0) - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le2; } @@ -3915,10 +3852,10 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } -static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) { +static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; - ULONG xasize; + ULONG xasize, maxlen; DIR_ITEM* xa; NTSTATUS Status; @@ -3928,15 +3865,14 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; } xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen; - - // FIXME - make sure xasize not too big + maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node); if (!keycmp(&tp.item->key, &searchkey)) { // key exists UINT8* newdata; @@ -3961,6 +3897,12 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in UINT64 pos; // replace + + if (tp.item->size + xasize - oldxasize > maxlen) { + ERR("DIR_ITEM would be over maximum size (%u + %u - %u > %u)\n", tp.item->size, xasize, oldxasize, maxlen); + return STATUS_INTERNAL_ERROR; + } + newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG); if (!newdata) { ERR("out of memory\n"); @@ -3989,13 +3931,19 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in RtlCopyMemory(xa->name + strlen(name), data, datalen); delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, Irp, rollback); break; } - if (xa->m + xa->n >= size) { // FIXME - test this works + if ((UINT8*)xa - (UINT8*)tp.item->data + oldxasize >= size) { // not found, add to end of data + + if (tp.item->size + xasize > maxlen) { + ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", tp.item->size, xasize, maxlen); + return STATUS_INTERNAL_ERROR; + } + newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG); if (!newdata) { ERR("out of memory\n"); @@ -4016,7 +3964,7 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in RtlCopyMemory(xa->name + strlen(name), data, datalen); delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, Irp, rollback); break; } else { @@ -4026,7 +3974,10 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in } } } else { - // add new DIR_ITEM struct + if (xasize > maxlen) { + ERR("DIR_ITEM would be over maximum size (%u > %u)\n", xasize, maxlen); + return STATUS_INTERNAL_ERROR; + } xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG); if (!xa) { @@ -4044,13 +3995,13 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in 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, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, Irp, rollback); } return STATUS_SUCCESS; } -static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) { +static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; DIR_ITEM* xa; @@ -4062,7 +4013,7 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = crc32; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return FALSE; @@ -4120,7 +4071,7 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino 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, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, Irp, rollback); return TRUE; @@ -4143,7 +4094,40 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino } } -void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { +static NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) { + EXTENT_DATA* ed; + EXTENT_DATA2* ed2; + + TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length); + + ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); + if (!ed) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ed->generation = fcb->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(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, Irp, rollback)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + +void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback) { traverse_ptr tp; KEY searchkey; NTSTATUS Status; @@ -4169,9 +4153,9 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { if (fcb->ads) { if (fcb->deleted) - delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, rollback); + delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, Irp, rollback); else { - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, rollback); + Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("set_xattr returned %08x\n", Status); goto end; @@ -4188,7 +4172,8 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { BOOL b; traverse_ptr next_tp; LIST_ENTRY* le; - BOOL prealloc = FALSE; + BOOL prealloc = FALSE, extents_inline = FALSE; + UINT64 last_end; // delete ignored extent items le = fcb->extents.Flink; @@ -4217,10 +4202,8 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data; EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->data->data; - if (ed2->address == 0 && ned2->address == 0 && ed2->size == 0 && ned2->size == 0) { - // FIXME - merge together adjacent sparse extents - } else if (ed2->address == ned2->address && ed2->size == ned2->size && nextext->offset == ext->offset + ed2->num_bytes && - ned2->offset == ed2->offset + ed2->num_bytes) { + if (ed2->size != 0 && ed2->address == ned2->address && ed2->size == ned2->size && + nextext->offset == ext->offset + ed2->num_bytes && ned2->offset == ed2->offset + ed2->num_bytes) { chunk* c; ext->data->generation = fcb->Vcb->superblock.generation; @@ -4236,7 +4219,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { ERR("get_chunk_from_address(%llx) failed\n", ed2->address); } else { Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); if (!NT_SUCCESS(Status)) { ERR("update_changed_extent_ref returned %08x\n", Status); goto end; @@ -4257,7 +4240,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_EXTENT_DATA; searchkey.offset = 0; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -4267,7 +4250,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) delete_tree_item(fcb->Vcb, &tp, rollback); - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp); if (b) { tp = next_tp; @@ -4277,40 +4260,74 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { } } while (b); - // add new EXTENT_DATAs - - le = fcb->extents.Flink; - while (le != &fcb->extents) { - extent* ext = CONTAINING_RECORD(le, extent, list_entry); - EXTENT_DATA* ed; + if (!fcb->deleted) { + // add new EXTENT_DATAs + + last_end = 0; + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + EXTENT_DATA* ed; - ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!ed) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; + if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && ext->offset > last_end) { + Status = insert_sparse_extent(fcb, last_end, ext->offset - last_end, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_sparse_extent returned %08x\n", Status); + goto end; + } + } + + ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!ed) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(ed, ext->data, ext->datalen); + + if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, Irp, rollback)) { + ERR("insert_tree_item failed\n"); + goto end; + } + + if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC) + prealloc = TRUE; + + if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_INLINE) + extents_inline = TRUE; + + if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) { + if (ed->type == EXTENT_TYPE_INLINE) + last_end = ext->offset + ed->decoded_size; + else { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; + + last_end = ext->offset + ed2->num_bytes; + } + } + + le = le->Flink; } - RtlCopyMemory(ed, ext->data, ext->datalen); - - if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - goto end; + if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && !extents_inline && + sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) { + Status = insert_sparse_extent(fcb, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_sparse_extent returned %08x\n", Status); + goto end; + } } - if (!prealloc && ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC) - prealloc = TRUE; + // update prealloc flag in INODE_ITEM - le = le->Flink; + if (!prealloc) + fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC; + else + fcb->inode_item.flags |= BTRFS_INODE_PREALLOC; } - // update prealloc flag in INODE_ITEM - - if (!prealloc) - fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC; - else - fcb->inode_item.flags |= BTRFS_INODE_PREALLOC; - fcb->extents_changed = FALSE; } @@ -4319,7 +4336,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -4335,7 +4352,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { + if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); goto end; } @@ -4362,7 +4379,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = ii_offset; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -4395,13 +4412,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = 0; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; } - while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) { + while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE, Irp)) { tp = tp2; if (tp.item->key.obj_id == fcb->inode) { @@ -4426,14 +4443,14 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, rollback)) { + if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); goto end; } } if (fcb->sd_dirty) { - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback); + Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("set_xattr returned %08x\n", Status); } @@ -4448,13 +4465,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { TRACE("inserting new DOSATTRIB xattr\n"); sprintf(val, "0x%lx", fcb->atts); - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback); + Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("set_xattr returned %08x\n", Status); goto end; } } else - delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback); + delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, Irp, rollback); fcb->atts_changed = FALSE; fcb->atts_deleted = FALSE; @@ -4462,13 +4479,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { if (fcb->reparse_xattr_changed) { if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) { - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, rollback); + Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("set_xattr returned %08x\n", Status); goto end; } } else - delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, rollback); + delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, Irp, rollback); fcb->reparse_xattr_changed = FALSE; } @@ -4480,7 +4497,7 @@ end: return; } -static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) { +static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -4489,7 +4506,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p searchkey.obj_type = TYPE_ROOT_REF; searchkey.offset = subvolid; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -4543,7 +4560,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p 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, rollback); + insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, Irp, rollback); } break; @@ -4564,7 +4581,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p return STATUS_SUCCESS; } -static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) { +static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; @@ -4573,7 +4590,7 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars searchkey.obj_type = TYPE_ROOT_REF; searchkey.offset = subvolid; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -4597,13 +4614,13 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars delete_tree_item(Vcb, &tp, rollback); - if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); ExFreePool(rr2); 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, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); ExFreePool(rr); return STATUS_INTERNAL_ERROR; @@ -4613,7 +4630,7 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars return STATUS_SUCCESS; } -static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) { +static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; UINT8* data; @@ -4624,7 +4641,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol searchkey.obj_type = TYPE_ROOT_REF; searchkey.offset = subvolid; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -4648,9 +4665,13 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol searchkey.obj_type = TYPE_ROOT_BACKREF; searchkey.offset = parsubvolid; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); + + if (datalen > 0) + ExFreePool(data); + return Status; } @@ -4658,7 +4679,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol delete_tree_item(Vcb, &tp, rollback); if (datalen > 0) { - if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp, rollback)) { ERR("error - failed to insert item\n"); ExFreePool(data); return STATUS_INTERNAL_ERROR; @@ -4668,7 +4689,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol return STATUS_SUCCESS; } -static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { +static NTSTATUS flush_fileref(file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; // if fileref created and then immediately deleted, do nothing @@ -4720,20 +4741,20 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { RtlCopyMemory(di2, di, disize); - if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, rollback)) { + if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); Status = STATUS_INTERNAL_ERROR; return Status; } - Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, rollback); + Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("add_dir_item returned %08x\n", Status); return Status; } if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { - Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback); + Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("add_inode_ref returned %08x\n", Status); return Status; @@ -4755,13 +4776,13 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { rr->n = fileref->utf8.Length; RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length); - Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback); + Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("add_root_ref returned %08x\n", Status); return Status; } - Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback); + Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_root_backref returned %08x\n", Status); return Status; @@ -4786,7 +4807,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { // delete DIR_ITEM (0x54) - Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, rollback); + Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_dir_item returned %08x\n", Status); return Status; @@ -4795,18 +4816,18 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { // delete INODE_REF (0xc) - Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, rollback); + Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_inode_ref returned %08x\n", Status); return Status; } } else { // subvolume - Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, rollback); + Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("delete_root_ref returned %08x\n", Status); } - Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback); + Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_root_backref returned %08x\n", Status); return Status; @@ -4819,7 +4840,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_DIR_INDEX; searchkey.offset = fileref->index; - Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); Status = STATUS_INTERNAL_ERROR; @@ -4835,145 +4856,150 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { ExFreePool(fileref->oldutf8.Buffer); fileref->oldutf8.Buffer = NULL; } - } else { // rename - if (fileref->oldutf8.Buffer) { - UINT32 crc32, oldcrc32; - ULONG disize; - DIR_ITEM *di, *di2; - KEY searchkey; - traverse_ptr tp; - - crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length); + } else { // rename or change type + PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->utf8; + UINT32 crc32, oldcrc32; + ULONG disize; + DIR_ITEM *di, *di2; + KEY searchkey; + traverse_ptr tp; + + crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length); + + if (!fileref->oldutf8.Buffer) + oldcrc32 = crc32; + else oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->oldutf8.Buffer, fileref->oldutf8.Length); - // delete DIR_ITEM (0x54) + // delete DIR_ITEM (0x54) + + Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, oldutf8, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("delete_dir_item returned %08x\n", Status); + return Status; + } + + // add DIR_ITEM (0x54) + + disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length; + di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); + if (!di) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); + if (!di2) { + ERR("out of memory\n"); + ExFreePool(di); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { + di->key.obj_id = fileref->fcb->inode; + di->key.obj_type = TYPE_INODE_ITEM; + di->key.offset = 0; + } else { // subvolume + di->key.obj_id = fileref->fcb->subvol->id; + di->key.obj_type = TYPE_ROOT_ITEM; + di->key.offset = 0xffffffffffffffff; + } + + di->transid = fileref->fcb->Vcb->superblock.generation; + di->m = 0; + di->n = (UINT16)fileref->utf8.Length; + di->type = fileref->fcb->type; + RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length); + + RtlCopyMemory(di2, di, disize); + + Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("add_dir_item returned %08x\n", Status); + return Status; + } + + if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { + // delete INODE_REF (0xc) - Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->oldutf8, rollback); + Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, oldutf8, Irp, rollback); if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); + ERR("delete_inode_ref returned %08x\n", Status); return Status; } - // add DIR_ITEM (0x54) + // add INODE_REF (0xc) - disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length; - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { + Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("add_inode_ref returned %08x\n", Status); + return Status; + } + } else { // subvolume + ULONG rrlen; + ROOT_REF* rr; + + // FIXME - make sure this works with duff subvols within snapshots + + Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("delete_root_ref returned %08x\n", Status); + } + + rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length; + + rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG); + if (!rr) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } - di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di2) { - ERR("out of memory\n"); - ExFreePool(di); - return STATUS_INSUFFICIENT_RESOURCES; - } + rr->dir = fileref->parent->fcb->inode; + rr->index = fileref->index; + rr->n = fileref->utf8.Length; + RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length); - if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { - di->key.obj_id = fileref->fcb->inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - } else { // subvolume - di->key.obj_id = fileref->fcb->subvol->id; - di->key.obj_type = TYPE_ROOT_ITEM; - di->key.offset = 0xffffffffffffffff; - } - - di->transid = fileref->fcb->Vcb->superblock.generation; - di->m = 0; - di->n = (UINT16)fileref->utf8.Length; - di->type = fileref->fcb->type; - RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length); - - RtlCopyMemory(di2, di, disize); - - Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, rollback); + Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback); if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); + ERR("add_root_ref returned %08x\n", Status); return Status; } - if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { - // delete INODE_REF (0xc) - - Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, &fileref->oldutf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_inode_ref returned %08x\n", Status); - return Status; - } - - // add INODE_REF (0xc) - - Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_inode_ref returned %08x\n", Status); - return Status; - } - } else { // subvolume - ULONG rrlen; - ROOT_REF* rr; - - // FIXME - make sure this works with duff subvols within snapshots - - Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->oldutf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_root_ref returned %08x\n", Status); - } - - rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length; - - rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG); - if (!rr) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - rr->dir = fileref->parent->fcb->inode; - rr->index = fileref->index; - rr->n = fileref->utf8.Length; - RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length); - - Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_root_ref returned %08x\n", Status); - return Status; - } - - Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_root_backref returned %08x\n", Status); - return Status; - } - } - - // delete DIR_INDEX (0x60) - - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = fileref->index; - - Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); + Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback); if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - Status = STATUS_INTERNAL_ERROR; - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - delete_tree_item(fileref->fcb->Vcb, &tp, rollback); - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } else - WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id); - - // add DIR_INDEX (0x60) - - if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; + ERR("update_root_backref returned %08x\n", Status); return Status; } + } + + // delete DIR_INDEX (0x60) + + searchkey.obj_id = fileref->parent->fcb->inode; + searchkey.obj_type = TYPE_DIR_INDEX; + searchkey.offset = fileref->index; + + Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + Status = STATUS_INTERNAL_ERROR; + return Status; + } + + if (!keycmp(&searchkey, &tp.item->key)) { + delete_tree_item(fileref->fcb->Vcb, &tp, rollback); + TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + } else + WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id); + + // add DIR_INDEX (0x60) + + if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, Irp, rollback)) { + ERR("insert_tree_item failed\n"); + Status = STATUS_INTERNAL_ERROR; + return Status; + } + if (fileref->oldutf8.Buffer) { ExFreePool(fileref->oldutf8.Buffer); fileref->oldutf8.Buffer = NULL; } @@ -4984,7 +5010,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } -static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback) { +static void convert_shared_data_refs(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le; NTSTATUS Status; @@ -5009,18 +5035,18 @@ static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback TRACE("tree %llx; root %llx, objid %llx, offset %llx, count %x\n", t->header.address, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count); - Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, rollback); + Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, Irp, rollback); if (!NT_SUCCESS(Status)) WARN("increase_extent_refcount_data returned %08x\n", Status); if (old) { - Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, rollback); + Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, Irp, rollback); if (!NT_SUCCESS(Status)) WARN("decrease_extent_refcount_old returned %08x\n", Status); } else { - Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, rollback); + Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, Irp, rollback); if (!NT_SUCCESS(Status)) WARN("decrease_extent_refcount_shared_data returned %08x\n", Status); @@ -5042,7 +5068,82 @@ static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback } } -NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { +static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp, LIST_ENTRY* rollback) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + + searchkey.obj_id = root; + searchkey.obj_type = TYPE_ROOT_ITEM; + searchkey.offset = 0xffffffffffffffff; + + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + 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); + int3; + 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); + if (!ri) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (tp.item->size > 0) + 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, rollback); + + if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + } else { + tp.tree->write = TRUE; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS add_root_items_to_cache(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { + LIST_ENTRY* le; + NTSTATUS Status; + + le = Vcb->trees.Flink; + while (le != &Vcb->trees) { + tree* t = CONTAINING_RECORD(le, tree, list_entry); + + if (t->write && t->root != Vcb->chunk_root && t->root != Vcb->root_root) { + Status = add_root_item_to_cache(Vcb, t->root->id, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("add_root_item_to_cache returned %08x\n", Status); + return Status; + } + } + + le = le->Flink; + } + + // make sure we always update the extent tree + Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("add_root_item_to_cache returned %08x\n", Status); + return Status; + } + + return STATUS_SUCCESS; +} + +NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; LIST_ENTRY* le; BOOL cache_changed = FALSE; @@ -5060,11 +5161,32 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry); - flush_fileref(dirt->fileref, rollback); + flush_fileref(dirt->fileref, Irp, rollback); free_fileref(dirt->fileref); ExFreePool(dirt); } + // We process deleted streams first, so we don't run over our xattr + // limit unless we absolutely have to. + + le = Vcb->dirty_fcbs.Flink; + while (le != &Vcb->dirty_fcbs) { + dirty_fcb* dirt; + LIST_ENTRY* le2 = le->Flink; + + dirt = CONTAINING_RECORD(le, dirty_fcb, list_entry); + + if (dirt->fcb->deleted && dirt->fcb->ads) { + RemoveEntryList(le); + + flush_fcb(dirt->fcb, FALSE, Irp, rollback); + free_fcb(dirt->fcb); + ExFreePool(dirt); + } + + le = le2; + } + le = Vcb->dirty_fcbs.Flink; while (le != &Vcb->dirty_fcbs) { dirty_fcb* dirt; @@ -5075,7 +5197,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { if (dirt->fcb->subvol != Vcb->root_root || dirt->fcb->deleted) { RemoveEntryList(le); - flush_fcb(dirt->fcb, FALSE, rollback); + flush_fcb(dirt->fcb, FALSE, Irp, rollback); free_fcb(dirt->fcb); ExFreePool(dirt); } @@ -5083,16 +5205,16 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { le = le2; } - convert_shared_data_refs(Vcb, rollback); + convert_shared_data_refs(Vcb, Irp, rollback); ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); if (!IsListEmpty(&Vcb->sector_checksums)) { - update_checksum_tree(Vcb, rollback); + update_checksum_tree(Vcb, Irp, rollback); } ExReleaseResourceLite(&Vcb->checksum_lock); if (!IsListEmpty(&Vcb->drop_roots)) { - Status = drop_roots(Vcb, rollback); + Status = drop_roots(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("drop_roots returned %08x\n", Status); @@ -5101,7 +5223,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { } if (!IsListEmpty(&Vcb->chunks_changed)) { - Status = update_chunks(Vcb, rollback); + Status = update_chunks(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_chunks returned %08x\n", Status); @@ -5120,7 +5242,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = 0; searchkey.offset = 0; - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; @@ -5129,6 +5251,12 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { Vcb->root_root->treeholder.tree->write = TRUE; } + Status = add_root_items_to_cache(Vcb, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("add_root_items_to_cache returned %08x\n", Status); + return Status; + } + do { Status = add_parents(Vcb, rollback); if (!NT_SUCCESS(Status)) { @@ -5136,25 +5264,25 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { goto end; } - Status = do_splits(Vcb, rollback); + Status = do_splits(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("do_splits returned %08x\n", Status); goto end; } - Status = allocate_tree_extents(Vcb, rollback); + Status = allocate_tree_extents(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("add_parents returned %08x\n", Status); goto end; } - Status = update_chunk_usage(Vcb, rollback); + Status = update_chunk_usage(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_chunk_usage returned %08x\n", Status); goto end; } - Status = allocate_cache(Vcb, &cache_changed, rollback); + Status = allocate_cache(Vcb, &cache_changed, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("allocate_cache returned %08x\n", Status); goto end; @@ -5174,13 +5302,13 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { TRACE("trees consistent\n"); - Status = update_root_root(Vcb, rollback); + Status = update_root_root(Vcb, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("update_root_root returned %08x\n", Status); goto end; } - Status = write_trees(Vcb); + Status = write_trees(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("write_trees returned %08x\n", Status); goto end; @@ -5188,7 +5316,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { Vcb->superblock.cache_generation = Vcb->superblock.generation; - Status = write_superblocks(Vcb); + Status = write_superblocks(Vcb, Irp); if (!NT_SUCCESS(Status)) { ERR("write_superblocks returned %08x\n", Status); goto end; @@ -5212,7 +5340,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_METADATA_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); int3; @@ -5223,7 +5351,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); int3; @@ -5311,7 +5439,7 @@ static changed_extent* get_changed_extent_item(chunk* c, UINT64 address, UINT64 } NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, signed long long count, - BOOL no_csum, UINT64 new_size) { + BOOL no_csum, UINT64 new_size, PIRP Irp) { LIST_ENTRY* le; changed_extent* ce; changed_extent_ref* cer; @@ -5320,7 +5448,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre traverse_ptr tp; UINT64 old_count; - ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE); + ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE); ce = get_changed_extent_item(c, address, size, no_csum); @@ -5335,7 +5463,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre searchkey.obj_type = TYPE_EXTENT_ITEM; searchkey.offset = 0xffffffffffffffff; - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto end; @@ -5384,7 +5512,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre le = le->Flink; } - old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset); + old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset, Irp); if (old_count > 0) { cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); @@ -5423,12 +5551,12 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre Status = STATUS_SUCCESS; end: - ExReleaseResourceLite(&c->nonpaged->changed_extents_lock); + ExReleaseResourceLite(&c->changed_extents_lock); return Status; } -NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) { +NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; LIST_ENTRY* le; @@ -5460,28 +5588,10 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - if (ext->offset < end_data && ext->offset + len >= 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, ext->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; - } - + if (ext->offset < end_data && ext->offset + len > start_data) { if (ed->type == EXTENT_TYPE_INLINE) { if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); fcb->inode_item.st_blocks -= len; } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning @@ -5522,7 +5632,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT newext->ignore = FALSE; InsertHeadList(&ext->list_entry, &newext->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); fcb->inode_item.st_blocks -= end_data - ext->offset; } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end @@ -5563,7 +5673,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT newext->ignore = FALSE; InsertHeadList(&ext->list_entry, &newext->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); fcb->inode_item.st_blocks -= ext->offset + len - start_data; } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle @@ -5642,7 +5752,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT InsertHeadList(&ext->list_entry, &newext1->list_entry); InsertHeadList(&newext1->list_entry, &newext2->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); fcb->inode_item.st_blocks -= end_data - start_data; } @@ -5659,7 +5769,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT ERR("get_chunk_from_address(%llx) failed\n", ed2->address); } else { Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); if (!NT_SUCCESS(Status)) { ERR("update_changed_extent_ref returned %08x\n", Status); goto end; @@ -5667,7 +5777,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT } } - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning EXTENT_DATA* ned; EXTENT_DATA2* ned2; @@ -5711,7 +5821,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT newext->ignore = FALSE; InsertHeadList(&ext->list_entry, &newext->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end EXTENT_DATA* ned; EXTENT_DATA2* ned2; @@ -5755,7 +5865,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT newext->ignore = FALSE; InsertHeadList(&ext->list_entry, &newext->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle EXTENT_DATA *neda, *nedb; EXTENT_DATA2 *neda2, *nedb2; @@ -5772,7 +5882,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT ERR("get_chunk_from_address(%llx) failed\n", ed2->address); } else { Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); if (!NT_SUCCESS(Status)) { ERR("update_changed_extent_ref returned %08x\n", Status); goto end; @@ -5855,7 +5965,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT InsertHeadList(&ext->list_entry, &newext1->list_entry); InsertHeadList(&newext1->list_entry, &newext2->list_entry); - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); } } } @@ -5880,7 +5990,7 @@ static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, changed_sector* sc; int i; - Status = write_data_complete(Vcb, address, data, length, Irp); + Status = write_data_complete(Vcb, address, data, length, Irp, NULL); if (!NT_SUCCESS(Status)) { ERR("write_data returned %08x\n", Status); return Status; @@ -5914,6 +6024,21 @@ static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, return STATUS_SUCCESS; } +static void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) { + rollback_extent* re; + + re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG); + if (!re) { + ERR("out of memory\n"); + return; + } + + re->fcb = fcb; + re->ext = ext; + + add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re); +} + static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG edsize, BOOL unique, LIST_ENTRY* rollback) { extent* ext; LIST_ENTRY* le; @@ -5947,15 +6072,27 @@ static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG ed InsertTailList(&fcb->extents, &ext->list_entry); end: - add_rollback(rollback, ROLLBACK_INSERT_EXTENT, ext); + add_insert_extent_rollback(rollback, fcb, ext); return TRUE; } -void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback) { +static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) { if (!ext->ignore) { + rollback_extent* re; + ext->ignore = TRUE; - add_rollback(rollback, ROLLBACK_DELETE_EXTENT, ext); + + re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG); + if (!re) { + ERR("out of memory\n"); + return; + } + + re->fcb = fcb; + re->ext = ext; + + add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re); } } @@ -6002,7 +6139,7 @@ static void add_changed_extent_ref(chunk* c, UINT64 address, UINT64 size, UINT64 } BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, - LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size) { UINT64 address; NTSTATUS Status; EXTENT_DATA* ed; @@ -6048,8 +6185,8 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start } ed->generation = Vcb->superblock.generation; - ed->decoded_size = length; - ed->compression = BTRFS_COMPRESSION_NONE; + ed->decoded_size = decoded_size; + ed->compression = compression; ed->encryption = BTRFS_ENCRYPTION_NONE; ed->encoding = BTRFS_ENCODING_NONE; ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR; @@ -6058,7 +6195,7 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start ed2->address = address; ed2->size = length; ed2->offset = 0; - ed2->num_bytes = length; + ed2->num_bytes = decoded_size; if (!add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, rollback)) { ERR("add_extent_to_fcb failed\n"); @@ -6069,16 +6206,16 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start increase_chunk_usage(c, length); space_list_subtract(Vcb, c, FALSE, address, length, rollback); - fcb->inode_item.st_blocks += length; + fcb->inode_item.st_blocks += decoded_size; fcb->extents_changed = TRUE; mark_fcb_dirty(fcb); - ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE); + ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE); add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM); - ExReleaseResourceLite(&c->nonpaged->changed_extents_lock); + ExReleaseResourceLite(&c->changed_extents_lock); return TRUE; } @@ -6086,14 +6223,42 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, extent* ext, chunk* c, PIRP Irp, LIST_ENTRY* rollback) { EXTENT_DATA* ed; - EXTENT_DATA2* ed2; + EXTENT_DATA2 *ed2, *ed2orig; extent* newext; - UINT64 addr; + UINT64 addr, origsize; NTSTATUS Status; + LIST_ENTRY* le; TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list, ext, c, rollback); + ed2orig = (EXTENT_DATA2*)ext->data->data; + + origsize = ed2orig->size; + addr = ed2orig->address + ed2orig->size; + + Status = write_data_complete(Vcb, addr, data, length, Irp, c); + if (!NT_SUCCESS(Status)) { + ERR("write_data returned %08x\n", Status); + return FALSE; + } + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext2 = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext2->ignore && (ext2->data->type == EXTENT_TYPE_REGULAR || ext2->data->type == EXTENT_TYPE_PREALLOC)) { + EXTENT_DATA2* ed2b = (EXTENT_DATA2*)ext2->data->data; + + if (ed2b->address == ed2orig->address) { + ed2b->size = origsize + length; + ext2->data->decoded_size = origsize + length; + } + } + + le = le->Flink; + } + ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); if (!ed) { ERR("out of memory\n"); @@ -6108,32 +6273,21 @@ static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT } RtlCopyMemory(ed, ext->data, ext->datalen); - - ed->decoded_size += length; + ed2 = (EXTENT_DATA2*)ed->data; - - addr = ed2->address + ed2->size; - - Status = write_data_complete(Vcb, addr, data, length, Irp); - if (!NT_SUCCESS(Status)) { - ERR("write_data returned %08x\n", Status); - ExFreePool(newext); - ExFreePool(ed); - return FALSE; - } - - ed2->size += length; - ed2->num_bytes += length; + ed2->offset = ed2orig->offset + ed2orig->num_bytes; + ed2->num_bytes = length; RtlCopyMemory(newext, ext, sizeof(extent)); + newext->offset = ext->offset + ed2orig->num_bytes; newext->data = ed; InsertHeadList(&ext->list_entry, &newext->list_entry); - remove_fcb_extent(ext, rollback); + add_insert_extent_rollback(rollback, fcb, newext); - Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size - length, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 0, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); + Status = update_changed_extent_ref(Vcb, c, ed2orig->address, origsize, fcb->subvol->id, fcb->inode, newext->offset - ed2->offset, + 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); if (!NT_SUCCESS(Status)) { ERR("update_changed_extent_ref returned %08x\n", Status); @@ -6167,7 +6321,7 @@ static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT increase_chunk_usage(c, length); - space_list_subtract(Vcb, c, FALSE, addr, length, rollback); + space_list_subtract(Vcb, c, FALSE, addr, length, NULL); // no rollback as we don't reverse extending the extent fcb->inode_item.st_blocks += length; @@ -6202,68 +6356,66 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, le = le->Flink; } - if (!ext) { - WARN("previous EXTENT_DATA not found\n"); - goto end; - } - + if (!ext) + return FALSE; + if (!ext->unique) { TRACE("extent was not unique\n"); - goto end; + return FALSE; } ed = ext->data; if (ext->datalen < sizeof(EXTENT_DATA)) { ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - goto end; + return FALSE; } if (ed->type != EXTENT_TYPE_REGULAR) { TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n"); - goto end; + return FALSE; } ed2 = (EXTENT_DATA2*)ed->data; if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - goto end; + return FALSE; } if (ext->offset + ed2->num_bytes != start_data) { TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data); - goto end; + return FALSE; } if (ed->compression != BTRFS_COMPRESSION_NONE) { - FIXME("FIXME: compression not yet supported\n"); - goto end; + TRACE("not extending a compressed extent\n"); + return FALSE; } if (ed->encryption != BTRFS_ENCRYPTION_NONE) { WARN("encryption not supported\n"); - goto end; + return FALSE; } if (ed->encoding != BTRFS_ENCODING_NONE) { WARN("other encodings not supported\n"); - goto end; + return FALSE; } if (ed2->size - ed2->offset != ed2->num_bytes) { TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n"); - goto end; + return FALSE; } if (ed2->size >= MAX_EXTENT_SIZE) { TRACE("extent size was too large to extend (%llx >= %llx)\n", ed2->size, (UINT64)MAX_EXTENT_SIZE); - goto end; + return FALSE; } c = get_chunk_from_address(Vcb, ed2->address); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); le = c->space.Flink; while (le != &c->space) { @@ -6284,10 +6436,8 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, le = le->Flink; } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); -end: - return success; } @@ -6300,47 +6450,56 @@ static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LI UINT64 flags, origlength = length; #endif NTSTATUS Status; + BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE; flags = fcb->Vcb->data_flags; // FIXME - try and maximize contiguous ranges first. If we can't do that, // allocate all the free space we find until it's enough. - ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); - do { UINT64 extlen = min(MAX_EXTENT_SIZE, length); + ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); + le = fcb->Vcb->chunks.Flink; while (le != &fcb->Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) { + ExReleaseResourceLite(&c->lock); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); goto cont; } } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le->Flink; } - if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + if ((c = alloc_chunk(fcb->Vcb, flags))) { + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) { - ExReleaseResourceLite(&c->nonpaged->lock); + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) { + ExReleaseResourceLite(&c->lock); goto cont; } } - ExReleaseResourceLite(&c->nonpaged->lock); - } + ExReleaseResourceLite(&c->lock); + } else + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); WARN("couldn't find any data chunks with %llx bytes free\n", origlength); Status = STATUS_DISK_FULL; @@ -6354,47 +6513,9 @@ cont: Status = STATUS_SUCCESS; end: - ExReleaseResourceLite(&fcb->Vcb->chunk_lock); - return Status; } -NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { - EXTENT_DATA* ed; - EXTENT_DATA2* ed2; - - TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length); - - ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); - if (!ed) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - ed->generation = fcb->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 (!add_extent_to_fcb(fcb, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), FALSE, rollback)) { - ERR("add_extent_to_fcb failed\n"); - return STATUS_INTERNAL_ERROR; - } - - fcb->extents_changed = TRUE; - mark_fcb_dirty(fcb); - - return STATUS_SUCCESS; -} - // static void print_tree(tree* t) { // LIST_ENTRY* le = t->itemlist.Flink; // while (le != &t->itemlist) { @@ -6425,61 +6546,8 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6 } } - // if there is a gap before start_data, plug it with a sparse extent - // FIXME - don't do this if no_holes set - if (start_data > 0) { - NTSTATUS Status; - EXTENT_DATA* ed; - extent* lastext = NULL; - UINT64 len; - - le = fcb->extents.Flink; - while (le != &fcb->extents) { - extent* ext = CONTAINING_RECORD(le, extent, list_entry); - - if (!ext->ignore) { - if (ext->offset == start_data) { - lastext = ext; - break; - } else if (ext->offset > start_data) - break; - - lastext = ext; - } - - le = le->Flink; - } - - if (lastext && lastext->datalen >= sizeof(EXTENT_DATA)) { - EXTENT_DATA2* ed2; - - ed = lastext->data; - ed2 = (EXTENT_DATA2*)ed->data; - - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - } else - ed = NULL; - - if (!lastext || !ed || lastext->offset + len < start_data) { - if (!lastext) - Status = insert_sparse_extent(fcb, 0, start_data, rollback); - else if (!ed) { - ERR("extent at %llx was %u bytes, expected at least %u\n", lastext->offset, lastext->datalen, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } else - Status = insert_sparse_extent(fcb, lastext->offset + len, start_data - lastext->offset - len, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("insert_sparse_extent returned %08x\n", Status); - return Status; - } - } - } - flags = Vcb->data_flags; - ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); - while (written < orig_length) { UINT64 newlen = min(length, MAX_EXTENT_SIZE); BOOL done = FALSE; @@ -6487,18 +6555,20 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB. // First, see if we can write the extent part to an existing chunk. + ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE); + le = Vcb->chunks.Flink; while (le != &Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) { - if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) { + if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) { written += newlen; if (written == orig_length) { - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); ExReleaseResourceLite(&Vcb->chunk_lock); return STATUS_SUCCESS; } else { @@ -6511,25 +6581,30 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6 } } - ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&c->lock); le = le->Flink; } + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + if (done) continue; // Otherwise, see if we can put it in a new chunk. - if ((c = alloc_chunk(Vcb, flags, rollback))) { - ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + if ((c = alloc_chunk(Vcb, flags))) { + ExReleaseResourceLite(&Vcb->chunk_lock); + + ExAcquireResourceExclusiveLite(&c->lock, TRUE); if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) { - if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) { + if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) { written += newlen; if (written == orig_length) { - ExReleaseResourceLite(&c->nonpaged->lock); - ExReleaseResourceLite(&Vcb->chunk_lock); + ExReleaseResourceLite(&c->lock); return STATUS_SUCCESS; } else { done = TRUE; @@ -6540,8 +6615,9 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6 } } - ExReleaseResourceLite(&c->nonpaged->lock); - } + ExReleaseResourceLite(&c->lock); + } else + ExReleaseResourceLite(&Vcb->chunk_lock); if (!done) { FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME @@ -6549,14 +6625,12 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6 } } - ExReleaseResourceLite(&Vcb->chunk_lock); - WARN("couldn't find any data chunks with %llx bytes free\n", length); return STATUS_DISK_FULL; } -static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { +static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { LIST_ENTRY* le = Vcb->sector_checksums.Flink; changed_sector* cs; traverse_ptr tp, next_tp; @@ -6585,7 +6659,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { // FIXME - create checksum_root if it doesn't exist at all - Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { // tree is completely empty // FIXME - do proper check here that tree is empty if (!cs->deleted) { @@ -6597,7 +6671,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length); - if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, rollback)) { + if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(checksums); goto exit; @@ -6617,7 +6691,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_EXTENT_CSUM; searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size); - Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto exit; @@ -6656,7 +6730,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { searchkey.obj_type = TYPE_EXTENT_CSUM; searchkey.offset = cs->ol.key; - Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE); + Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto exit; @@ -6676,7 +6750,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { delete_tree_item(Vcb, &tp, rollback); } - if (find_next_item(Vcb, &tp, &next_tp, FALSE)) { + if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) { tp = next_tp; } else break; @@ -6711,7 +6785,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { 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, rollback)) { + 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, Irp, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(data); ExFreePool(bmparr); @@ -6752,13 +6826,13 @@ void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_l } } -NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) { +NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) { NTSTATUS Status; // 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), rollback); + sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("error - excise_extents failed\n"); return Status; @@ -6815,7 +6889,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes); cur_inline = ed->type == EXTENT_TYPE_INLINE; - if (cur_inline && end > fcb->Vcb->max_inline) { + if (cur_inline && end > fcb->Vcb->options.max_inline) { LIST_ENTRY changed_sector_list; BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM; UINT64 origlength, length; @@ -6846,13 +6920,22 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR fcb->inode_item.st_blocks -= origlength; - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); - Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); - if (!NT_SUCCESS(Status)) { - ERR("insert_extent returned %08x\n", Status); - ExFreePool(data); - return Status; + if (write_fcb_compressed(fcb)) { + Status = write_compressed(fcb, offset, offset + length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("write_compressed returned %08x\n", Status); + ExFreePool(data); + return Status; + } + } else { + Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_extent returned %08x\n", Status); + ExFreePool(data); + return Status; + } } oldalloc = ext->offset + length; @@ -6883,7 +6966,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR ed->decoded_size = end - ext->offset; - remove_fcb_extent(ext, rollback); + remove_fcb_extent(fcb, ext, rollback); if (!add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, rollback)) { ERR("add_extent_to_fcb failed\n"); @@ -6916,13 +6999,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR ERR("insert_prealloc_extent returned %08x\n", Status); return Status; } - } else { - Status = insert_sparse_extent(fcb, oldalloc, newalloc - oldalloc, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("insert_sparse_extent returned %08x\n", Status); - return Status; - } } fcb->extents_changed = TRUE; @@ -6938,7 +7014,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end; } } else { - if (end > fcb->Vcb->max_inline) { + if (end > fcb->Vcb->options.max_inline) { newalloc = sector_align(end, fcb->Vcb->superblock.sector_size); if (prealloc) { @@ -6948,13 +7024,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR ERR("insert_prealloc_extent returned %08x\n", Status); return Status; } - } else { - Status = insert_sparse_extent(fcb, 0, newalloc, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("insert_sparse_extent returned %08x\n", Status); - return Status; - } } fcb->extents_changed = TRUE; @@ -7010,677 +7079,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR return STATUS_SUCCESS; } -static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data) { - LIST_ENTRY* le; - extent* ext = NULL; - - le = fcb->extents.Flink; - - while (le != &fcb->extents) { - extent* nextext = CONTAINING_RECORD(le, extent, list_entry); - - if (!nextext->ignore) { - if (nextext->offset == start_data) { - ext = nextext; - break; - } else if (nextext->offset > start_data) - break; - - ext = nextext; - } - - le = le->Flink; - } - - if (!ext) - return FALSE; - - le = &ext->list_entry; - - while (le != &fcb->extents) { - ext = CONTAINING_RECORD(le, extent, list_entry); - - if (!ext->ignore) { - if (ext->datalen < sizeof(EXTENT_DATA)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - return FALSE; - } - - if (ext->offset < end_data && ext->data->type == EXTENT_TYPE_PREALLOC) { - EXTENT_DATA2* ed2; - - if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return FALSE; - } - - ed2 = (EXTENT_DATA2*)ext->data->data; - - if (ext->offset + ed2->num_bytes >= start_data) - return TRUE; - } - } - - le = le->Flink; - } - - return FALSE; -} - -static NTSTATUS do_cow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { - NTSTATUS Status; - - Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback); - if (!NT_SUCCESS(Status)) { - ERR("error - excise_extents returned %08x\n", Status); - goto end; - } - - Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - insert_extent returned %08x\n", Status); - goto end; - } - - Status = STATUS_SUCCESS; - -end: - return Status; -} - -static NTSTATUS do_prealloc_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { - NTSTATUS Status; - UINT64 last_written = start_data; - extent* ext = NULL; - LIST_ENTRY* le; - chunk* c; - - le = fcb->extents.Flink; - - while (le != &fcb->extents) { - extent* nextext = CONTAINING_RECORD(le, extent, list_entry); - - if (!nextext->ignore) { - if (nextext->offset == start_data) { - ext = nextext; - break; - } else if (nextext->offset > start_data) - break; - - ext = nextext; - } - - le = le->Flink; - } - - if (!ext) - return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback); - - le = &ext->list_entry; - - while (le != &fcb->extents) { - EXTENT_DATA* ed; - EXTENT_DATA2* ed2; - LIST_ENTRY* le2 = le->Flink; - - ext = CONTAINING_RECORD(le, extent, list_entry); - ed = ext->data; - - if (!ext->ignore) { - if (ext->offset >= end_data) - break; - - if (ext->datalen < sizeof(EXTENT_DATA)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } - - if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - ed2 = (EXTENT_DATA2*)ed->data; - } - - if (ed->type == EXTENT_TYPE_PREALLOC) { - if (ext->offset > last_written) { - Status = do_cow_write(Vcb, fcb, last_written, ext->offset, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("do_cow_write returned %08x\n", Status); - return Status; - } - - last_written = ext->offset; - } - - if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all - EXTENT_DATA* ned; - extent* newext; - - ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext) { - ERR("out of memory\n"); - ExFreePool(ned); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ned, ext->data, ext->datalen); - - ned->type = EXTENT_TYPE_REGULAR; - - Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - return Status; - } - - last_written = ext->offset + ed2->num_bytes; - - newext->offset = ext->offset; - newext->data = ned; - newext->datalen = ext->datalen; - newext->unique = ext->unique; - newext->ignore = FALSE; - InsertHeadList(&ext->list_entry, &newext->list_entry); - - remove_fcb_extent(ext, rollback); - } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning - EXTENT_DATA *ned, *nedb; - EXTENT_DATA2* ned2; - extent *newext1, *newext2; - - ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext1) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext2) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(newext1); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ned, ext->data, ext->datalen); - ned->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = end_data - ext->offset; - - RtlCopyMemory(nedb, ext->data, ext->datalen); - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += end_data - ext->offset; - ned2->num_bytes -= end_data - ext->offset; - - Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - return Status; - } - - last_written = end_data; - - newext1->offset = ext->offset; - newext1->data = ned; - newext1->datalen = ext->datalen; - newext1->unique = FALSE; - newext1->ignore = FALSE; - InsertHeadList(&ext->list_entry, &newext1->list_entry); - - newext2->offset = end_data; - newext2->data = nedb; - newext2->datalen = ext->datalen; - newext2->unique = FALSE; - newext2->ignore = FALSE; - InsertHeadList(&newext1->list_entry, &newext2->list_entry); - - c = get_chunk_from_address(Vcb, ed2->address); - - if (!c) - ERR("get_chunk_from_address(%llx) failed\n", ed2->address); - else { - Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); - - if (!NT_SUCCESS(Status)) { - ERR("update_changed_extent_ref returned %08x\n", Status); - return Status; - } - } - - remove_fcb_extent(ext, rollback); - } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end - EXTENT_DATA *ned, *nedb; - EXTENT_DATA2* ned2; - extent *newext1, *newext2; - - // FIXME - test this - - ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext1) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext2) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(newext1); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ned, ext->data, ext->datalen); - - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = start_data - ext->offset; - - RtlCopyMemory(nedb, ext->data, ext->datalen); - - nedb->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += start_data - ext->offset; - ned2->num_bytes = ext->offset + ed2->num_bytes - start_data; - - Status = do_write_data(Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - - return Status; - } - - last_written = start_data + ned2->num_bytes; - - newext1->offset = ext->offset; - newext1->data = ned; - newext1->datalen = ext->datalen; - newext1->unique = FALSE; - newext1->ignore = FALSE; - InsertHeadList(&ext->list_entry, &newext1->list_entry); - - newext2->offset = start_data; - newext2->data = nedb; - newext2->datalen = ext->datalen; - newext2->unique = FALSE; - newext2->ignore = FALSE; - InsertHeadList(&newext1->list_entry, &newext2->list_entry); - - c = get_chunk_from_address(Vcb, ed2->address); - - if (!c) - ERR("get_chunk_from_address(%llx) failed\n", ed2->address); - else { - Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); - - if (!NT_SUCCESS(Status)) { - ERR("update_changed_extent_ref returned %08x\n", Status); - return Status; - } - } - - remove_fcb_extent(ext, rollback); - } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle - EXTENT_DATA *ned, *nedb, *nedc; - EXTENT_DATA2* ned2; - extent *newext1, *newext2, *newext3; - - // FIXME - test this - - ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext1) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(nedc); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext2) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(nedc); - ExFreePool(newext1); - return STATUS_INSUFFICIENT_RESOURCES; - } - - newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); - if (!newext2) { - ERR("out of memory\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(nedc); - ExFreePool(newext1); - ExFreePool(newext2); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ned, ext->data, ext->datalen); - RtlCopyMemory(nedb, ext->data, ext->datalen); - RtlCopyMemory(nedc, ext->data, ext->datalen); - - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = start_data - ext->offset; - - nedb->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += start_data - ext->offset; - ned2->num_bytes = end_data - start_data; - - ned2 = (EXTENT_DATA2*)nedc->data; - ned2->offset += end_data - ext->offset; - ned2->num_bytes -= end_data - ext->offset; - - ned2 = (EXTENT_DATA2*)nedb->data; - Status = do_write_data(Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - return Status; - } - - last_written = end_data; - - newext1->offset = ext->offset; - newext1->data = ned; - newext1->datalen = ext->datalen; - newext1->unique = FALSE; - newext1->ignore = FALSE; - InsertHeadList(&ext->list_entry, &newext1->list_entry); - - newext2->offset = start_data; - newext2->data = nedb; - newext2->datalen = ext->datalen; - newext2->unique = FALSE; - newext2->ignore = FALSE; - InsertHeadList(&newext1->list_entry, &newext2->list_entry); - - newext3->offset = end_data; - newext3->data = nedc; - newext3->datalen = ext->datalen; - newext3->unique = FALSE; - newext3->ignore = FALSE; - InsertHeadList(&newext2->list_entry, &newext3->list_entry); - - c = get_chunk_from_address(Vcb, ed2->address); - - if (!c) - ERR("get_chunk_from_address(%llx) failed\n", ed2->address); - else { - Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2, - fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size); - - if (!NT_SUCCESS(Status)) { - ERR("update_changed_extent_ref returned %08x\n", Status); - return Status; - } - } - - remove_fcb_extent(ext, rollback); - } - } - } - - le = le2; - } - - if (last_written < end_data) { - Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("do_cow_write returned %08x\n", Status); - return Status; - } - } - - fcb->extents_changed = TRUE; - mark_fcb_dirty(fcb); - - return STATUS_SUCCESS; -} - -NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { - NTSTATUS Status; - UINT64 size, new_start, new_end, last_written = start_data; - extent* ext = NULL; - LIST_ENTRY* le; - - TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, data, changed_sector_list); - - le = fcb->extents.Flink; - - while (le != &fcb->extents) { - extent* nextext = CONTAINING_RECORD(le, extent, list_entry); - - if (!nextext->ignore) { - if (nextext->offset == start_data) { - ext = nextext; - break; - } else if (nextext->offset > start_data) - break; - - ext = nextext; - } - - le = le->Flink; - } - - if (!ext) - return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback); - - le = &ext->list_entry; - - while (le != &fcb->extents) { - EXTENT_DATA* ed; - EXTENT_DATA2* ed2; - BOOL do_cow; - LIST_ENTRY* le2 = le->Flink; - - ext = CONTAINING_RECORD(le, extent, list_entry); - - if (!ext->ignore) { - ed = ext->data; - - if (ext->offset >= end_data) - break; - - if (ext->datalen < sizeof(EXTENT_DATA)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } - - if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - ed2 = (EXTENT_DATA2*)ed->data; - } - - if (ed->type == EXTENT_TYPE_REGULAR) { - do_cow = !ext->unique; - } else { - do_cow = TRUE; - } - - if (ed->compression != BTRFS_COMPRESSION_NONE) { - FIXME("FIXME: compression not yet supported\n"); - return STATUS_NOT_SUPPORTED; - } - - if (ed->encryption != BTRFS_ENCRYPTION_NONE) { - WARN("encryption not supported\n"); - return STATUS_NOT_SUPPORTED; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("other encodings not supported\n"); - return STATUS_NOT_SUPPORTED; - } - - size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - TRACE("extent: start = %llx, length = %llx\n", ext->offset, size); - - new_start = ext->offset < start_data ? start_data : ext->offset; - new_end = ext->offset + size > end_data ? end_data : (ext->offset + size); - - TRACE("new_start = %llx\n", new_start); - TRACE("new_end = %llx\n", new_end); - - if (ed->type == EXTENT_TYPE_PREALLOC) { - Status = do_prealloc_write(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("do_prealloc_write returned %08x\n", Status); - return Status; - } - } else if (do_cow) { - TRACE("doing COW write\n"); - - Status = excise_extents(Vcb, fcb, new_start, new_end, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - excise_extents returned %08x\n", Status); - return Status; - } - - Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - insert_extent returned %08x\n", Status); - return Status; - } - } else { - UINT64 writeaddr = ed2->address + ed2->offset + new_start - ext->offset; - - TRACE("doing non-COW write to %llx\n", writeaddr); - - Status = write_data_complete(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start, Irp); - - if (!NT_SUCCESS(Status)) { - ERR("error - write_data returned %08x\n", Status); - return Status; - } - - if (changed_sector_list) { - unsigned int i; - changed_sector* sc; - - sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); - if (!sc) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - 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); - if (!sc->checksums) { - ERR("out of memory\n"); - ExFreePool(sc); - return STATUS_INSUFFICIENT_RESOURCES; - } - - 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_written = new_end; - } - - le = le2; - } - - if (last_written < end_data) { - Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("do_cow_write returned %08x\n", Status); - return Status; - } - } - - Status = STATUS_SUCCESS; - - fcb->extents_changed = TRUE; - mark_fcb_dirty(fcb); - - return Status; -} - // #ifdef DEBUG_PARANOID // static void print_loaded_trees(tree* t, int spaces) { // char pref[10]; @@ -7881,12 +7279,567 @@ NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT // } // #endif -static void STDCALL deferred_write_callback(void* context1, void* context2) { - PIRP Irp = context1; - device_extension* Vcb = context2; +static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written, + LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + EXTENT_DATA* ed = ext->data; + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; + NTSTATUS Status; + chunk* c; - if (!add_thread_job(Vcb, Irp)) - do_write_job(Vcb, Irp); + if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all + EXTENT_DATA* ned; + extent* newext; + + ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + ExFreePool(ned); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(ned, ext->data, ext->datalen); + + ned->type = EXTENT_TYPE_REGULAR; + + Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp); + if (!NT_SUCCESS(Status)) { + ERR("do_write_data returned %08x\n", Status); + return Status; + } + + *written = ed2->num_bytes; + + newext->offset = ext->offset; + newext->data = ned; + newext->datalen = ext->datalen; + newext->unique = ext->unique; + newext->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext); + + remove_fcb_extent(fcb, ext, rollback); + } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning + EXTENT_DATA *ned, *nedb; + EXTENT_DATA2* ned2; + extent *newext1, *newext2; + + ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!nedb) { + ERR("out of memory\n"); + ExFreePool(ned); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext2) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + ExFreePool(newext1); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(ned, ext->data, ext->datalen); + ned->type = EXTENT_TYPE_REGULAR; + ned2 = (EXTENT_DATA2*)ned->data; + ned2->num_bytes = end_data - ext->offset; + + RtlCopyMemory(nedb, ext->data, ext->datalen); + ned2 = (EXTENT_DATA2*)nedb->data; + ned2->offset += end_data - ext->offset; + ned2->num_bytes -= end_data - ext->offset; + + Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp); + if (!NT_SUCCESS(Status)) { + ERR("do_write_data returned %08x\n", Status); + return Status; + } + + *written = end_data - ext->offset; + + newext1->offset = ext->offset; + newext1->data = ned; + newext1->datalen = ext->datalen; + newext1->unique = FALSE; + newext1->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext1->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext1); + + newext2->offset = end_data; + newext2->data = nedb; + newext2->datalen = ext->datalen; + newext2->unique = FALSE; + newext2->ignore = FALSE; + InsertHeadList(&newext1->list_entry, &newext2->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext2); + + c = get_chunk_from_address(fcb->Vcb, ed2->address); + + if (!c) + ERR("get_chunk_from_address(%llx) failed\n", ed2->address); + else { + Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); + + if (!NT_SUCCESS(Status)) { + ERR("update_changed_extent_ref returned %08x\n", Status); + return Status; + } + } + + remove_fcb_extent(fcb, ext, rollback); + } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end + EXTENT_DATA *ned, *nedb; + EXTENT_DATA2* ned2; + extent *newext1, *newext2; + + ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!nedb) { + ERR("out of memory\n"); + ExFreePool(ned); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext2) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + ExFreePool(newext1); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(ned, ext->data, ext->datalen); + + ned2 = (EXTENT_DATA2*)ned->data; + ned2->num_bytes = start_data - ext->offset; + + RtlCopyMemory(nedb, ext->data, ext->datalen); + + nedb->type = EXTENT_TYPE_REGULAR; + ned2 = (EXTENT_DATA2*)nedb->data; + ned2->offset += start_data - ext->offset; + ned2->num_bytes = ext->offset + ed2->num_bytes - start_data; + + Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp); + if (!NT_SUCCESS(Status)) { + ERR("do_write_data returned %08x\n", Status); + return Status; + } + + *written = ned2->num_bytes; + + newext1->offset = ext->offset; + newext1->data = ned; + newext1->datalen = ext->datalen; + newext1->unique = FALSE; + newext1->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext1->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext1); + + newext2->offset = start_data; + newext2->data = nedb; + newext2->datalen = ext->datalen; + newext2->unique = FALSE; + newext2->ignore = FALSE; + InsertHeadList(&newext1->list_entry, &newext2->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext2); + + c = get_chunk_from_address(fcb->Vcb, ed2->address); + + if (!c) + ERR("get_chunk_from_address(%llx) failed\n", ed2->address); + else { + Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); + + if (!NT_SUCCESS(Status)) { + ERR("update_changed_extent_ref returned %08x\n", Status); + return Status; + } + } + + remove_fcb_extent(fcb, ext, rollback); + } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle + EXTENT_DATA *ned, *nedb, *nedc; + EXTENT_DATA2* ned2; + extent *newext1, *newext2, *newext3; + + ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!nedb) { + ERR("out of memory\n"); + ExFreePool(ned); + return STATUS_INSUFFICIENT_RESOURCES; + } + + nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); + if (!nedb) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + ExFreePool(nedc); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext2) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + ExFreePool(nedc); + ExFreePool(newext1); + return STATUS_INSUFFICIENT_RESOURCES; + } + + newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext2) { + ERR("out of memory\n"); + ExFreePool(ned); + ExFreePool(nedb); + ExFreePool(nedc); + ExFreePool(newext1); + ExFreePool(newext2); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(ned, ext->data, ext->datalen); + RtlCopyMemory(nedb, ext->data, ext->datalen); + RtlCopyMemory(nedc, ext->data, ext->datalen); + + ned2 = (EXTENT_DATA2*)ned->data; + ned2->num_bytes = start_data - ext->offset; + + nedb->type = EXTENT_TYPE_REGULAR; + ned2 = (EXTENT_DATA2*)nedb->data; + ned2->offset += start_data - ext->offset; + ned2->num_bytes = end_data - start_data; + + ned2 = (EXTENT_DATA2*)nedc->data; + ned2->offset += end_data - ext->offset; + ned2->num_bytes -= end_data - ext->offset; + + ned2 = (EXTENT_DATA2*)nedb->data; + Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp); + if (!NT_SUCCESS(Status)) { + ERR("do_write_data returned %08x\n", Status); + return Status; + } + + *written = end_data - start_data; + + newext1->offset = ext->offset; + newext1->data = ned; + newext1->datalen = ext->datalen; + newext1->unique = FALSE; + newext1->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext1->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext1); + + newext2->offset = start_data; + newext2->data = nedb; + newext2->datalen = ext->datalen; + newext2->unique = FALSE; + newext2->ignore = FALSE; + InsertHeadList(&newext1->list_entry, &newext2->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext2); + + newext3->offset = end_data; + newext3->data = nedc; + newext3->datalen = ext->datalen; + newext3->unique = FALSE; + newext3->ignore = FALSE; + InsertHeadList(&newext2->list_entry, &newext3->list_entry); + + add_insert_extent_rollback(rollback, fcb, newext3); + + c = get_chunk_from_address(fcb->Vcb, ed2->address); + + if (!c) + ERR("get_chunk_from_address(%llx) failed\n", ed2->address); + else { + Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2, + fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp); + + if (!NT_SUCCESS(Status)) { + ERR("update_changed_extent_ref returned %08x\n", Status); + return Status; + } + } + + remove_fcb_extent(fcb, ext, rollback); + } + + return STATUS_SUCCESS; +} + +NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + NTSTATUS Status; + LIST_ENTRY *le, *le2; + UINT64 written = 0, length = end_data - start; + UINT64 last_cow_start; +#ifdef DEBUG_PARANOID + UINT64 last_off; +#endif + + last_cow_start = 0; + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + le2 = le->Flink; + + if (!ext->ignore) { + EXTENT_DATA* ed = ext->data; + EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data; + UINT64 len; + BOOL nocow; + + len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; + + if (ext->offset + len <= start) + goto nextitem; + + if (ext->offset > start + written + length) + break; + + nocow = (ext->unique && fcb->inode_item.flags & BTRFS_INODE_NODATACOW) || ed->type == EXTENT_TYPE_PREALLOC; + + if (nocow) { + if (max(last_cow_start, start + written) < ext->offset) { + UINT64 start_write = max(last_cow_start, start + written); + + Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + return Status; + } + + Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, data, changed_sector_list, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_extent returned %08x\n", Status); + return Status; + } + + written += ext->offset - start_write; + length -= ext->offset - start_write; + + if (length == 0) + break; + } + + if (ed->type == EXTENT_TYPE_REGULAR) { + UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset; + UINT64 write_len = min(len, length); + + TRACE("doing non-COW write to %llx\n", writeaddr); + + Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL); + if (!NT_SUCCESS(Status)) { + ERR("write_data_complete returned %08x\n", Status); + return Status; + } + + if (changed_sector_list) { + unsigned int i; + changed_sector* sc; + + sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); + if (!sc) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + sc->ol.key = writeaddr; + sc->length = write_len / fcb->Vcb->superblock.sector_size; + sc->deleted = FALSE; + + sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG); + if (!sc->checksums) { + ERR("out of memory\n"); + ExFreePool(sc); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for (i = 0; i < sc->length; i++) { + sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + written + (i * fcb->Vcb->superblock.sector_size), fcb->Vcb->superblock.sector_size); + } + + insert_into_ordered_list(changed_sector_list, &sc->ol); + } + + written += write_len; + length -= write_len; + + if (length == 0) + break; + } else if (ed->type == EXTENT_TYPE_PREALLOC) { + UINT64 write_len; + + Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len, + changed_sector_list, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("do_write_file_prealloc returned %08x\n", Status); + return Status; + } + + written += write_len; + length -= write_len; + + if (length == 0) + break; + } + + last_cow_start = ext->offset + len; + } + } + +nextitem: + le = le2; + } + + if (length > 0) { + UINT64 start_write = max(last_cow_start, start + written); + + Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + return Status; + } + + Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, data, changed_sector_list, Irp, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_extent returned %08x\n", Status); + return Status; + } + } + + // FIXME - make extending work again (here?) + // FIXME - make maximum extent size 128 MB again (here?) + +#ifdef DEBUG_PARANOID + last_off = 0xffffffffffffffff; + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext->ignore) { + if (ext->offset == last_off) { + ERR("offset %llx duplicated\n", ext->offset); + int3; + } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) { + ERR("offsets out of order\n"); + int3; + } + + last_off = ext->offset; + } + + le = le->Flink; + } +#endif + + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + + return STATUS_SUCCESS; +} + +NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) { + NTSTATUS Status; + UINT64 i; + + for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) { + UINT64 s2, e2; + BOOL compressed; + + s2 = start_data + (i * COMPRESSED_EXTENT_SIZE); + e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data); + + Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, changed_sector_list, Irp, rollback); + + if (!NT_SUCCESS(Status)) { + ERR("write_compressed_bit returned %08x\n", Status); + return Status; + } + + // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't + // bother with the rest of it. + if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) { + fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS; + mark_fcb_dirty(fcb); + + // write subsequent data non-compressed + if (e2 < end_data) { + Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, changed_sector_list, Irp, rollback); + + if (!NT_SUCCESS(Status)) { + ERR("do_write_file returned %08x\n", Status); + return Status; + } + } + + return STATUS_SUCCESS; + } + } + + return STATUS_SUCCESS; } NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, @@ -7900,14 +7853,14 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void UINT8* data; LIST_ENTRY changed_sector_list; INODE_ITEM* origii; - BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE, write_eof = FALSE*/; + BOOL changed_length = FALSE, nocsum/*, lazy_writer = FALSE, write_eof = FALSE*/; NTSTATUS Status; LARGE_INTEGER time; BTRFS_TIME now; fcb* fcb; ccb* ccb; file_ref* fileref; - BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE; + BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile; ULONG filter = 0; TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache); @@ -7938,11 +7891,8 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags); - if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write)) { - CcDeferWrite(FileObject, (PCC_POST_DEFERRED_WRITE)deferred_write_callback, Irp, Vcb, *length, deferred_write); - + if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write)) return STATUS_PENDING; - } if (!wait && no_cache) return STATUS_PENDING; @@ -7973,7 +7923,9 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void paging_lock = TRUE; } - if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { + pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io; + + if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { Status = STATUS_PENDING; goto end; @@ -7990,7 +7942,6 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } 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->adsdata.Length : fcb->inode_item.st_size; @@ -8024,7 +7975,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } } - make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->max_inline; + make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->options.max_inline; if (changed_length) { if (newlength > fcb->Header.AllocationSize.QuadPart) { @@ -8092,43 +8043,14 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } if (fcb->ads) { -// UINT32 maxlen; - if (changed_length) { char* data2; -// // 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; -// -// Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// goto end; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// ERR("error - could not find key for xattr\n"); -// Status = STATUS_INTERNAL_ERROR; -// goto end; -// } -// -// if (tp.item->size < datalen) { -// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen); -// Status = STATUS_INTERNAL_ERROR; -// goto end; -// } -// -// maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead -// -// if (newlength > maxlen) { -// ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen); -// Status = STATUS_DISK_FULL; -// goto end; -// } + if (newlength > fcb->adsmaxlen) { + ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen); + Status = STATUS_DISK_FULL; + goto end; + } data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG); if (!data2) { @@ -8164,10 +8086,17 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void if (fileref) mark_fileref_dirty(fileref); } else { + BOOL compress = write_fcb_compressed(fcb); + if (make_inline) { start_data = 0; end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size); bufhead = sizeof(EXTENT_DATA) - 1; + } else if (compress) { + start_data = offset.QuadPart & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1); + end_data = min(sector_align(offset.QuadPart + *length, COMPRESSED_EXTENT_SIZE), + sector_align(newlength, fcb->Vcb->superblock.sector_size)); + bufhead = 0; } else { start_data = offset.QuadPart & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1); end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size); @@ -8211,7 +8140,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void InitializeListHead(&changed_sector_list); if (make_inline) { - Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback); + Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("error - excise_extents returned %08x\n", Status); ExFreePool(data); @@ -8234,31 +8163,21 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } fcb->inode_item.st_blocks += newlength; - } else if (!nocow) { - if (is_file_prealloc(fcb, start_data, end_data)) { - Status = do_prealloc_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - do_prealloc_write returned %08x\n", Status); - ExFreePool(data); - goto end; - } - } else { - Status = do_cow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - do_cow_write returned %08x\n", Status); - ExFreePool(data); - goto end; - } + } else if (compress) { + Status = write_compressed(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); + + if (!NT_SUCCESS(Status)) { + ERR("write_compressed returned %08x\n", Status); + ExFreePool(data); + goto end; } ExFreePool(data); } else { - Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); + Status = do_write_file(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback); if (!NT_SUCCESS(Status)) { - ERR("error - do_nocow_write returned %08x\n", Status); + ERR("do_write_file returned %08x\n", Status); ExFreePool(data); goto end; } @@ -8267,54 +8186,56 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } } - KeQuerySystemTime(&time); - win_time_to_unix(time, &now); + if (!pagefile) { + 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; +// 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) { - if (fileref && fileref->parent) - origii = &fileref->parent->fcb->inode_item; - else { - ERR("no parent fcb found for stream\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - } else - origii = &fcb->inode_item; - - origii->transid = Vcb->superblock.generation; - origii->sequence++; - origii->st_ctime = now; - - if (!fcb->ads) { - if (changed_length) { - TRACE("setting st_size to %llx\n", newlength); - origii->st_size = newlength; - filter |= FILE_NOTIFY_CHANGE_SIZE; + + if (fcb->ads) { + if (fileref && fileref->parent) + origii = &fileref->parent->fcb->inode_item; + else { + ERR("no parent fcb found for stream\n"); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + } else + origii = &fcb->inode_item; + + origii->transid = Vcb->superblock.generation; + origii->sequence++; + origii->st_ctime = now; + + if (!fcb->ads) { + if (changed_length) { + TRACE("setting st_size to %llx\n", newlength); + origii->st_size = newlength; + filter |= FILE_NOTIFY_CHANGE_SIZE; + } + + origii->st_mtime = now; + filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; } - origii->st_mtime = now; - filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb); } - mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb); - if (!nocsum) { ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); commit_checksum_changes(Vcb, &changed_sector_list); @@ -8496,7 +8417,7 @@ NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { goto end; } - if (!(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { + if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { WARN("insufficient permissions\n"); Status = STATUS_ACCESS_DENIED; goto end; diff --git a/reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt b/reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt new file mode 100644 index 00000000000..06aa88d2f06 --- /dev/null +++ b/reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt @@ -0,0 +1,17 @@ + +include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib + inc) + +list(APPEND SOURCE + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/adler32.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/crc32.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/deflate.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inffast.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inflate.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inftrees.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/trees.c + ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/zutil.c) + +add_library(zlib_solo ${SOURCE}) + +add_definitions(-DZ_SOLO) diff --git a/reactos/media/doc/README.FSD b/reactos/media/doc/README.FSD index 42c9d7de95b..59d0a88a477 100644 --- a/reactos/media/doc/README.FSD +++ b/reactos/media/doc/README.FSD @@ -3,7 +3,7 @@ The following FSD are shared with: https://github.com/maharmstone/btrfs. -reactos/drivers/filesystems/btrfs # Synced to 0.5 +reactos/drivers/filesystems/btrfs # Synced to 0.6 The following FSD are shared with: http://www.ext2fsd.com/