From 7695f39c11619463148c7087fef9e982483e5dd5 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Wed, 27 Jul 2016 19:24:26 +0000 Subject: [PATCH] [BTRFS] Sync btrfs to 0.5. This breaks BTRFS in ReactOS. CORE-11674 svn path=/trunk/; revision=72023 --- .../drivers/filesystems/btrfs/CMakeLists.txt | 2 + reactos/drivers/filesystems/btrfs/btrfs.c | 2651 +++--- reactos/drivers/filesystems/btrfs/btrfs.rc | 4 +- reactos/drivers/filesystems/btrfs/btrfs_drv.h | 405 +- reactos/drivers/filesystems/btrfs/create.c | 1965 +++-- reactos/drivers/filesystems/btrfs/dirctrl.c | 608 +- .../drivers/filesystems/btrfs/extent-tree.c | 735 +- reactos/drivers/filesystems/btrfs/fastio.c | 125 +- reactos/drivers/filesystems/btrfs/fileinfo.c | 4247 +++++----- .../drivers/filesystems/btrfs/flushthread.c | 22 +- .../drivers/filesystems/btrfs/free-space.c | 1202 ++- reactos/drivers/filesystems/btrfs/fsctl.c | 1359 ++- reactos/drivers/filesystems/btrfs/pnp.c | 18 +- reactos/drivers/filesystems/btrfs/read.c | 1316 ++- reactos/drivers/filesystems/btrfs/registry.c | 667 ++ reactos/drivers/filesystems/btrfs/reparse.c | 599 +- reactos/drivers/filesystems/btrfs/search.c | 391 +- reactos/drivers/filesystems/btrfs/security.c | 308 +- reactos/drivers/filesystems/btrfs/treefuncs.c | 543 +- .../drivers/filesystems/btrfs/worker-thread.c | 108 + reactos/drivers/filesystems/btrfs/write.c | 7546 ++++++++++------- 21 files changed, 15007 insertions(+), 9814 deletions(-) create mode 100644 reactos/drivers/filesystems/btrfs/registry.c create mode 100644 reactos/drivers/filesystems/btrfs/worker-thread.c diff --git a/reactos/drivers/filesystems/btrfs/CMakeLists.txt b/reactos/drivers/filesystems/btrfs/CMakeLists.txt index b39737e3734..1156757b4f4 100644 --- a/reactos/drivers/filesystems/btrfs/CMakeLists.txt +++ b/reactos/drivers/filesystems/btrfs/CMakeLists.txt @@ -16,10 +16,12 @@ list(APPEND SOURCE fsctl.c pnp.c read.c + registry.c reparse.c search.c security.c treefuncs.c + worker-thread.c write.c btrfs_drv.h) diff --git a/reactos/drivers/filesystems/btrfs/btrfs.c b/reactos/drivers/filesystems/btrfs/btrfs.c index b31059aeb94..54723e27bfa 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs.c +++ b/reactos/drivers/filesystems/btrfs/btrfs.c @@ -33,6 +33,7 @@ #else #include #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) @@ -53,7 +54,7 @@ LIST_ENTRY VcbList; ERESOURCE global_loading_lock; UINT32 debug_log_level = 0; BOOL log_started = FALSE; -UNICODE_STRING log_device, log_file; +UNICODE_STRING log_device, log_file, registry_path; #ifdef _DEBUG PFILE_OBJECT comfo = NULL; @@ -217,7 +218,7 @@ exit2: } #endif -ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment ) +UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment ) { if( Alignment & ( Alignment - 1 ) ) { @@ -307,6 +308,9 @@ static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) { if (log_file.Buffer) ExFreePool(log_file.Buffer); + + if (registry_path.Buffer) + ExFreePool(registry_path.Buffer); } BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) { @@ -415,234 +419,6 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* return FALSE; } -NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - ULONG xasize; - DIR_ITEM* xa; - NTSTATUS Status; - - TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen); - - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_XATTR_ITEM; - searchkey.offset = crc32; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - 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 - - if (!keycmp(&tp.item->key, &searchkey)) { // key exists - UINT8* newdata; - ULONG size = tp.item->size; - - xa = (DIR_ITEM*)tp.item->data; - - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - } else { - while (TRUE) { - ULONG oldxasize; - - if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n; - - if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) { - UINT64 pos; - - // replace - newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG); - if (!newdata) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - pos = (UINT8*)xa - tp.item->data; - if (pos + oldxasize < tp.item->size) { // copy after changed xattr - RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize); - } - - if (pos > 0) { // copy before changed xattr - RtlCopyMemory(newdata, tp.item->data, pos); - xa = (DIR_ITEM*)(newdata + pos); - } else - xa = (DIR_ITEM*)newdata; - - xa->key.obj_id = 0; - xa->key.obj_type = 0; - xa->key.offset = 0; - xa->transid = Vcb->superblock.generation; - xa->m = datalen; - xa->n = (UINT16)strlen(name); - xa->type = BTRFS_TYPE_EA; - RtlCopyMemory(xa->name, name, strlen(name)); - RtlCopyMemory(xa->name + strlen(name), data, datalen); - - delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback); - - break; - } - - if (xa->m + xa->n >= size) { // FIXME - test this works - // not found, add to end of data - newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG); - if (!newdata) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(newdata, tp.item->data, tp.item->size); - - xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size); - xa->key.obj_id = 0; - xa->key.obj_type = 0; - xa->key.offset = 0; - xa->transid = Vcb->superblock.generation; - xa->m = datalen; - xa->n = (UINT16)strlen(name); - xa->type = BTRFS_TYPE_EA; - RtlCopyMemory(xa->name, name, strlen(name)); - RtlCopyMemory(xa->name + strlen(name), data, datalen); - - delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback); - - break; - } else { - xa = (DIR_ITEM*)&xa->name[xa->m + xa->n]; - size -= oldxasize; - } - } - } - } else { - // add new DIR_ITEM struct - - xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG); - if (!xa) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - xa->key.obj_id = 0; - xa->key.obj_type = 0; - xa->key.offset = 0; - xa->transid = Vcb->superblock.generation; - xa->m = datalen; - xa->n = (UINT16)strlen(name); - xa->type = BTRFS_TYPE_EA; - RtlCopyMemory(xa->name, name, strlen(name)); - RtlCopyMemory(xa->name + strlen(name), data, datalen); - - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback); - } - - return STATUS_SUCCESS; -} - -BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - DIR_ITEM* xa; - NTSTATUS Status; - - TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32); - - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_XATTR_ITEM; - searchkey.offset = crc32; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return FALSE; - } - - if (!keycmp(&tp.item->key, &searchkey)) { // key exists - ULONG size = tp.item->size; - - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - - return FALSE; - } else { - xa = (DIR_ITEM*)tp.item->data; - - while (TRUE) { - ULONG oldxasize; - - if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - return FALSE; - } - - oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n; - - if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) { - ULONG newsize; - UINT8 *newdata, *dioff; - - newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m); - - delete_tree_item(Vcb, &tp, rollback); - - if (newsize == 0) { - TRACE("xattr %s deleted\n", name); - - return TRUE; - } - - // FIXME - deleting collisions almost certainly works, but we should test it properly anyway - newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG); - if (!newdata) { - ERR("out of memory\n"); - return FALSE; - } - - if ((UINT8*)xa > tp.item->data) { - RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data); - dioff = newdata + ((UINT8*)xa - tp.item->data); - } else { - dioff = newdata; - } - - if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size) - RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data)); - - insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback); - - - return TRUE; - } - - if (xa->m + xa->n >= size) { // FIXME - test this works - WARN("xattr %s not found\n", name); - - return FALSE; - } else { - xa = (DIR_ITEM*)&xa->name[xa->m + xa->n]; - size -= oldxasize; - } - } - } - } else { - WARN("xattr %s not found\n", name); - - return FALSE; - } -} - NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; @@ -690,41 +466,10 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 return STATUS_SUCCESS; } -UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode) { - KEY searchkey; - traverse_ptr tp, prev_tp; - UINT64 dirpos; - NTSTATUS Status; - - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_DIR_INDEX + 1; - searchkey.offset = 0; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return 0; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) { - tp = prev_tp; - - TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } - } - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) { - dirpos = tp.item->key.offset + 1; - } else - dirpos = 2; - - return dirpos; -} - static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; + device_extension* Vcb = DeviceObject->DeviceExtension; BOOL top_level; TRACE("close\n"); @@ -733,7 +478,7 @@ static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { top_level = is_top_level(Irp); - if (DeviceObject == devobj) { + if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) { TRACE("Closing file system\n"); Status = STATUS_SUCCESS; goto exit; @@ -762,55 +507,20 @@ exit: return Status; } -static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { - NTSTATUS Status; - BOOL top_level; - PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - - FsRtlEnterFileSystem(); - - top_level = is_top_level(Irp); - -// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE"); - - _SEH2_TRY { - if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { - CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress); - - Irp->MdlAddress = NULL; - Irp->IoStatus.Status = STATUS_SUCCESS; - } else { - Status = write_file(DeviceObject, Irp); - } - } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { - Status = _SEH2_GetExceptionCode(); - } _SEH2_END; - - Irp->IoStatus.Status = Status; - - TRACE("wrote %u bytes\n", Irp->IoStatus.Information); - - if (Status != STATUS_PENDING) - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - if (top_level) - IoSetTopLevelIrp(NULL); - - FsRtlExitFileSystem(); - - TRACE("returning %08x\n", Status); - - return Status; -} - static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; BOOL top_level; + device_extension* Vcb = DeviceObject->DeviceExtension; FsRtlEnterFileSystem(); top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + FIXME("STUB: query ea\n"); Status = STATUS_NOT_IMPLEMENTED; @@ -818,7 +528,8 @@ static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); - + +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -836,6 +547,11 @@ static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + FIXME("STUB: set ea\n"); Status = STATUS_NOT_IMPLEMENTED; @@ -849,6 +565,7 @@ static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) IoCompleteRequest( Irp, IO_NO_INCREMENT ); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -862,6 +579,7 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PFILE_OBJECT FileObject = IrpSp->FileObject; fcb* fcb = FileObject->FsContext; + device_extension* Vcb = DeviceObject->DeviceExtension; BOOL top_level; TRACE("flush buffers\n"); @@ -870,6 +588,11 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + Status = STATUS_SUCCESS; Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; @@ -887,6 +610,7 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR IoCompleteRequest(Irp, IO_NO_INCREMENT); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -895,6 +619,18 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR return Status; } +static void calculate_total_space(device_extension* Vcb, LONGLONG* totalsize, LONGLONG* freespace) { + UINT8 factor; + + if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) + factor = 2; + else + factor = 1; + + *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) / factor; + *freespace = ((Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size) / factor; +} + static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IrpSp; NTSTATUS Status; @@ -920,6 +656,11 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj FsRtlEnterFileSystem(); top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + IrpSp = IoGetCurrentIrpStackLocation(Irp); Status = STATUS_NOT_IMPLEMENTED; @@ -944,7 +685,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH | FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS | - FILE_SUPPORTS_REPARSE_POINTS; + FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS; if (Vcb->readonly) data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME; @@ -973,16 +714,10 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj case FileFsFullSizeInformation: { FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer; - UINT64 totalsize, freespace; TRACE("FileFsFullSizeInformation\n"); - // FIXME - calculate correctly for RAID - totalsize = Vcb->superblock.total_bytes / Vcb->superblock.sector_size; - freespace = (Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size; - - ffsi->TotalAllocationUnits.QuadPart = totalsize; - ffsi->ActualAvailableAllocationUnits.QuadPart = freespace; + calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->ActualAvailableAllocationUnits.QuadPart); ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart; ffsi->SectorsPerAllocationUnit = 1; ffsi->BytesPerSector = Vcb->superblock.sector_size; @@ -994,23 +729,27 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj } case FileFsObjectIdInformation: - FIXME("STUB: FileFsObjectIdInformation\n"); + { + FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer; + + TRACE("FileFsObjectIdInformation\n"); + + RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16); + RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo)); + + BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION); + Status = STATUS_SUCCESS; + break; + } case FileFsSizeInformation: { FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer; - UINT64 totalsize, freespace; TRACE("FileFsSizeInformation\n"); - // FIXME - calculate correctly for RAID - // FIXME - is this returning the right free space? - totalsize = Vcb->superblock.dev_item.num_bytes / Vcb->superblock.sector_size; - freespace = (Vcb->superblock.dev_item.num_bytes - Vcb->superblock.dev_item.bytes_used) / Vcb->superblock.sector_size; - - ffsi->TotalAllocationUnits.QuadPart = totalsize; - ffsi->AvailableAllocationUnits.QuadPart = freespace; + calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->AvailableAllocationUnits.QuadPart); ffsi->SectorsPerAllocationUnit = 1; ffsi->BytesPerSector = Vcb->superblock.sector_size; @@ -1030,7 +769,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj TRACE("FileFsVolumeInformation\n"); TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length); - acquire_tree_lock(Vcb, FALSE); + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR)); RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label)); @@ -1062,7 +801,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel); } - release_tree_lock(Vcb, FALSE); + ExReleaseResourceLite(&Vcb->tree_lock); BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len; Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; @@ -1071,7 +810,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj default: Status = STATUS_INVALID_PARAMETER; - WARN("unknown FsInformatClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass); + WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass); break; } @@ -1090,6 +829,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj IoCompleteRequest( Irp, IO_DISK_INCREMENT ); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -1277,7 +1017,7 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t InsertTailList(&Vcb->trees, &t->list_entry); t->write = TRUE; - Vcb->write_trees++; + Vcb->need_write = TRUE; } *rootptr = r; @@ -1308,6 +1048,114 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t // } // } +// static void test_alloc_chunk(device_extension* Vcb) { +// LIST_ENTRY rollback; +// chunk* c; +// +// InitializeListHead(&rollback); +// +// c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback); +// if (!c) { +// ERR("alloc_chunk failed\n"); +// do_rollback(Vcb, &rollback); +// } else { +// clear_rollback(&rollback); +// } +// } + +// static void test_space_list(device_extension* Vcb) { +// chunk* c; +// int i, j; +// LIST_ENTRY* le; +// +// typedef struct { +// UINT64 address; +// UINT64 length; +// BOOL add; +// } space_test; +// +// static const space_test entries[] = { +// { 0x1000, 0x1000 }, +// { 0x3000, 0x2000 }, +// { 0x6000, 0x1000 }, +// { 0, 0 } +// }; +// +// static const space_test tests[] = { +// { 0x0, 0x800, TRUE }, +// { 0x1800, 0x400, TRUE }, +// { 0x800, 0x2000, TRUE }, +// { 0x1000, 0x2000, TRUE }, +// { 0x2000, 0x3800, TRUE }, +// { 0x800, 0x1000, TRUE }, +// { 0x1800, 0x1000, TRUE }, +// { 0x5000, 0x800, TRUE }, +// { 0x5000, 0x1000, TRUE }, +// { 0x7000, 0x1000, TRUE }, +// { 0x8000, 0x1000, TRUE }, +// { 0x800, 0x800, TRUE }, +// { 0x0, 0x3800, TRUE }, +// { 0x1000, 0x2800, TRUE }, +// { 0x1000, 0x1000, FALSE }, +// { 0x800, 0x2000, FALSE }, +// { 0x0, 0x3800, FALSE }, +// { 0x2800, 0x1000, FALSE }, +// { 0x1800, 0x2000, FALSE }, +// { 0x3800, 0x1000, FALSE }, +// { 0, 0, FALSE } +// }; +// +// c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry); +// +// i = 0; +// while (tests[i].length > 0) { +// InitializeListHead(&c->space); +// InitializeListHead(&c->space_size); +// ERR("test %u\n", i); +// +// j = 0; +// while (entries[j].length > 0) { +// space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); +// s->address = entries[j].address; +// s->size = entries[j].length; +// InsertTailList(&c->space, &s->list_entry); +// +// order_space_entry(s, &c->space_size); +// +// j++; +// } +// +// if (tests[i].add) +// space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL); +// else +// space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL); +// +// le = c->space.Flink; +// while (le != &c->space) { +// space* s = CONTAINING_RECORD(le, space, list_entry); +// +// ERR("(%llx,%llx)\n", s->address, s->size); +// +// le = le->Flink; +// } +// +// ERR("--\n"); +// +// le = c->space_size.Flink; +// while (le != &c->space_size) { +// space* s = CONTAINING_RECORD(le, space, list_entry_size); +// +// ERR("(%llx,%llx)\n", s->address, s->size); +// +// le = le->Flink; +// } +// +// i++; +// } +// +// int3; +// } + static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) { ULONG utf8len; NTSTATUS Status; @@ -1325,14 +1173,14 @@ static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATI // FIXME - check for '/' and '\\' and reject - acquire_tree_lock(Vcb, TRUE); - // utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG); Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength); if (!NT_SUCCESS(Status)) goto release; + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); + if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR)) RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len); @@ -1340,11 +1188,13 @@ static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATI // test_tree_splitting(Vcb); // test_dropping_tree(Vcb); // test_creating_root(Vcb); +// test_alloc_chunk(Vcb); +// test_space_list(Vcb); - Status = consider_write(Vcb); + Vcb->need_write = TRUE; release: - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); end: TRACE("returning %08x\n", Status); @@ -1364,6 +1214,11 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + Status = STATUS_NOT_IMPLEMENTED; if (Vcb->readonly) { @@ -1371,7 +1226,7 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec goto end; } - if (Vcb->removing) { + if (Vcb->removing || Vcb->locked) { Status = STATUS_ACCESS_DENIED; goto end; } @@ -1402,6 +1257,7 @@ end: IoCompleteRequest( Irp, IO_NO_INCREMENT ); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -1480,7 +1336,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, UINT64* index, LIST_ENTRY* rollback) { +NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; BOOL changed = FALSE; @@ -1547,9 +1403,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback); } - if (index) - *index = ir->index; - break; } @@ -1635,9 +1488,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback); } - if (index) - *index = ier->index; - break; } @@ -1655,156 +1505,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; } -static NTSTATUS delete_subvol(file_ref* fileref, LIST_ENTRY* rollback) { - NTSTATUS Status; - UINT64 index; - KEY searchkey; - traverse_ptr tp; - UINT32 crc32; - ROOT_ITEM* ri; - BOOL no_ref = FALSE; - fcb* fcb = fileref->fcb; - - // delete ROOT_REF in root tree - - Status = delete_root_ref(fcb->Vcb, fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->utf8, &index, rollback); - - // A bug in Linux means that if you create a snapshot of a subvol containing another subvol, - // the ROOT_REF and ROOT_BACKREF items won't be created, nor will num_references of ROOT_ITEM - // be increased. In this case, we just unlink the subvol from its parent, and don't worry - // about anything else. - - if (Status == STATUS_NOT_FOUND) - no_ref = TRUE; - else if (!NT_SUCCESS(Status)) { - ERR("delete_root_ref returned %08x\n", Status); - return Status; - } - - if (!no_ref) { - // delete ROOT_BACKREF in root tree - - Status = update_root_backref(fcb->Vcb, 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_ITEM in parent - - crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length); - Status = delete_dir_item(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, &fileref->utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); - return Status; - } - - // delete DIR_INDEX in parent - - if (!no_ref) { - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = index; - - Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("find_item 1 returned %08x\n", Status); - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - delete_tree_item(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 { - BOOL b; - traverse_ptr next_tp; - - // If we have no ROOT_REF, we have to look through all the DIR_INDEX entries manually :-( - - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("find_item 1 returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_type == TYPE_DIR_INDEX && tp.item->size >= sizeof(DIR_ITEM)) { - DIR_ITEM* di = (DIR_ITEM*)tp.item->data; - - if (di->key.obj_id == fcb->subvol->id && di->key.obj_type == TYPE_ROOT_ITEM && di->n == fileref->utf8.Length && - tp.item->size >= sizeof(DIR_ITEM) - 1 + di->m + di->n && RtlCompareMemory(fileref->utf8.Buffer, di->name, di->n) == di->n) { - delete_tree_item(fcb->Vcb, &tp, rollback); - break; - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - - if (b) { - tp = next_tp; - - 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)) - break; - } - } while (b); - } - - if (no_ref) - return STATUS_SUCCESS; - - if (fcb->subvol->root_item.num_references > 1) { - UINT64 offset; - - // change ROOT_ITEM num_references - - fcb->subvol->root_item.num_references--; - - searchkey.obj_id = fcb->subvol->id; - searchkey.obj_type = TYPE_ROOT_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(fcb->Vcb, fcb->Vcb->root_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("find_item 2 returned %08x\n", Status); - return Status; - } - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { - delete_tree_item(fcb->Vcb, &tp, rollback); - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - offset = tp.item->key.offset; - } else { - ERR("could not find ROOT_ITEM for subvol %llx\n", fcb->subvol->id); - offset = 0; - } - - ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG); - if (!ri) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ri, &fcb->subvol->root_item, sizeof(ROOT_ITEM)); - - if (!insert_tree_item(fcb->Vcb, fcb->Vcb->root_root, fcb->subvol->id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - return STATUS_INTERNAL_ERROR; - } - } else { - RemoveEntryList(&fcb->subvol->list_entry); - - InsertTailList(&fcb->Vcb->drop_roots, &fcb->subvol->list_entry); - } - - return STATUS_SUCCESS; -} - static WCHAR* file_desc_fcb(fcb* fcb) { char s[60]; UNICODE_STRING us; @@ -1813,6 +1513,9 @@ static WCHAR* file_desc_fcb(fcb* fcb) { if (fcb->debug_desc) return fcb->debug_desc; + if (fcb == fcb->Vcb->volume_fcb) + return L"volume FCB"; + fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG); if (!fcb->debug_desc) return L"(memory error)"; @@ -1838,15 +1541,27 @@ static WCHAR* file_desc_fcb(fcb* fcb) { } WCHAR* file_desc_fileref(file_ref* fileref) { + NTSTATUS Status; + UNICODE_STRING fn; + if (fileref->debug_desc) return fileref->debug_desc; - fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length + sizeof(WCHAR), ALLOC_TAG); - if (!fileref->debug_desc) - return L"(memory error)"; + Status = fileref_get_filename(fileref, &fn, NULL); + if (!NT_SUCCESS(Status)) { + return L"ERROR"; + } - RtlCopyMemory(fileref->debug_desc, fileref->full_filename.Buffer, fileref->full_filename.Length); - fileref->debug_desc[fileref->full_filename.Length / sizeof(WCHAR)] = 0; + fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG); + if (!fileref->debug_desc) { + ExFreePool(fn.Buffer); + return L"(memory error)"; + } + + RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length); + fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0; + + ExFreePool(fn.Buffer); return fileref->debug_desc; } @@ -1863,349 +1578,155 @@ WCHAR* file_desc(PFILE_OBJECT FileObject) { } void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) { + UNICODE_STRING fn; + NTSTATUS Status; + USHORT name_offset; fcb* fcb = fileref->fcb; - FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fileref->full_filename, fileref->name_offset * sizeof(WCHAR), - NULL, NULL, filter_match, action, NULL); + Status = fileref_get_filename(fileref, &fn, &name_offset); + if (!NT_SUCCESS(Status)) { + ERR("fileref_get_filename returned %08x\n", Status); + return; + } + + FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset, + NULL, NULL, filter_match, action, NULL, NULL); + ExFreePool(fn.Buffer); } -NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) { - ULONG bytecount; - NTSTATUS Status; - char* utf8 = NULL; - UINT32 crc32; - KEY searchkey; - traverse_ptr tp, tp2; - UINT64 parinode, index; - root* parsubvol; - INODE_ITEM *ii, *dirii; - LARGE_INTEGER time; - BTRFS_TIME now; - LIST_ENTRY changed_sector_list; +void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) { fcb* fcb = fileref->fcb; -#ifdef _DEBUG - LARGE_INTEGER freq, time1, time2; -#endif + LIST_ENTRY* le; + NTSTATUS Status; - if (fileref->deleted || fcb->deleted) { - WARN("trying to delete already-deleted file\n"); - return STATUS_SUCCESS; + // no point looking for hardlinks if st_nlink == 1 + if (fileref->fcb->inode_item.st_nlink == 1) { + send_notification_fileref(fileref, filter_match, action); + return; } - if (fileref == fcb->Vcb->root_fileref) { - ERR("error - trying to delete root FCB\n"); - return STATUS_INTERNAL_ERROR; - } + ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE); - if (fcb->inode == SUBVOL_ROOT_INODE) { - Status = delete_subvol(fileref, rollback); + le = fcb->hardlinks.Flink; + while (le != &fcb->hardlinks) { + hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); + file_ref* parfr; - if (!NT_SUCCESS(Status)) - goto exit; - else { - parinode = fileref->parent->fcb->inode; - parsubvol = fileref->parent->fcb->subvol; - bytecount = fileref->utf8.Length; - goto success2; - } - } - -#ifdef _DEBUG - time1 = KeQueryPerformanceCounter(&freq); -#endif - - KeQuerySystemTime(&time); - win_time_to_unix(time, &now); - - if (fcb->ads) { - char* s; - TRACE("deleting ADS\n"); + Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr); - s = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.Length + 1, ALLOC_TAG); - if (!s) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length); - s[fcb->adsxattr.Length] = 0; - - if (!delete_xattr(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, s, fcb->adshash, rollback)) { - ERR("failed to delete xattr %s\n", s); - } - - ExFreePool(s); - - fileref->parent->fcb->inode_item.transid = fcb->Vcb->superblock.generation; - fileref->parent->fcb->inode_item.sequence++; - fileref->parent->fcb->inode_item.st_ctime = now; - - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto exit; - } - - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM)); - delete_tree_item(fcb->Vcb, &tp, rollback); - - insert_tree_item(fcb->Vcb, fileref->parent->fcb->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL, rollback); - - fileref->parent->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; - fileref->parent->fcb->subvol->root_item.ctime = now; - - goto success; - } - - Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length); - if (!NT_SUCCESS(Status)) { - ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status); - return Status; - } - - utf8 = ExAllocatePoolWithTag(PagedPool, bytecount + 1, ALLOC_TAG); - if (!utf8) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length); - utf8[bytecount] = 0; - - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount); - - TRACE("deleting %.*S\n", file_desc_fileref(fileref)); - - if (fileref->parent->fcb->subvol == fcb->subvol) - parinode = fileref->parent->fcb->inode; - else - parinode = SUBVOL_ROOT_INODE; - - parsubvol = fcb->subvol; - - // delete DIR_ITEM (0x54) - - Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fileref->utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); - return Status; - } - - // delete INODE_REF (0xc) - - index = 0; - - Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fileref->utf8, &index, rollback); - - // delete DIR_INDEX (0x60) - - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = index; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - delete_tree_item(fcb->Vcb, &tp, rollback); - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } - - // delete INODE_ITEM (0x1) - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto exit; - } - - tp = tp2; - - if (keycmp(&searchkey, &tp.item->key)) { - ERR("error - INODE_ITEM not found\n"); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - 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)); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - ii = (INODE_ITEM*)tp.item->data; - TRACE("nlink = %u\n", ii->st_nlink); - - if (ii->st_nlink > 1) { - INODE_ITEM* newii; - - newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!newii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - RtlCopyMemory(newii, ii, sizeof(INODE_ITEM)); - newii->st_nlink--; - newii->transid = fcb->Vcb->superblock.generation; - newii->sequence++; - newii->st_ctime = now; - - TRACE("replacing (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - delete_tree_item(fcb->Vcb, &tp, rollback); - - if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newii, sizeof(INODE_ITEM), NULL, rollback)) - ERR("error - failed to insert item\n"); - - goto success2; - } - - delete_tree_item(fcb->Vcb, &tp, rollback); - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - fcb->deleted = TRUE; - - // delete XATTR_ITEM (0x18) - - while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) { - tp = tp2; - - if (tp.item->key.obj_id == fcb->inode) { - // FIXME - do metadata thing here too? - if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { - delete_tree_item(fcb->Vcb, &tp, rollback); - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + ERR("open_fileref_by_inode returned %08x\n", Status); + } else if (!parfr->deleted) { + LIST_ENTRY* le2; + BOOL found = FALSE, deleted = FALSE; + UNICODE_STRING* fn; + + le2 = parfr->children.Flink; + while (le2 != &parfr->children) { + file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); + + if (fr2->index == hl->index) { + found = TRUE; + deleted = fr2->deleted; + + if (!deleted) + fn = &fr2->filepart; + + break; + } + + le2 = le2->Flink; } - } else - break; - } - - // excise extents - - InitializeListHead(&changed_sector_list); - - if (fcb->type != BTRFS_TYPE_DIRECTORY && 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), &changed_sector_list, rollback); - if (!NT_SUCCESS(Status)) { - ERR("excise_extents returned %08x\n", Status); - goto exit; + + if (!found) + fn = &hl->name; + + if (!deleted) { + UNICODE_STRING path; + + Status = fileref_get_filename(parfr, &path, NULL); + if (!NT_SUCCESS(Status)) { + ERR("fileref_get_filename returned %08x\n", Status); + } else { + UNICODE_STRING fn2; + ULONG name_offset; + + name_offset = path.Length; + if (parfr != fileref->fcb->Vcb->root_fileref) name_offset += sizeof(WCHAR); + + fn2.Length = fn2.MaximumLength = fn->Length + name_offset; + fn2.Buffer = ExAllocatePoolWithTag(PagedPool, fn2.MaximumLength, ALLOC_TAG); + + RtlCopyMemory(fn2.Buffer, path.Buffer, path.Length); + if (parfr != fileref->fcb->Vcb->root_fileref) fn2.Buffer[path.Length / sizeof(WCHAR)] = '\\'; + RtlCopyMemory(&fn2.Buffer[name_offset / sizeof(WCHAR)], fn->Buffer, fn->Length); + + TRACE("%.*S\n", fn2.Length / sizeof(WCHAR), fn2.Buffer); + + FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn2, name_offset, + NULL, NULL, filter_match, action, NULL, NULL); + + ExFreePool(fn2.Buffer); + ExFreePool(path.Buffer); + } + } + + free_fileref(parfr); } - if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) - update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback); + le = le->Flink; } -success2: - // update INODE_ITEM of parent - - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, parsubvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_tree returned %08x\n", Status); - goto exit; - } - - if (keycmp(&searchkey, &tp.item->key)) { - ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, parsubvol->id); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - TRACE("fileref->parent->fcb->inode_item.st_size was %llx\n", fileref->parent->fcb->inode_item.st_size); - fileref->parent->fcb->inode_item.st_size -= bytecount * 2; - TRACE("fileref->parent->fcb->inode_item.st_size now %llx\n", fileref->parent->fcb->inode_item.st_size); - fileref->parent->fcb->inode_item.transid = fcb->Vcb->superblock.generation; - fileref->parent->fcb->inode_item.sequence++; - fileref->parent->fcb->inode_item.st_ctime = now; - fileref->parent->fcb->inode_item.st_mtime = now; + ExReleaseResourceLite(&fcb->Vcb->fcb_lock); +} - dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!dirii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - RtlCopyMemory(dirii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM)); - delete_tree_item(fcb->Vcb, &tp, rollback); - - insert_tree_item(fcb->Vcb, parsubvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback); - - parsubvol->root_item.ctransid = fcb->Vcb->superblock.generation; - parsubvol->root_item.ctime = now; - -success: - consider_write(fcb->Vcb); - - fileref->deleted = TRUE; - - fcb->Header.AllocationSize.QuadPart = 0; - fcb->Header.FileSize.QuadPart = 0; - fcb->Header.ValidDataLength.QuadPart = 0; - - if (FileObject && FileObject->PrivateCacheMap) { - CC_FILE_SIZES ccfs; - - ccfs.AllocationSize = fcb->Header.AllocationSize; - ccfs.FileSize = fcb->Header.FileSize; - ccfs.ValidDataLength = fcb->Header.ValidDataLength; - - CcSetFileSizes(FileObject, &ccfs); - } - - // FIXME - set deleted flag of any open FCBs for ADS - - if (FileObject && FileObject->FsContext2) { - ccb* ccb = FileObject->FsContext2; - - if (ccb->fileref) - send_notification_fileref(ccb->fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); - } - -#ifdef _DEBUG - time2 = KeQueryPerformanceCounter(NULL); +void mark_fcb_dirty(fcb* fcb) { + if (!fcb->dirty) { +#ifdef DEBUG_FCB_REFCOUNTS + LONG rc; #endif + dirty_fcb* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fcb), ALLOC_TAG); + + if (!dirt) { + ExFreePool("out of memory\n"); + return; + } + + fcb->dirty = TRUE; + +#ifdef DEBUG_FCB_REFCOUNTS + rc = InterlockedIncrement(&fcb->refcount); + WARN("fcb %p: refcount now %i\n", fcb, rc); +#else + InterlockedIncrement(&fcb->refcount); +#endif + + dirt->fcb = fcb; + + ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock); + } - TRACE("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart); + fcb->Vcb->need_write = TRUE; +} + +void mark_fileref_dirty(file_ref* fileref) { + if (!fileref->dirty) { + dirty_fileref* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fileref), ALLOC_TAG); + + if (!dirt) { + ExFreePool("out of memory\n"); + return; + } + + fileref->dirty = TRUE; + increase_fileref_refcount(fileref); + + dirt->fileref = fileref; + + ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock); + } - Status = STATUS_SUCCESS; - -exit: - if (utf8) - ExFreePool(utf8); - - return Status; + fileref->fcb->Vcb->need_write = TRUE; } void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) { @@ -2226,26 +1747,66 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) return; ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE); - - ExDeleteResourceLite(&fcb->nonpaged->resource); - ExDeleteResourceLite(&fcb->nonpaged->paging_resource); - ExFreePool(fcb->nonpaged); if (fcb->list_entry.Flink) RemoveEntryList(&fcb->list_entry); + if (fcb->list_entry_all.Flink) + RemoveEntryList(&fcb->list_entry_all); + + ExReleaseResourceLite(&fcb->Vcb->fcb_lock); + + ExDeleteResourceLite(&fcb->nonpaged->resource); + ExDeleteResourceLite(&fcb->nonpaged->paging_resource); + ExDeleteResourceLite(&fcb->nonpaged->index_lock); + ExFreePool(fcb->nonpaged); + if (fcb->sd) ExFreePool(fcb->sd); if (fcb->adsxattr.Buffer) ExFreePool(fcb->adsxattr.Buffer); + if (fcb->reparse_xattr.Buffer) + ExFreePool(fcb->reparse_xattr.Buffer); + + if (fcb->adsdata.Buffer) + ExFreePool(fcb->adsdata.Buffer); + if (fcb->debug_desc) ExFreePool(fcb->debug_desc); - FsRtlUninitializeFileLock(&fcb->lock); + while (!IsListEmpty(&fcb->extents)) { + LIST_ENTRY* le = RemoveHeadList(&fcb->extents); + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + ExFreePool(ext->data); + ExFreePool(ext); + } - ExReleaseResourceLite(&fcb->Vcb->fcb_lock); + while (!IsListEmpty(&fcb->index_list)) { + LIST_ENTRY* le = RemoveHeadList(&fcb->index_list); + index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry); + + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer); + ExFreePool(ie); + } + + while (!IsListEmpty(&fcb->hardlinks)) { + LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks); + hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); + + if (hl->name.Buffer) + ExFreePool(hl->name.Buffer); + + if (hl->utf8.Buffer) + ExFreePool(hl->utf8.Buffer); + + ExFreePool(hl); + } + + FsRtlUninitializeFileLock(&fcb->lock); ExFreePool(fcb); #ifdef DEBUG_FCB_REFCOUNTS @@ -2279,6 +1840,9 @@ void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned in if (rc > 0) return; + + if (fr->parent) + ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE); // FIXME - do we need a file_ref lock? @@ -2287,25 +1851,33 @@ void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned in if (fr->filepart.Buffer) ExFreePool(fr->filepart.Buffer); + if (fr->filepart_uc.Buffer) + ExFreePool(fr->filepart_uc.Buffer); + if (fr->utf8.Buffer) ExFreePool(fr->utf8.Buffer); - if (fr->full_filename.Buffer) - ExFreePool(fr->full_filename.Buffer); - if (fr->debug_desc) ExFreePool(fr->debug_desc); + ExDeleteResourceLite(&fr->nonpaged->children_lock); + + ExFreePool(fr->nonpaged); + // FIXME - throw error if children not empty - free_fcb(fr->fcb); + if (fr->fcb->fileref == fr) + fr->fcb->fileref = NULL; if (fr->list_entry.Flink) RemoveEntryList(&fr->list_entry); - if (fr->parent) - free_fileref((file_ref*)fr->parent); + if (fr->parent) { + ExReleaseResourceLite(&fr->parent->nonpaged->children_lock); + free_fileref(fr->parent); + } + free_fcb(fr->fcb); ExFreePool(fr); } @@ -2326,14 +1898,15 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb); - FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb); - // FIXME - make sure notification gets sent if file is being deleted if (ccb) { if (ccb->query_string.Buffer) RtlFreeUnicodeString(&ccb->query_string); + if (ccb->filename.Buffer) + ExFreePool(ccb->filename.Buffer); + // FIXME - use refcounts for fileref fileref = ccb->fileref; @@ -2355,32 +1928,67 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec } void STDCALL uninit(device_extension* Vcb, BOOL flush) { - chunk* c; space* s; UINT64 i; LIST_ENTRY rollback; + NTSTATUS Status; + LIST_ENTRY* le; + LARGE_INTEGER time; + + RemoveEntryList(&Vcb->list_entry); + + Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid); + if (!NT_SUCCESS(Status)) + WARN("registry_mark_volume_unmounted returned %08x\n", Status); if (flush) { InitializeListHead(&rollback); - acquire_tree_lock(Vcb, TRUE); + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->write_trees > 0) + if (Vcb->need_write) do_write(Vcb, &rollback); free_trees(Vcb); clear_rollback(&rollback); - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); } - // FIXME - stop async threads + for (i = 0; i < Vcb->threads.num_threads; i++) { + Vcb->threads.threads[i].quit = TRUE; + KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE); + + KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL); + + ZwClose(Vcb->threads.threads[i].handle); + } + + 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); free_fcb(Vcb->volume_fcb); - free_fileref(Vcb->root_fileref); - // FIXME - free any open fcbs? + if (Vcb->root_file) + ObDereferenceObject(Vcb->root_file); + + le = Vcb->chunks.Flink; + while (le != &Vcb->chunks) { + chunk* c = CONTAINING_RECORD(le, chunk, list_entry); + + if (c->cache) { + free_fcb(c->cache); + c->cache = NULL; + } + + le = le->Flink; + } while (!IsListEmpty(&Vcb->roots)) { LIST_ENTRY* le = RemoveHeadList(&Vcb->roots); @@ -2392,7 +2000,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { } while (!IsListEmpty(&Vcb->chunks)) { - LIST_ENTRY* le = RemoveHeadList(&Vcb->chunks); + chunk* c; + + le = RemoveHeadList(&Vcb->chunks); c = CONTAINING_RECORD(le, chunk, list_entry); while (!IsListEmpty(&c->space)) { @@ -2402,19 +2012,42 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { ExFreePool(s); } + while (!IsListEmpty(&c->deleting)) { + LIST_ENTRY* le2 = RemoveHeadList(&c->deleting); + s = CONTAINING_RECORD(le2, space, list_entry); + + ExFreePool(s); + } + if (c->devices) ExFreePool(c->devices); + if (c->cache) + free_fcb(c->cache); + + ExDeleteResourceLite(&c->nonpaged->lock); + ExDeleteResourceLite(&c->nonpaged->changed_extents_lock); + + ExFreePool(c->nonpaged); ExFreePool(c->chunk_item); ExFreePool(c); } + // FIXME - free any open fcbs? + + while (!IsListEmpty(&Vcb->sector_checksums)) { + LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums); + changed_sector* cs = (changed_sector*)le; + + ExFreePool(cs); + } + for (i = 0; i < Vcb->superblock.num_devices; i++) { - while (!IsListEmpty(&Vcb->devices[i].disk_holes)) { - LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].disk_holes); - disk_hole* dh = CONTAINING_RECORD(le, disk_hole, listentry); + while (!IsListEmpty(&Vcb->devices[i].space)) { + LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space); + space* s = CONTAINING_RECORD(le, space, list_entry); - ExFreePool(dh); + ExFreePool(s); } } @@ -2423,14 +2056,149 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) { ExDeleteResourceLite(&Vcb->fcb_lock); ExDeleteResourceLite(&Vcb->load_lock); ExDeleteResourceLite(&Vcb->tree_lock); + ExDeleteResourceLite(&Vcb->checksum_lock); + ExDeleteResourceLite(&Vcb->chunk_lock); ZwClose(Vcb->flush_thread_handle); } +NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) { + LARGE_INTEGER newlength, time; + BTRFS_TIME now; + NTSTATUS Status; + + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE); + + if (fileref->deleted) { + ExReleaseResourceLite(fileref->fcb->Header.Resource); + return STATUS_SUCCESS; + } + + fileref->deleted = TRUE; + mark_fileref_dirty(fileref); + + // delete INODE_ITEM (0x1) + + TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink); + + if (!fileref->fcb->ads) { + if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { + LIST_ENTRY* le; + + mark_fcb_dirty(fileref->fcb); + + if (fileref->fcb->inode_item.st_nlink > 1) { + fileref->fcb->inode_item.st_nlink--; + fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->inode_item.sequence++; + fileref->fcb->inode_item.st_ctime = now; + } else { + fileref->fcb->deleted = TRUE; + + // 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); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + ExReleaseResourceLite(fileref->fcb->Header.Resource); + return Status; + } + } + + fileref->fcb->Header.AllocationSize.QuadPart = 0; + fileref->fcb->Header.FileSize.QuadPart = 0; + fileref->fcb->Header.ValidDataLength.QuadPart = 0; + + if (FileObject) { + CC_FILE_SIZES ccfs; + + ccfs.AllocationSize = fileref->fcb->Header.AllocationSize; + ccfs.FileSize = fileref->fcb->Header.FileSize; + ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength; + + CcSetFileSizes(FileObject, &ccfs); + } + } + + le = fileref->fcb->hardlinks.Flink; + while (le != &fileref->fcb->hardlinks) { + hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); + + if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) { + RemoveEntryList(&hl->list_entry); + + if (hl->name.Buffer) + ExFreePool(hl->name.Buffer); + + if (hl->utf8.Buffer) + ExFreePool(hl->utf8.Buffer); + + ExFreePool(hl); + break; + } + + le = le->Flink; + } + } else { // subvolume + if (fileref->fcb->subvol->root_item.num_references > 1) { + fileref->fcb->subvol->root_item.num_references--; + + mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated + } else { + // FIXME - we need a lock here + + RemoveEntryList(&fileref->fcb->subvol->list_entry); + + InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry); + } + } + } else { + fileref->fcb->deleted = TRUE; + mark_fcb_dirty(fileref->fcb); + } + + // update INODE_ITEM of parent + + TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer); + ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE); + TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size); + fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2; + TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size); + fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; + fileref->parent->fcb->inode_item.sequence++; + fileref->parent->fcb->inode_item.st_ctime = now; + fileref->parent->fcb->inode_item.st_mtime = now; + ExReleaseResourceLite(fileref->parent->fcb->Header.Resource); + + mark_fcb_dirty(fileref->parent->fcb); + + send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + + fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->subvol->root_item.ctime = now; + + if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject) + CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE); + + newlength.QuadPart = 0; + + if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL)) + TRACE("CcUninitializeCacheMap failed\n"); + + ExReleaseResourceLite(fileref->fcb->Header.Resource); + + return STATUS_SUCCESS; +} + static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; + device_extension* Vcb = DeviceObject->DeviceExtension; fcb* fcb; BOOL top_level; @@ -2440,6 +2208,11 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit2; + } + if (DeviceObject == devobj) { TRACE("closing file system\n"); Status = STATUS_SUCCESS; @@ -2460,6 +2233,8 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) IoRemoveShareAccess(FileObject, &fcb->share_access); + FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb); + oc = InterlockedDecrement(&fcb->open_count); #ifdef DEBUG_FCB_REFCOUNTS ERR("fcb %p: open_count now %i\n", fcb, oc); @@ -2471,47 +2246,51 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) fileref->delete_on_close = FALSE; + if (Vcb->locked && Vcb->locked_fileobj == FileObject) { + TRACE("unlocking volume\n"); + do_unlock_volume(Vcb); + FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK); + } + if (oc == 0) { - if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) { + if (!Vcb->removing) { LIST_ENTRY rollback; + InitializeListHead(&rollback); - - acquire_tree_lock(fcb->Vcb, TRUE); - - Status = delete_fileref(fileref, FileObject, &rollback); - - if (NT_SUCCESS(Status)) { - LARGE_INTEGER newlength; - - if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) - CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE); + + if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) { + send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); - newlength.QuadPart = 0; + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); - if (!CcUninitializeCacheMap(FileObject, &newlength, NULL)) { - TRACE("CcUninitializeCacheMap failed\n"); + Status = delete_fileref(fileref, FileObject, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("delete_fileref returned %08x\n", Status); + do_rollback(Vcb, &rollback); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); + goto exit; } + ExReleaseResourceLite(&fcb->Vcb->tree_lock); clear_rollback(&rollback); - } else - do_rollback(fcb->Vcb, &rollback); - - release_tree_lock(fcb->Vcb, TRUE); - } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) { - IO_STATUS_BLOCK iosb; - CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb); - - if (!NT_SUCCESS(iosb.Status)) { - ERR("CcFlushCache returned %08x\n", iosb.Status); + } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) { + IO_STATUS_BLOCK iosb; + CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb); + + if (!NT_SUCCESS(iosb.Status)) { + ERR("CcFlushCache returned %08x\n", iosb.Status); + } + + if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) { + ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite(fcb->Header.PagingIoResource); + } + + CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE); + + TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n", + FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart); } - - ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE); - ExReleaseResourceLite(fcb->Header.PagingIoResource); - - CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE); - - TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n", - FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart); } if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb) @@ -2529,6 +2308,7 @@ exit: IoCompleteRequest(Irp, IO_NO_INCREMENT); +exit2: if (top_level) IoSetTopLevelIrp(NULL); @@ -2606,10 +2386,6 @@ ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r return att; } -// static int STDCALL utf8icmp(char* a, char* b) { -// return strcmp(a, b); // FIXME - don't treat as ASCII -// } - NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) { IO_STATUS_BLOCK* IoStatus; LARGE_INTEGER Offset; @@ -2714,7 +2490,7 @@ exit: return Status; } -static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) { +static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) { NTSTATUS Status; superblock* sb; unsigned int i, to_read; @@ -2731,7 +2507,7 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de i = 0; while (superblock_addrs[i] > 0) { - if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length) + if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length) break; Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb); @@ -2741,6 +2517,8 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de return Status; } + // FIXME - check checksum before accepting? + TRACE("got superblock %u!\n", i); if (i == 0 || sb->generation > Vcb->superblock.generation) @@ -2805,31 +2583,6 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID return Status; } -// static void STDCALL find_chunk_root(device_extension* Vcb) { -// UINT32 i; -// KEY* key; -// -// i = 0; -// while (i < Vcb->superblock.n) { -// key = &Vcb->superblock.sys_chunk_array[i]; -// i += sizeof(KEY); -// } -// -// // FIXME -// } - -// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) { -// if (!Vcb->log_to_phys) { -// Vcb->log_to_phys = ltp; -// ltp->next = NULL; -// return; -// } -// -// // FIXME - these should be ordered -// ltp->next = Vcb->log_to_phys; -// Vcb->log_to_phys = ltp; -// } - static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) { root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG); if (!r) { @@ -2841,7 +2594,6 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, r->path.Buffer = NULL; r->treeholder.address = addr; r->treeholder.tree = NULL; - init_tree_holder(&r->treeholder); InitializeListHead(&r->fcbs); r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG); @@ -2936,23 +2688,6 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) { return STATUS_SUCCESS; } -static NTSTATUS add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) { - disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG); - - if (!dh) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - dh->address = address; - dh->size = size; - dh->provisional = FALSE; - - InsertTailList(disk_holes, &dh->listentry); - - return STATUS_SUCCESS; -} - static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { KEY searchkey; traverse_ptr tp, next_tp; @@ -2960,7 +2695,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { UINT64 lastaddr; NTSTATUS Status; - InitializeListHead(&dev->disk_holes); + InitializeListHead(&dev->space); searchkey.obj_id = dev->devitem.dev_id; searchkey.obj_type = TYPE_DEV_EXTENT; @@ -2980,9 +2715,9 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data; if (tp.item->key.offset > lastaddr) { - Status = add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr); + Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr); if (!NT_SUCCESS(Status)) { - ERR("add_disk_hole returned %08x\n", Status); + ERR("add_space_entry returned %08x\n", Status); return Status; } } @@ -3003,22 +2738,20 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) { } while (b); if (lastaddr < dev->devitem.num_bytes) { - Status = add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr); + Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr); if (!NT_SUCCESS(Status)) { - ERR("add_disk_hole returned %08x\n", Status); + ERR("add_space_entry returned %08x\n", Status); return Status; } } - // FIXME - free disk_holes when unmounting - return STATUS_SUCCESS; } device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) { UINT64 i; - for (i = 0; i < Vcb->superblock.num_devices; i++) { + for (i = 0; i < Vcb->devices_loaded; i++) { TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i, Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7], Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]); @@ -3029,6 +2762,41 @@ device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) { } } + if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) { + LIST_ENTRY* le = volumes.Flink; + + while (le != &volumes) { + volume* v = CONTAINING_RECORD(le, volume, list_entry); + + if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && + RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) + ) { + NTSTATUS Status; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + + Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); + if (!NT_SUCCESS(Status)) { + ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status); + return NULL; + } + + DeviceObject = FileObject->DeviceObject; + + ObReferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + + Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject; + Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid; + Vcb->devices_loaded++; + + return &Vcb->devices[Vcb->devices_loaded - 1]; + } + + le = le->Flink; + } + } + WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7], uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]); @@ -3036,6 +2804,58 @@ device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) { return NULL; } +static BOOL is_device_removable(PDEVICE_OBJECT devobj) { + NTSTATUS Status; + STORAGE_HOTPLUG_INFO shi; + + Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL); + + if (!NT_SUCCESS(Status)) { + ERR("dev_ioctl returned %08x\n", Status); + return FALSE; + } + + return shi.MediaRemovable != 0 ? TRUE : FALSE; +} + +static ULONG get_device_change_count(PDEVICE_OBJECT devobj) { + NTSTATUS Status; + ULONG cc; + IO_STATUS_BLOCK iosb; + + Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) { + ERR("dev_ioctl returned %08x\n", Status); + return 0; + } + + if (iosb.Information < sizeof(ULONG)) { + ERR("iosb.Information was too short\n"); + return 0; + } + + return cc; +} + +static void init_device(device_extension* Vcb, device* dev, BOOL get_length) { + NTSTATUS Status; + GET_LENGTH_INFORMATION gli; + + dev->removable = is_device_removable(dev->devobj); + dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0; + + if (get_length) { + Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, + &gli, sizeof(gli), TRUE, NULL); + if (!NT_SUCCESS(Status)) { + ERR("error reading length information: %08x\n", Status); + } + + dev->length = gli.Length.QuadPart; + } +} + static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { traverse_ptr tp, next_tp; KEY searchkey; @@ -3048,6 +2868,8 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { searchkey.obj_type = 0; searchkey.offset = 0; + Vcb->data_flags = 0; + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); @@ -3057,10 +2879,71 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { do { TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) { - // FIXME - this is a hack; make this work with multiple devices! - if (tp.item->size > 0) - RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM))); + if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) { + if (tp.item->size < sizeof(DEV_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(DEV_ITEM)); + } else { + DEV_ITEM* di = (DEV_ITEM*)tp.item->data; + BOOL done = FALSE; + + for (i = 0; i < Vcb->devices_loaded; i++) { + if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { + RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM))); + + if (i > 0) + init_device(Vcb, &Vcb->devices[i], TRUE); + + done = TRUE; + break; + } + } + + if (!done) { + if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) { + LIST_ENTRY* le = volumes.Flink; + + while (le != &volumes) { + volume* v = CONTAINING_RECORD(le, volume, list_entry); + + if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && + RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) + ) { + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + + Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject); + if (!NT_SUCCESS(Status)) { + ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status); + return Status; + } + + DeviceObject = FileObject->DeviceObject; + + ObReferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + + Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject; + RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM))); + init_device(Vcb, &Vcb->devices[i], FALSE); + Vcb->devices[i].length = v->length; + Vcb->devices_loaded++; + + done = TRUE; + break; + } + + le = le->Flink; + } + + if (!done) { + ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset, + di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7], + di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]); + } + } else + ERR("unexpected device %llx found\n", tp.item->key.offset); + } + } } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) { 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)); @@ -3072,22 +2955,34 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { 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->space_changed = FALSE; - c->cache_inode = 0; - c->cache_size = 0; + c->cache = NULL; + c->created = FALSE; c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); if (!c->chunk_item) { ERR("out of memory\n"); + ExFreePool(c); + ExFreePool(c->nonpaged); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size); + if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags) + Vcb->data_flags = c->chunk_item->type; + if (c->chunk_item->num_stripes > 0) { CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; @@ -3095,6 +2990,9 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { if (!c->devices) { ERR("out of memory\n"); + ExFreePool(c); + ExFreePool(c->nonpaged); + ExFreePool(c->chunk_item); return STATUS_INSUFFICIENT_RESOURCES; } @@ -3105,9 +3003,17 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { } else c->devices = NULL; + ExInitializeResourceLite(&c->nonpaged->lock); + ExInitializeResourceLite(&c->nonpaged->changed_extents_lock); + InitializeListHead(&c->space); + InitializeListHead(&c->space_size); + InitializeListHead(&c->deleting); + InitializeListHead(&c->changed_extents); InsertTailList(&Vcb->chunks, &c->list_entry); + + c->list_entry_changed.Flink = NULL; } } @@ -3119,29 +3025,66 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) { Vcb->log_to_phys_loaded = TRUE; + if (Vcb->data_flags == 0) + Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0); + return STATUS_SUCCESS; } void protect_superblocks(device_extension* Vcb, chunk* c) { - int i = 0, j; + UINT16 i = 0, j; UINT64 off_start, off_end; - // FIXME - this will need modifying for RAID + // The Linux driver also protects all the space before the first superblock. + // I realize this confuses physical and logical addresses, but this is what btrfs-progs does - + // evidently Linux assumes the chunk at 0 is always SINGLE. + if (c->offset < superblock_addrs[0]) + space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL); while (superblock_addrs[i] != 0) { CHUNK_ITEM* ci = c->chunk_item; CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1]; - for (j = 0; j < ci->num_stripes; j++) { - if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) { - TRACE("cut out superblock in chunk %llx\n", c->offset); + if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) { + for (j = 0; j < ci->num_stripes; j++) { + ULONG sub_stripes = max(ci->sub_stripes, 1); - // The Linux driver protects the whole stripe in which the superblock lives - - off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length; - off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length); - - add_to_space_list(c, c->offset + off_start, off_end - off_start, SPACE_TYPE_USED); + if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) { +#ifdef _DEBUG + UINT64 startoff; + UINT16 startoffstripe; +#endif + + TRACE("cut out superblock in chunk %llx\n", c->offset); + + off_start = superblock_addrs[i] - cis[j].offset; + off_start -= off_start % ci->stripe_length; + off_start *= ci->num_stripes / sub_stripes; + off_start += (j / sub_stripes) * ci->stripe_length; + + off_end = off_start + ci->stripe_length; + +#ifdef _DEBUG + get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe); + TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe); + TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]); +#endif + + space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL); + } + } + } else { // SINGLE, DUPLICATE, RAID1 + for (j = 0; j < ci->num_stripes; j++) { + if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) { + TRACE("cut out superblock in chunk %llx\n", c->offset); + + // The Linux driver protects the whole stripe in which the superblock lives + + off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length; + off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length); + + space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL); + } } } @@ -3309,25 +3252,68 @@ static NTSTATUS load_sys_chunks(device_extension* Vcb) { } static root* find_default_subvol(device_extension* Vcb) { - root* subvol; - UINT64 inode; - UINT8 type; - UNICODE_STRING filename; LIST_ENTRY* le; - static WCHAR fn[] = L"default"; + static char fn[] = "default"; static UINT32 crc32 = 0x8dbfc2d2; if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) { - filename.Buffer = fn; - filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR); + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; + DIR_ITEM* di; - if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL)) - WARN("couldn't find default subvol DIR_ITEM, using default tree\n"); - else - return subvol; + searchkey.obj_id = Vcb->superblock.root_dir_objectid; + searchkey.obj_type = TYPE_DIR_ITEM; + searchkey.offset = crc32; + + Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; + } + + if (keycmp(&tp.item->key, &searchkey)) { + ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + goto end; + } + + if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); + goto end; + } + + di = (DIR_ITEM*)tp.item->data; + + if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) { + ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n); + goto end; + } + + if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) { + ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n"); + goto end; + } + + if (di->key.obj_type != TYPE_ROOT_ITEM) { + ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset); + goto end; + } + + le = Vcb->roots.Flink; + while (le != &Vcb->roots) { + root* r = CONTAINING_RECORD(le, root, list_entry); + + if (r->id == di->key.obj_id) + return r; + + le = le->Flink; + } + + ERR("could not find root %llx, using default instead\n", di->key.obj_id); } +end: le = Vcb->roots.Flink; while (le != &Vcb->roots) { root* r = CONTAINING_RECORD(le, root, list_entry); @@ -3341,38 +3327,95 @@ static root* find_default_subvol(device_extension* Vcb) { return NULL; } -static BOOL is_device_removable(PDEVICE_OBJECT devobj) { +static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) { + device_extension* Vcb = DeviceObject->DeviceExtension; + ULONG i; NTSTATUS Status; - STORAGE_HOTPLUG_INFO shi; - Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL); + Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors? - if (!NT_SUCCESS(Status)) { - ERR("dev_ioctl returned %08x\n", Status); + Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG); + if (!Vcb->threads.threads) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(Vcb->threads.threads, sizeof(drv_thread) * Vcb->threads.num_threads); + + for (i = 0; i < Vcb->threads.num_threads; i++) { + Vcb->threads.threads[i].DeviceObject = DeviceObject; + KeInitializeEvent(&Vcb->threads.threads[i].event, SynchronizationEvent, FALSE); + KeInitializeEvent(&Vcb->threads.threads[i].finished, NotificationEvent, FALSE); + InitializeListHead(&Vcb->threads.threads[i].jobs); + KeInitializeSpinLock(&Vcb->threads.threads[i].spin_lock); + + Status = PsCreateSystemThread(&Vcb->threads.threads[i].handle, 0, NULL, NULL, NULL, worker_thread, &Vcb->threads.threads[i]); + if (!NT_SUCCESS(Status)) { + ULONG j; + + ERR("PsCreateSystemThread returned %08x\n", Status); + + for (j = 0; j < i; j++) { + Vcb->threads.threads[i].quit = TRUE; + KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE); + } + + return Status; + } + } + + return STATUS_SUCCESS; +} + +BOOL add_thread_job(device_extension* Vcb, PIRP Irp) { + ULONG threadnum; + thread_job* tj; + + threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads; + + if (Vcb->threads.threads[threadnum].quit) + return FALSE; + + tj = ExAllocatePoolWithTag(NonPagedPool, sizeof(thread_job), ALLOC_TAG); + if (!tj) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); return FALSE; } - return shi.MediaRemovable != 0 ? TRUE : FALSE; + tj->Irp = Irp; + + ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock); + KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE); + + return TRUE; } -static ULONG get_device_change_count(PDEVICE_OBJECT devobj) { - NTSTATUS Status; - ULONG cc; - IO_STATUS_BLOCK iosb; +static BOOL raid_generations_okay(device_extension* Vcb) { + UINT64 i; - Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + // FIXME - if the difference between superblocks is small, we should try to recover - if (!NT_SUCCESS(Status)) { - ERR("dev_ioctl returned %08x\n", Status); - return 0; + for (i = 0; i < Vcb->superblock.num_devices; i++) { + LIST_ENTRY* le = volumes.Flink; + while (le != &volumes) { + volume* v = CONTAINING_RECORD(le, volume, list_entry); + + if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && + RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) + ) { + if (v->gen1 != Vcb->superblock.generation - 1) { + WARN("device %llu had generation %llx, expected %llx\n", i, v->gen1, Vcb->superblock.generation - 1); + return FALSE; + } else + break; + } + le = le->Flink; + } } - if (iosb.Information < sizeof(ULONG)) { - ERR("iosb.Information was too short\n"); - return 0; - } - - return cc; + return TRUE; } static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { @@ -3381,12 +3424,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PDEVICE_OBJECT DeviceToMount; NTSTATUS Status; device_extension* Vcb = NULL; - PARTITION_INFORMATION_EX piex; + GET_LENGTH_INFORMATION gli; UINT64 i; LIST_ENTRY* le; KEY searchkey; traverse_ptr tp; fcb* root_fcb = NULL; + ccb* root_ccb = NULL; TRACE("mount_vol called\n"); @@ -3399,16 +3443,10 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Stack = IoGetCurrentIrpStackLocation(Irp); DeviceToMount = Stack->Parameters.MountVolume.DeviceObject; -// Status = NtfsHasFileSystem(DeviceToMount); -// if (!NT_SUCCESS(Status)) -// { -// goto ByeBye; -// } - - Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, - &piex, sizeof(piex), TRUE, NULL); + Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, + &gli, sizeof(gli), TRUE, NULL); if (!NT_SUCCESS(Status)) { - ERR("error reading partition information: %08x\n", Status); + ERR("error reading length information: %08x\n", Status); Status = STATUS_UNRECOGNIZED_VOLUME; goto exit; } @@ -3431,14 +3469,16 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NewDeviceObject->Flags |= DO_DIRECT_IO; Vcb = (PVOID)NewDeviceObject->DeviceExtension; RtlZeroMemory(Vcb, sizeof(device_extension)); + Vcb->type = VCB_TYPE_VOLUME; ExInitializeResourceLite(&Vcb->tree_lock); - Vcb->tree_lock_counter = 0; Vcb->open_trees = 0; - Vcb->write_trees = 0; + Vcb->need_write = FALSE; ExInitializeResourceLite(&Vcb->fcb_lock); ExInitializeResourceLite(&Vcb->DirResource); + ExInitializeResourceLite(&Vcb->checksum_lock); + ExInitializeResourceLite(&Vcb->chunk_lock); ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE); InsertTailList(&VcbList, &Vcb->list_entry); @@ -3469,10 +3509,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector); // } - Vcb->length = piex.PartitionLength.QuadPart; - TRACE("partition length = %u\n", piex.PartitionLength); + TRACE("partition length = %llx\n", gli.Length.QuadPart); - Status = read_superblock(Vcb, DeviceToMount); + Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart); if (!NT_SUCCESS(Status)) { Status = STATUS_UNRECOGNIZED_VOLUME; goto exit; @@ -3485,6 +3524,18 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { TRACE("btrfs magic found\n"); } + + Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options); + if (!NT_SUCCESS(Status)) { + ERR("registry_load_volume_options returned %08x\n", Status); + goto exit; + } + + if (Vcb->options.ignore) { + TRACE("ignoring volume\n"); + Status = STATUS_UNRECOGNIZED_VOLUME; + goto exit; + } if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) { WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED); @@ -3498,7 +3549,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) { // skipping over device in RAID which isn't the first one - // FIXME - hide this in My Computer Status = STATUS_UNRECOGNIZED_VOLUME; goto exit; } @@ -3506,13 +3556,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { le = le->Flink; } - // FIXME - remove this when we can - if (Vcb->superblock.num_devices > 1) { - WARN("not mounting - multiple devices not yet implemented\n"); - Status = STATUS_UNRECOGNIZED_VOLUME; - goto exit; - } - Vcb->readonly = FALSE; if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) { WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED); @@ -3531,13 +3574,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->devices[0].devobj = DeviceToMount; RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM)); - Vcb->devices[0].removable = is_device_removable(Vcb->devices[0].devobj); - Vcb->devices[0].change_count = Vcb->devices[0].removable ? get_device_change_count(Vcb->devices[0].devobj) : 0; + init_device(Vcb, &Vcb->devices[0], FALSE); + Vcb->devices[0].length = gli.Length.QuadPart; if (Vcb->superblock.num_devices > 1) RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1)); - // FIXME - find other devices, if there are any + Vcb->devices_loaded = 1; TRACE("DeviceToMount = %p\n", DeviceToMount); TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb); @@ -3545,19 +3588,12 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NewDeviceObject->StackSize = DeviceToMount->StackSize + 1; NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; -// find_chunk_root(Vcb); -// Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped) - -// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr); - InitializeListHead(&Vcb->roots); InitializeListHead(&Vcb->drop_roots); Vcb->log_to_phys_loaded = FALSE; Vcb->max_inline = Vcb->superblock.node_size / 2; - -// Vcb->write_trees = NULL; add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL); @@ -3566,7 +3602,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = STATUS_INTERNAL_ERROR; goto exit; } - + InitializeListHead(&Vcb->sys_chunks); Status = load_sys_chunks(Vcb); if (!NT_SUCCESS(Status)) { @@ -3575,7 +3611,17 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } InitializeListHead(&Vcb->chunks); + InitializeListHead(&Vcb->chunks_changed); InitializeListHead(&Vcb->trees); + InitializeListHead(&Vcb->all_fcbs); + InitializeListHead(&Vcb->dirty_fcbs); + InitializeListHead(&Vcb->dirty_filerefs); + InitializeListHead(&Vcb->shared_extents); + InitializeListHead(&Vcb->sector_checksums); + + KeInitializeSpinLock(&Vcb->dirty_fcbs_lock); + KeInitializeSpinLock(&Vcb->dirty_filerefs_lock); + KeInitializeSpinLock(&Vcb->shared_extents_lock); InitializeListHead(&Vcb->DirNotifyList); @@ -3587,6 +3633,26 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto exit; } + if (Vcb->superblock.num_devices > 1) { + if (Vcb->devices_loaded < Vcb->superblock.num_devices) { + ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded); + + IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL); + + Status = STATUS_INTERNAL_ERROR; + goto exit; + } + + if (!raid_generations_okay(Vcb)) { + ERR("could not mount as generation mismatch\n"); + + IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL); + + Status = STATUS_INTERNAL_ERROR; + goto exit; + } + } + add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL); if (!Vcb->root_root) { @@ -3606,7 +3672,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 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); @@ -3683,18 +3749,26 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Vcb->root_fileref->fcb = root_fcb; InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all); - Vcb->root_fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG); + root_fcb->fileref = Vcb->root_fileref; - if (!Vcb->root_fileref->full_filename.Buffer) { + root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG); + if (!root_ccb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } - Vcb->root_fileref->full_filename.Buffer[0] = '\\'; - Vcb->root_fileref->full_filename.Length = Vcb->root_fileref->full_filename.MaximumLength = sizeof(WCHAR); - + Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount); + Vcb->root_file->FsContext = root_fcb; + + RtlZeroMemory(root_ccb, sizeof(ccb)); + root_ccb->NodeType = BTRFS_NODE_TYPE_CCB; + root_ccb->NodeSize = sizeof(ccb); + + Vcb->root_file->FsContext = root_ccb; + for (i = 0; i < Vcb->superblock.num_devices; i++) { Status = find_disk_holes(Vcb, &Vcb->devices[i]); if (!NT_SUCCESS(Status)) { @@ -3705,74 +3779,18 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { // root_test(Vcb); -// Vcb->StreamFileObject = IoCreateStreamFileObject(NULL, -// Vcb->StorageDevice); -// -// InitializeListHead(&Vcb->FcbListHead); -// -// Fcb = NtfsCreateFCB(NULL, Vcb); -// if (Fcb == NULL) -// { -// Status = STATUS_INSUFFICIENT_RESOURCES; -// goto ByeBye; -// } -// -// Ccb = ExAllocatePoolWithTag(NonPagedPool, -// sizeof(NTFS_CCB), -// TAG_CCB); -// if (Ccb == NULL) -// { -// Status = STATUS_INSUFFICIENT_RESOURCES; -// goto ByeBye; -// } -// -// RtlZeroMemory(Ccb, sizeof(NTFS_CCB)); -// -// Ccb->Identifier.Type = NTFS_TYPE_CCB; -// Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB); -// -// Vcb->StreamFileObject->FsContext = Fcb; -// Vcb->StreamFileObject->FsContext2 = Ccb; -// Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; -// Vcb->StreamFileObject->PrivateCacheMap = NULL; -// Vcb->StreamFileObject->Vpb = Vcb->Vpb; -// Ccb->PtrFileObject = Vcb->StreamFileObject; -// Fcb->FileObject = Vcb->StreamFileObject; -// Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice; -// -// Fcb->Flags = FCB_IS_VOLUME_STREAM; -// -// Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; -// Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; -// Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */ -// -// CcInitializeCacheMap(Vcb->StreamFileObject, -// (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), -// FALSE, -// &(NtfsGlobalData->CacheMgrCallbacks), -// Fcb); -// -// ExInitializeResourceLite(&Vcb->LogToPhysLock); - KeInitializeSpinLock(&Vcb->FcbListLock); -// -// /* Get serial number */ -// NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber; -// -// /* Get volume label */ -// NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength; -// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel, -// Vcb->NtfsInfo.VolumeLabel, -// Vcb->NtfsInfo.VolumeLabelLength); NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb; Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject; - Stack->Parameters.MountVolume.Vpb->RealDevice = DeviceToMount; Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED; NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME NewDeviceObject->Vpb->VolumeLabel[0] = '?'; NewDeviceObject->Vpb->VolumeLabel[1] = 0; NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point? + Vcb->Vpb = NewDeviceObject->Vpb; + + KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE); Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject); if (!NT_SUCCESS(Status)) { @@ -3780,32 +3798,28 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) { goto exit; } + Status = create_worker_threads(NewDeviceObject); + if (!NT_SUCCESS(Status)) { + ERR("create_worker_threads returned %08x\n", Status); + goto exit; + } + + Status = registry_mark_volume_mounted(&Vcb->superblock.uuid); + if (!NT_SUCCESS(Status)) + WARN("registry_mark_volume_mounted returned %08x\n", Status); + Status = STATUS_SUCCESS; exit: -// if (!NT_SUCCESS(Status)) -// { -// /* Cleanup */ -// if (Vcb && Vcb->StreamFileObject) -// ObDereferenceObject(Vcb->StreamFileObject); -// -// if (Fcb) -// ExFreePool(Fcb); -// -// if (Ccb) -// ExFreePool(Ccb); -// -// if (NewDeviceObject) -// IoDeleteDevice(NewDeviceObject); -// } - if (Vcb) { ExReleaseResourceLite(&Vcb->load_lock); } if (!NT_SUCCESS(Status)) { if (Vcb) { - if (Vcb->root_fileref) + if (Vcb->root_file) + ObDereferenceObject(Vcb->root_file); + else if (Vcb->root_fileref) free_fileref(Vcb->root_fileref); else if (root_fcb) free_fcb(root_fcb); @@ -3817,6 +3831,8 @@ exit: ExDeleteResourceLite(&Vcb->load_lock); ExDeleteResourceLite(&Vcb->fcb_lock); ExDeleteResourceLite(&Vcb->DirResource); + ExDeleteResourceLite(&Vcb->checksum_lock); + ExDeleteResourceLite(&Vcb->chunk_lock); if (Vcb->devices) ExFreePoolWithTag(Vcb->devices, ALLOC_TAG); @@ -3826,7 +3842,8 @@ exit: if (NewDeviceObject) IoDeleteDevice(NewDeviceObject); - } + } else + FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT); TRACE("mount_vol done (status: %lx)\n", Status); @@ -3835,7 +3852,8 @@ exit: static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IrpSp; - NTSTATUS status; + NTSTATUS Status; + device_extension* Vcb = DeviceObject->DeviceExtension; BOOL top_level; TRACE("file system control\n"); @@ -3844,7 +3862,12 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, top_level = is_top_level(Irp); - status = STATUS_NOT_IMPLEMENTED; + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + + Status = STATUS_NOT_IMPLEMENTED; IrpSp = IoGetCurrentIrpStackLocation( Irp ); @@ -3854,26 +3877,19 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, case IRP_MN_MOUNT_VOLUME: TRACE("IRP_MN_MOUNT_VOLUME\n"); -// Irp->IoStatus.Status = STATUS_SUCCESS; - status = mount_vol(DeviceObject, Irp); -// IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de; -// IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef; - -// IoCompleteRequest( Irp, IO_DISK_INCREMENT ); - -// return Irp->IoStatus.Status; + Status = mount_vol(DeviceObject, Irp); break; case IRP_MN_KERNEL_CALL: TRACE("IRP_MN_KERNEL_CALL\n"); - status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE); + Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE); break; case IRP_MN_USER_FS_REQUEST: TRACE("IRP_MN_USER_FS_REQUEST\n"); - status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE); + Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE); break; case IRP_MN_VERIFY_VOLUME: @@ -3884,34 +3900,42 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, break; } - Irp->IoStatus.Status = status; + Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); +exit: if (top_level) IoSetTopLevelIrp(NULL); FsRtlExitFileSystem(); - return status; + return Status; } static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); fcb* fcb = IrpSp->FileObject->FsContext; + device_extension* Vcb = DeviceObject->DeviceExtension; BOOL top_level; FsRtlEnterFileSystem(); top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + TRACE("lock control\n"); Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL); fcb->Header.IsFastIoPossible = fast_io_possible(fcb); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -3920,6 +3944,101 @@ static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP return Status; } +NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + NTSTATUS Status; + part0_device_extension* p0de = DeviceObject->DeviceExtension; + + IoSkipCurrentIrpStackLocation(Irp); + + Status = IoCallDriver(p0de->devobj, Irp); + + return Status; +} + +static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + NTSTATUS Status; + part0_device_extension* p0de = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode); + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: + { + MOUNTDEV_UNIQUE_ID* mduid; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { + Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + mduid = Irp->AssociatedIrp.SystemBuffer; + mduid->UniqueIdLength = sizeof(BTRFS_UUID); + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) { + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID)); + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + } + + case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: + { + PMOUNTDEV_NAME name; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { + Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + name = Irp->AssociatedIrp.SystemBuffer; + name->NameLength = p0de->name.Length; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME) - 1 + name->NameLength) { + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length); + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) - 1 + name->NameLength; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + } + } + + IoSkipCurrentIrpStackLocation(Irp); + + Status = IoCallDriver(p0de->devobj, Irp); + + TRACE("returning %08x\n", Status); + + return Status; +} + static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); @@ -3928,21 +4047,23 @@ static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PI fcb* fcb; BOOL top_level; - FIXME("STUB: device control\n"); - FsRtlEnterFileSystem(); top_level = is_top_level(Irp); Irp->IoStatus.Information = 0; - WARN("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_device_control(DeviceObject, Irp); + goto end2; + } + + TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode); if (!FileObject) { ERR("FileObject was NULL\n"); Status = STATUS_INVALID_PARAMETER; goto end; - } fcb = FileObject->FsContext; @@ -3953,33 +4074,24 @@ static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PI goto end; } - if (fcb == Vcb->volume_fcb) { - FIXME("FIXME - pass through\n"); + if (fcb != Vcb->volume_fcb) { Status = STATUS_NOT_IMPLEMENTED; - } else { - TRACE("filename = %S\n", file_desc(FileObject)); - - switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { - case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: - TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n"); - Status = STATUS_INVALID_PARAMETER; - break; - - default: - WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n", - IrpSp->Parameters.DeviceIoControl.IoControlCode, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xff0000) >> 16, - (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xc000) >> 14, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3ffc) >> 2, - IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3); - Status = STATUS_INVALID_PARAMETER; - break; - } + goto end; } + IoSkipCurrentIrpStackLocation(Irp); + + Status = IoCallDriver(Vcb->devices[0].devobj, Irp); + + goto end2; + end: Irp->IoStatus.Status = Status; - IoCompleteRequest( Irp, IO_NO_INCREMENT ); + if (Status != STATUS_PENDING) + IoCompleteRequest(Irp, IO_NO_INCREMENT); +end2: if (top_level) IoSetTopLevelIrp(NULL); @@ -3991,18 +4103,24 @@ end: static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; BOOL top_level; + device_extension* Vcb = DeviceObject->DeviceExtension; - ERR("shutdown\n"); + TRACE("shutdown\n"); FsRtlEnterFileSystem(); top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + Status = STATUS_SUCCESS; while (!IsListEmpty(&VcbList)) { LIST_ENTRY* le = RemoveHeadList(&VcbList); - device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); + Vcb = CONTAINING_RECORD(le, device_extension, list_entry); TRACE("shutting down Vcb %p\n", Vcb); @@ -4013,7 +4131,8 @@ static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); - + +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -4072,250 +4191,6 @@ static void STDCALL check_cpu() { } #endif -static void STDCALL read_registry(PUNICODE_STRING regpath) { - HANDLE h; - UNICODE_STRING us; - OBJECT_ATTRIBUTES oa; - ULONG dispos; - NTSTATUS Status; - WCHAR* path; - ULONG kvfilen, retlen, i; - KEY_VALUE_FULL_INFORMATION* kvfi; - - const WCHAR mappings[] = L"\\Mappings"; -#ifndef __REACTOS__ - static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log"; -#endif - - path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG); - if (!path) { - ERR("out of memory\n"); - return; - } - - RtlCopyMemory(path, regpath->Buffer, regpath->Length); - RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR)); - - us.Buffer = path; - us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR)); - - InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - // FIXME - keep open and do notify for changes - Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); - - if (!NT_SUCCESS(Status)) { - ERR("ZwCreateKey returned %08x\n", Status); - ExFreePool(path); - return; - } - - if (dispos == REG_OPENED_EXISTING_KEY) { - kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256; - kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); - - if (!kvfi) { - ERR("out of memory\n"); - ExFreePool(path); - ZwClose(h); - return; - } - - i = 0; - do { - Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen); - - if (NT_SUCCESS(Status) && kvfi->DataLength > 0) { - UINT32 val = 0; - - RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32))); - - TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val); - - add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val); - } - - i = i + 1; - } while (Status != STATUS_NO_MORE_ENTRIES); - } - - ZwClose(h); - - ExFreePool(path); - -#ifdef _DEBUG - InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); - - if (!NT_SUCCESS(Status)) { - ERR("ZwCreateKey returned %08x\n", Status); - return; - } - - RtlInitUnicodeString(&us, L"DebugLogLevel"); - - kvfi = NULL; - kvfilen = 0; - Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); - - if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { - kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); - - if (!kvfi) { - ERR("out of memory\n"); - ZwClose(h); - return; - } - - 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)); - } 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)); - if (!NT_SUCCESS(Status)) { - ERR("ZwSetValueKey reutrned %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)); - - if (!NT_SUCCESS(Status)) { - ERR("ZwSetValueKey reutrned %08x\n", Status); - } - } else { - ERR("ZwQueryValueKey returned %08x\n", Status); - } - - RtlInitUnicodeString(&us, L"LogDevice"); - - kvfi = NULL; - kvfilen = 0; - Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); - - if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { - kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); - - if (!kvfi) { - ERR("out of memory\n"); - ZwClose(h); - return; - } - - Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); - - if (NT_SUCCESS(Status)) { - if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { - log_device.Length = log_device.MaximumLength = kvfi->DataLength; - log_device.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG); - - if (!log_device.Buffer) { - ERR("out of memory\n"); - ExFreePool(kvfi); - ZwClose(h); - return; - } - - RtlCopyMemory(log_device.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength); - - if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0) - log_device.Length -= sizeof(WCHAR); - } else { - ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength); - - Status = ZwDeleteValueKey(h, &us); - if (!NT_SUCCESS(Status)) { - ERR("ZwDeleteValueKey returned %08x\n", Status); - } - } - } - - ExFreePool(kvfi); - } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { - ERR("ZwQueryValueKey returned %08x\n", Status); - } - - RtlInitUnicodeString(&us, L"LogFile"); - - kvfi = NULL; - kvfilen = 0; - Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); - - if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { - kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); - - if (!kvfi) { - ERR("out of memory\n"); - ZwClose(h); - return; - } - - Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); - - if (NT_SUCCESS(Status)) { - if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { - log_file.Length = log_file.MaximumLength = kvfi->DataLength; - log_file.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG); - - if (!log_file.Buffer) { - ERR("out of memory\n"); - ExFreePool(kvfi); - ZwClose(h); - return; - } - - RtlCopyMemory(log_file.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength); - - if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0) - log_file.Length -= sizeof(WCHAR); - } else { - ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength); - - Status = ZwDeleteValueKey(h, &us); - if (!NT_SUCCESS(Status)) { - ERR("ZwDeleteValueKey returned %08x\n", Status); - } - } - } - - ExFreePool(kvfi); - } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { - Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file, (wcslen(def_log_file) + 1) * sizeof(WCHAR)); - - if (!NT_SUCCESS(Status)) { - ERR("ZwSetValueKey returned %08x\n", Status); - } - } else { - ERR("ZwQueryValueKey returned %08x\n", Status); - } - - if (log_file.Length == 0) { - log_file.Length = log_file.MaximumLength = wcslen(def_log_file) * sizeof(WCHAR); - log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG); - - if (!log_file.Buffer) { - ERR("out of memory\n"); - ZwClose(h); - return; - } - - RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length); - } - - ZwClose(h); -#endif -} - #ifdef _DEBUG static void init_logging() { if (log_device.Length > 0) @@ -4418,6 +4293,16 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist #endif TRACE("DriverEntry\n"); + + registry_path.Length = registry_path.MaximumLength = RegistryPath->Length; + registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG); + + if (!registry_path.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length); #ifndef __REACTOS__ check_cpu(); @@ -4457,7 +4342,7 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist dosdevice_nameW.Buffer = dosdevice_name; dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR); - Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &DeviceObject); + Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { ERR("IoCreateDevice returned %08x\n", Status); return Status; @@ -4480,7 +4365,7 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist } InitializeListHead(&volumes); - look_for_vols(&volumes); + look_for_vols(DriverObject, &volumes); InitializeListHead(&VcbList); ExInitializeResourceLite(&global_loading_lock); diff --git a/reactos/drivers/filesystems/btrfs/btrfs.rc b/reactos/drivers/filesystems/btrfs/btrfs.rc index b3ba777d68e..3246f5dc389 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs.rc +++ b/reactos/drivers/filesystems/btrfs/btrfs.rc @@ -70,12 +70,12 @@ BEGIN BLOCK "080904b0" BEGIN VALUE "FileDescription", "WinBtrfs" - VALUE "FileVersion", "0.4" + VALUE "FileVersion", "0.5" VALUE "InternalName", "btrfs" VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016" VALUE "OriginalFilename", "btrfs.sys" VALUE "ProductName", "WinBtrfs" - VALUE "ProductVersion", "0.4" + VALUE "ProductVersion", "0.5" END END BLOCK "VarFileInfo" diff --git a/reactos/drivers/filesystems/btrfs/btrfs_drv.h b/reactos/drivers/filesystems/btrfs/btrfs_drv.h index 0aae32065b3..d0b62fadd5f 100644 --- a/reactos/drivers/filesystems/btrfs/btrfs_drv.h +++ b/reactos/drivers/filesystems/btrfs/btrfs_drv.h @@ -73,6 +73,8 @@ #define READ_AHEAD_GRANULARITY 0x10000 // 64 KB +#define MAX_EXTENT_SIZE 0x8000000 // 128 MB + #ifdef _MSC_VER #define try __try #define except __except @@ -88,11 +90,12 @@ struct device_extension; typedef struct { - PDEVICE_OBJECT devobj; BTRFS_UUID fsuuid; BTRFS_UUID devuuid; UINT64 devnum; UNICODE_STRING devpath; + UINT64 length; + UINT64 gen1, gen2; BOOL processed; LIST_ENTRY list_entry; } volume; @@ -102,10 +105,42 @@ typedef struct _fcb_nonpaged { SECTION_OBJECT_POINTERS segment_object; ERESOURCE resource; ERESOURCE paging_resource; + ERESOURCE index_lock; } fcb_nonpaged; struct _root; +typedef struct { + UINT64 offset; + EXTENT_DATA* data; + ULONG datalen; + BOOL unique; + BOOL ignore; + + LIST_ENTRY list_entry; +} extent; + +typedef struct { + UINT32 hash; + KEY key; + UINT8 type; + UINT64 index; + ANSI_STRING utf8; + UNICODE_STRING filepart_uc; + + LIST_ENTRY list_entry; +} index_entry; + +typedef struct { + UINT64 parent; + UINT64 index; + UNICODE_STRING name; + ANSI_STRING utf8; + LIST_ENTRY list_entry; +} hardlink; + +struct _file_ref; + typedef struct _fcb { FSRTL_ADVANCED_FCB_HEADER Header; struct _fcb_nonpaged* nonpaged; @@ -123,33 +158,66 @@ typedef struct _fcb { ULONG atts; SHARE_ACCESS share_access; WCHAR* debug_desc; + LIST_ENTRY extents; + UINT64 last_dir_index; + ANSI_STRING reparse_xattr; + LIST_ENTRY hardlinks; + struct _file_ref* fileref; + + BOOL index_loaded; + LIST_ENTRY index_list; + + BOOL dirty; + BOOL sd_dirty; + BOOL atts_changed, atts_deleted; + BOOL extents_changed; + BOOL reparse_xattr_changed; + BOOL created; BOOL ads; - UINT32 adssize; UINT32 adshash; ANSI_STRING adsxattr; + ANSI_STRING adsdata; LIST_ENTRY list_entry; + LIST_ENTRY list_entry_all; } fcb; -struct _file_ref; +typedef struct { + fcb* fcb; + LIST_ENTRY list_entry; +} dirty_fcb; + +typedef struct { + ERESOURCE children_lock; +} file_ref_nonpaged; typedef struct _file_ref { fcb* fcb; UNICODE_STRING filepart; + UNICODE_STRING filepart_uc; ANSI_STRING utf8; - UNICODE_STRING full_filename; - ULONG name_offset; + ANSI_STRING oldutf8; + UINT64 index; BOOL delete_on_close; BOOL deleted; + BOOL created; + file_ref_nonpaged* nonpaged; LIST_ENTRY children; LONG refcount; struct _file_ref* parent; WCHAR* debug_desc; + BOOL dirty; + LIST_ENTRY list_entry; } file_ref; +typedef struct { + file_ref* fileref; + LIST_ENTRY list_entry; +} dirty_fileref; + typedef struct _ccb { USHORT NodeType; CSHORT NodeSize; @@ -162,6 +230,7 @@ typedef struct _ccb { BOOL specific_file; ACCESS_MASK access; file_ref* fileref; + UNICODE_STRING filename; } ccb; // typedef struct _log_to_phys { @@ -259,47 +328,77 @@ typedef struct _root_cache { struct _root_cache* next; } root_cache; -#define SPACE_TYPE_FREE 0 -#define SPACE_TYPE_USED 1 -#define SPACE_TYPE_DELETING 2 -#define SPACE_TYPE_WRITING 3 - -typedef struct { - UINT64 offset; - UINT64 size; - UINT8 type; - LIST_ENTRY list_entry; -} space; - typedef struct { UINT64 address; UINT64 size; - BOOL provisional; - LIST_ENTRY listentry; -} disk_hole; + LIST_ENTRY list_entry; + LIST_ENTRY list_entry_size; +} space; typedef struct { PDEVICE_OBJECT devobj; DEV_ITEM devitem; BOOL removable; ULONG change_count; - LIST_ENTRY disk_holes; + UINT64 length; + LIST_ENTRY space; } device; +typedef struct { + ERESOURCE lock; + ERESOURCE changed_extents_lock; +} chunk_nonpaged; + typedef struct { CHUNK_ITEM* chunk_item; UINT32 size; UINT64 offset; UINT64 used; UINT32 oldused; - BOOL space_changed; device** devices; - UINT64 cache_size; - UINT64 cache_inode; + fcb* cache; LIST_ENTRY space; + LIST_ENTRY space_size; + LIST_ENTRY deleting; + LIST_ENTRY changed_extents; + chunk_nonpaged* nonpaged; + BOOL created; + LIST_ENTRY list_entry; + LIST_ENTRY list_entry_changed; } chunk; +typedef struct { + UINT64 address; + UINT64 size; + UINT64 old_size; + UINT64 count; + UINT64 old_count; + BOOL no_csum; + LIST_ENTRY refs; + LIST_ENTRY old_refs; + LIST_ENTRY list_entry; +} changed_extent; + +typedef struct { + EXTENT_DATA_REF edr; + LIST_ENTRY list_entry; +} changed_extent_ref; + +typedef struct { + UINT64 address; + UINT64 size; + EXTENT_DATA_REF edr; + LIST_ENTRY list_entry; +} shared_data_entry; + +typedef struct { + UINT64 address; + UINT64 parent; + LIST_ENTRY entries; + LIST_ENTRY list_entry; +} shared_data; + typedef struct { KEY key; void* data; @@ -307,14 +406,46 @@ typedef struct { LIST_ENTRY list_entry; } sys_chunk; +typedef struct { + PIRP Irp; + LIST_ENTRY list_entry; +} thread_job; + +typedef struct { + PDEVICE_OBJECT DeviceObject; + HANDLE handle; + KEVENT event, finished; + BOOL quit; + LIST_ENTRY jobs; + KSPIN_LOCK spin_lock; +} drv_thread; + +typedef struct { + ULONG num_threads; + LONG next_thread; + drv_thread* threads; +} drv_threads; + +typedef struct { + BOOL ignore; +} mount_options; + +#define VCB_TYPE_VOLUME 1 +#define VCB_TYPE_PARTITION0 2 + typedef struct _device_extension { + UINT32 type; + mount_options options; + PVPB Vpb; device* devices; + UINT64 devices_loaded; // DISK_GEOMETRY geometry; - UINT64 length; superblock superblock; // WCHAR label[MAX_LABEL_SIZE]; BOOL readonly; BOOL removing; + BOOL locked; + PFILE_OBJECT locked_fileobj; fcb* volume_fcb; file_ref* root_fileref; ERESOURCE DirResource; @@ -324,12 +455,12 @@ typedef struct _device_extension { ERESOURCE tree_lock; PNOTIFY_SYNC NotifySync; LIST_ENTRY DirNotifyList; - LONG tree_lock_counter; LONG open_trees; - ULONG write_trees; + BOOL need_write; // ERESOURCE LogToPhysLock; // UINT64 chunk_root_phys_addr; UINT64 root_tree_phys_addr; + UINT64 data_flags; // log_to_phys* log_to_phys; LIST_ENTRY roots; LIST_ENTRY drop_roots; @@ -343,11 +474,33 @@ typedef struct _device_extension { UINT32 max_inline; LIST_ENTRY sys_chunks; LIST_ENTRY chunks; + LIST_ENTRY chunks_changed; LIST_ENTRY trees; + LIST_ENTRY all_fcbs; + LIST_ENTRY dirty_fcbs; + KSPIN_LOCK dirty_fcbs_lock; + LIST_ENTRY dirty_filerefs; + KSPIN_LOCK dirty_filerefs_lock; + ERESOURCE checksum_lock; + ERESOURCE chunk_lock; + LIST_ENTRY sector_checksums; + LIST_ENTRY shared_extents; + KSPIN_LOCK shared_extents_lock; HANDLE flush_thread_handle; + KTIMER flush_thread_timer; + KEVENT flush_thread_finished; + drv_threads threads; + PFILE_OBJECT root_file; LIST_ENTRY list_entry; } device_extension; +typedef struct { + UINT32 type; + PDEVICE_OBJECT devobj; + BTRFS_UUID uuid; + UNICODE_STRING name; +} part0_device_extension; + typedef struct { LIST_ENTRY listentry; PSID sid; @@ -366,39 +519,37 @@ typedef struct { BOOL deleted; } changed_sector; -enum write_tree_status { - WriteTreeStatus_Pending, - WriteTreeStatus_Success, - WriteTreeStatus_Error, - WriteTreeStatus_Cancelling, - WriteTreeStatus_Cancelled +enum write_data_status { + WriteDataStatus_Pending, + WriteDataStatus_Success, + WriteDataStatus_Error, + WriteDataStatus_Cancelling, + WriteDataStatus_Cancelled, + WriteDataStatus_Ignore }; -struct write_tree_context; +struct write_data_context; typedef struct { - struct write_tree_context* context; + struct write_data_context* context; UINT8* buf; + BOOL need_free; device* device; PIRP Irp; IO_STATUS_BLOCK iosb; - enum write_tree_status status; + enum write_data_status status; LIST_ENTRY list_entry; -} write_tree_stripe; +} write_data_stripe; typedef struct { KEVENT Event; LIST_ENTRY stripes; -} write_tree_context; + LONG stripes_left; + BOOL tree; +} write_data_context; // #pragma pack(pop) -static __inline void init_tree_holder(tree_holder* th) { -// th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG); -// KeInitializeSpinLock(&th->nonpaged->spin_lock); -// ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later -} - static __inline void* map_user_buffer(PIRP Irp) { if (!Irp->MdlAddress) { return Irp->UserBuffer; @@ -439,22 +590,28 @@ static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* in InsertTailList(list, &ins->list_entry); } +static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 num_stripes, UINT64* stripeoff, UINT16* stripe) { + UINT64 initoff, startoff; + + startoff = off % (num_stripes * stripe_length); + initoff = (off / (num_stripes * stripe_length)) * stripe_length; + + *stripe = startoff / stripe_length; + *stripeoff = initoff + startoff - (*stripe * stripe_length); +} + // in btrfs.c device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid); -ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment ); +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); -NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback); -BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback); 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); -UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode); -NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback); -NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, 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(); file_ref* create_fileref(); void protect_superblocks(device_extension* Vcb, chunk* c); @@ -465,8 +622,14 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb); BOOL is_file_name_valid(PUNICODE_STRING us); void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action); +void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action); WCHAR* file_desc(PFILE_OBJECT FileObject); WCHAR* file_desc_fileref(file_ref* fileref); +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); #ifdef _MSC_VER #define funcname __FUNCTION__ @@ -533,6 +696,13 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod); // in crc32c.c UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen); +enum rollback_type { + ROLLBACK_INSERT_ITEM, + ROLLBACK_DELETE_ITEM, + ROLLBACK_INSERT_EXTENT, + ROLLBACK_DELETE_EXTENT +}; + // 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); @@ -541,22 +711,22 @@ 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); 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, 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); void clear_rollback(LIST_ENTRY* rollback); void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback); void free_trees_root(device_extension* Vcb, root* r); -NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf); +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 free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__) -#define load_tree(t, addr, r, pt) _load_tree(t, addr, r, pt, 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__) // in search.c -void STDCALL look_for_vols(LIST_ENTRY* volumes); +void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes); // in cache.c NTSTATUS STDCALL init_cache(); @@ -565,28 +735,32 @@ extern CACHE_MANAGER_CALLBACKS* cache_callbacks; // in write.c NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback); -NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp); -NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, 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 extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback); -NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback); -NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback); -void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback); -NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, 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); +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); -void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type); -NTSTATUS consider_write(device_extension* Vcb); -BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data, - UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback); chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback); -NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length); -NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc); -void free_write_tree_stripes(write_tree_context* wtc); +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); +void free_write_data_stripes(write_data_context* wtc); NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback); +NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); +void flush_fcb(fcb* fcb, BOOL cache, 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); +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); // 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); // in security.c NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); @@ -595,16 +769,16 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent); // UINT32 STDCALL get_uid(); void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid); UINT32 sid_to_uid(PSID sid); -NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as); +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 delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback); -NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, 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); // in reparse.c NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen); @@ -613,20 +787,28 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp); // in create.c NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); -BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol, - UINT64* inode, UINT8* type, PANSI_STRING utf8); -NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback); +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); +void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock); +NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index); // in fsctl.c NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user); +void do_unlock_volume(device_extension* Vcb); // in flushthread.c void STDCALL flush_thread(void* context); // in read.c NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp); -NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr); +NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk** pc, PIRP Irp); +NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp); +NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read); // in pnp.c NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp); @@ -636,13 +818,36 @@ 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 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_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); + +#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_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) // in extent-tree.c -NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback); -NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback); +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); void decrease_chunk_usage(chunk* c, UINT64 delta); -NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback); 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); + +// in worker-thread.c +void STDCALL worker_thread(void* context); +void do_read_job(PIRP Irp); +void do_write_job(device_extension* Vcb, PIRP Irp); + +// in registry.c +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); #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable) @@ -669,6 +874,22 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* head->Blink = item; } +#ifdef DEBUG_FCB_REFCOUNTS +#ifdef DEBUG_LONG_MESSAGES +#define increase_fileref_refcount(fileref) {\ + LONG rc = InterlockedIncrement(&fileref->refcount);\ + MSG(funcname, __FILE__, __LINE__, "fileref %p: refcount now %i\n", 1, fileref, rc);\ +} +#else +#define increase_fileref_refcount(fileref) {\ + LONG rc = InterlockedIncrement(&fileref->refcount);\ + MSG(funcname, "fileref %p: refcount now %i\n", 1, fileref, rc);\ +} +#endif +#else +#define increase_fileref_refcount(fileref) InterlockedIncrement(&fileref->refcount) +#endif + #ifdef _MSC_VER // #define int3 __asm { int 3 } #define int3 __debugbreak() @@ -676,34 +897,11 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* #define int3 asm("int3;") #endif -#define acquire_tree_lock(Vcb, exclusive) {\ - LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \ - ref = ref; \ - if (exclusive) { \ - TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \ - ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \ - TRACE("open tree count = %i\n", Vcb->open_trees); \ - } else { \ - TRACE("getting tree_lock %u->%u\n", ref-1, ref); \ - ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \ - } \ -} - // if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; } // else TRACE("open tree count = %i\n", Vcb->open_trees); // FIXME - find a way to catch unfreed trees again -#define release_tree_lock(Vcb, exclusive) {\ - LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \ - ref = ref; \ - TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \ - if (exclusive) {\ - TRACE("open tree count = %i\n", Vcb->open_trees); \ - } \ - ExReleaseResourceLite(&Vcb->tree_lock); \ -} - // from sys/stat.h #define __S_IFMT 0170000 /* These bits determine file type. */ #define __S_IFDIR 0040000 /* Directory. */ @@ -773,6 +971,7 @@ NTSTATUS NTAPI FsRtlRemoveDotsFromPath(PWSTR OriginalString, USHORT PathLength, USHORT *NewLength); NTSTATUS NTAPI FsRtlValidateReparsePointBuffer(ULONG BufferLength, PREPARSE_DATA_BUFFER ReparseBuffer); -#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */ +ULONG NTAPI KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors); +#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA) */ #endif diff --git a/reactos/drivers/filesystems/btrfs/create.c b/reactos/drivers/filesystems/btrfs/create.c index e428fef9fb5..39b6cf2630d 100644 --- a/reactos/drivers/filesystems/btrfs/create.c +++ b/reactos/drivers/filesystems/btrfs/create.c @@ -19,28 +19,478 @@ #include #endif /* __REACTOS__ */ #include "btrfs_drv.h" +#ifndef __REACTOS__ +#include +#endif extern PDEVICE_OBJECT devobj; -BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, - UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) { - DIR_ITEM* di; +static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) { KEY searchkey; - traverse_ptr tp, tp2, next_tp; - BOOL b; + traverse_ptr tp; NTSTATUS Status; - ULONG stringlen; + UINT64 index; - TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type); - - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_ITEM; - searchkey.offset = crc32; + searchkey.obj_id = inode; + searchkey.obj_type = TYPE_INODE_REF; + searchkey.offset = parinode; Status = find_item(Vcb, r, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); - return FALSE; + return Status; + } + + if (!keycmp(&tp.item->key, &searchkey)) { + INODE_REF* ir; + ULONG len; + + index = 0; + + ir = (INODE_REF*)tp.item->data; + len = tp.item->size; + + do { + ULONG itemlen; + + if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + break; + } + + itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n; + + if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) { + index = ir->index; + break; + } + + if (len > itemlen) { + len -= itemlen; + ir = (INODE_REF*)&ir->name[ir->n]; + } else + break; + } while (len > 0); + + if (index == 0) + return STATUS_NOT_FOUND; + + *pindex = index; + + return STATUS_SUCCESS; + } else + 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) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + UINT64 index; + + searchkey.obj_id = inode; + 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); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + if (!keycmp(&tp.item->key, &searchkey)) { + INODE_EXTREF* ier; + ULONG len; + + index = 0; + + ier = (INODE_EXTREF*)tp.item->data; + len = tp.item->size; + + do { + ULONG itemlen; + + if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + break; + } + + itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n; + + if (ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) { + index = ier->index; + break; + } + + if (len > itemlen) { + len -= itemlen; + ier = (INODE_EXTREF*)&ier->name[ier->n]; + } else + break; + } while (len > 0); + + if (index == 0) + return STATUS_NOT_FOUND; + + *pindex = index; + + return STATUS_SUCCESS; + } else + return STATUS_NOT_FOUND; +} + +static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + ROOT_REF* rr; + + searchkey.obj_id = r->id; + searchkey.obj_type = TYPE_ROOT_REF; + searchkey.offset = subvolid; + + 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 (keycmp(&tp.item->key, &searchkey)) { + ERR("couldn't find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + return STATUS_INTERNAL_ERROR; + } + + if (tp.item->size < sizeof(ROOT_REF)) { + ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, + tp.item->size, sizeof(ROOT_REF)); + return STATUS_INTERNAL_ERROR; + } + + rr = (ROOT_REF*)tp.item->data; + + if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) { + ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, + tp.item->size, sizeof(ROOT_REF) - 1 + rr->n); + return STATUS_INTERNAL_ERROR; + } + + if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(utf8->Buffer, rr->name, rr->n) == rr->n) { + *pindex = rr->index; + return STATUS_SUCCESS; + } else + return STATUS_NOT_FOUND; +} + +static NTSTATUS load_index_list(fcb* fcb) { + KEY searchkey; + traverse_ptr tp, next_tp; + NTSTATUS Status; + BOOL b; + + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_DIR_INDEX; + searchkey.offset = 2; + + 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) == -1) { + if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) { + tp = next_tp; + + TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + } + } + + if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_DIR_INDEX) { + Status = STATUS_SUCCESS; + goto end; + } + + do { + DIR_ITEM* di; + + TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + di = (DIR_ITEM*)tp.item->data; + + if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) { + WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + } else { + index_entry* ie; + ULONG stringlen; + UNICODE_STRING us; + LIST_ENTRY* le; + BOOL inserted; + + ie = ExAllocatePoolWithTag(PagedPool, sizeof(index_entry), ALLOC_TAG); + if (!ie) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + ie->utf8.Length = ie->utf8.MaximumLength = di->n; + + if (di->n > 0) { + ie->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, ie->utf8.MaximumLength, ALLOC_TAG); + if (!ie->utf8.Buffer) { + ERR("out of memory\n"); + ExFreePool(ie); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(ie->utf8.Buffer, di->name, di->n); + } else + ie->utf8.Buffer = NULL; + + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + ExFreePool(ie); + goto nextitem; + } + + if (stringlen == 0) { + ERR("UTF8 length was 0\n"); + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + ExFreePool(ie); + goto nextitem; + } + + us.Length = us.MaximumLength = stringlen; + us.Buffer = ExAllocatePoolWithTag(PagedPool, us.MaximumLength, ALLOC_TAG); + + if (!us.Buffer) { + ERR("out of memory\n"); + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + ExFreePool(ie); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = RtlUTF8ToUnicodeN(us.Buffer, stringlen, &stringlen, di->name, di->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + ExFreePool(us.Buffer); + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + ExFreePool(ie); + goto nextitem; + } + + Status = RtlUpcaseUnicodeString(&ie->filepart_uc, &us, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + ExFreePool(us.Buffer); + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + ExFreePool(ie); + goto nextitem; + } + + ie->key = di->key; + ie->type = di->type; + ie->index = tp.item->key.offset; + + ie->hash = calc_crc32c(0xfffffffe, (UINT8*)ie->filepart_uc.Buffer, (ULONG)ie->filepart_uc.Length); + inserted = FALSE; + + le = fcb->index_list.Flink; + while (le != &fcb->index_list) { + index_entry* ie2 = CONTAINING_RECORD(le, index_entry, list_entry); + + if (ie2->hash >= ie->hash) { + InsertHeadList(le->Blink, &ie->list_entry); + inserted = TRUE; + break; + } + + le = le->Flink; + } + + if (!inserted) + InsertTailList(&fcb->index_list, &ie->list_entry); + } + +nextitem: + b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); + + if (b) { + tp = next_tp; + + b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX; + } + } while (b); + + Status = STATUS_SUCCESS; + +end: + if (!NT_SUCCESS(Status)) { + while (!IsListEmpty(&fcb->index_list)) { + LIST_ENTRY* le = RemoveHeadList(&fcb->index_list); + index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry); + + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer); + ExFreePool(ie); + } + } else + mark_fcb_dirty(fcb); + + return Status; +} + +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) { + LIST_ENTRY* le; + NTSTATUS Status; + UNICODE_STRING us; + UINT32 hash; + + Status = RtlUpcaseUnicodeString(&us, filename, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + return Status; + } + + hash = calc_crc32c(0xfffffffe, (UINT8*)us.Buffer, (ULONG)us.Length); + + ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE); + + if (!fr->fcb->index_loaded) { + Status = load_index_list(fr->fcb); + if (!NT_SUCCESS(Status)) { + ERR("load_index_list returned %08x\n", Status); + goto end; + } + + fr->fcb->index_loaded = TRUE; + } + + ExConvertExclusiveToSharedLite(&fr->fcb->nonpaged->index_lock); + + le = fr->fcb->index_list.Flink; + while (le != &fr->fcb->index_list) { + index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry); + + if (ie->hash == hash && ie->filepart_uc.Length == us.Length && RtlCompareMemory(ie->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) { + LIST_ENTRY* le; + BOOL ignore_entry = FALSE; + + ExAcquireResourceSharedLite(&fr->nonpaged->children_lock, TRUE); + + le = fr->children.Flink; + while (le != &fr->children) { + file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry); + + if (fr2->index == ie->index) { + if (fr2->deleted || fr2->filepart_uc.Length != us.Length || + RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) != us.Length) { + ignore_entry = TRUE; + break; + } + break; + } else if (fr2->index > ie->index) + break; + + le = le->Flink; + } + + ExReleaseResourceLite(&fr->nonpaged->children_lock); + + if (ignore_entry) + goto nextitem; + + if (ie->key.obj_type == TYPE_ROOT_ITEM) { + if (subvol) { + *subvol = NULL; + + le = fr->fcb->Vcb->roots.Flink; + while (le != &fr->fcb->Vcb->roots) { + root* r2 = CONTAINING_RECORD(le, root, list_entry); + + if (r2->id == ie->key.obj_id) { + *subvol = r2; + break; + } + + le = le->Flink; + } + } + + if (inode) + *inode = SUBVOL_ROOT_INODE; + + if (type) + *type = BTRFS_TYPE_DIRECTORY; + } else { + if (subvol) + *subvol = fr->fcb->subvol; + + if (inode) + *inode = ie->key.obj_id; + + if (type) + *type = ie->type; + } + + if (utf8) { + utf8->MaximumLength = utf8->Length = ie->utf8.Length; + utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG); + if (!utf8->Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(utf8->Buffer, ie->utf8.Buffer, ie->utf8.Length); + } + + if (pindex) + *pindex = ie->index; + + Status = STATUS_SUCCESS; + goto end; + } else if (ie->hash > hash) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto end; + } + +nextitem: + le = le->Flink; + } + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + +end: + ExReleaseResourceLite(&fr->fcb->nonpaged->index_lock); + + ExFreePool(us.Buffer); + + 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) { + DIR_ITEM* di; + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + ULONG stringlen; + + TRACE("(%p, %.*S, %08x, (%llx, %llx), %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, + fr->fcb->subvol->id, fr->fcb->inode, subvol, inode, type); + + searchkey.obj_id = fr->fcb->inode; + searchkey.obj_type = TYPE_DIR_ITEM; + searchkey.offset = crc32; + + Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; } TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); @@ -51,7 +501,8 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING // found by hash if (tp.item->size < sizeof(DIR_ITEM)) { - WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", r->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM)); + WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", fr->fcb->subvol->id, tp.item->key.obj_id, tp.item->key.obj_type, + tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM)); } else { di = (DIR_ITEM*)tp.item->data; @@ -74,7 +525,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING if (!utf16) { ERR("out of memory\n"); - return FALSE; + return STATUS_INSUFFICIENT_RESOURCES; } Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n); @@ -86,6 +537,8 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING us.Length = us.MaximumLength = (USHORT)stringlen; if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) { + UINT64 index; + if (di->key.obj_type == TYPE_ROOT_ITEM) { LIST_ENTRY* le = Vcb->roots.Flink; @@ -111,7 +564,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING *type = BTRFS_TYPE_DIRECTORY; } else { if (subvol) - *subvol = r; + *subvol = fr->fcb->subvol; if (inode) *inode = di->key.obj_id; @@ -127,7 +580,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING if (!utf8->Buffer) { ERR("out of memory\n"); ExFreePool(utf16); - return FALSE; + return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(utf8->Buffer, di->name, di->n); @@ -135,9 +588,57 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING ExFreePool(utf16); -// TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode); + index = 0; + + if (fr->fcb->subvol != Vcb->root_root) { + 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); + 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); + 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); + + if (!NT_SUCCESS(Status)) { + ERR("find_file_dir_index_extref returned %08x\n", Status); + return Status; + } + } else { + ERR("find_file_dir_index returned %08x\n", Status); + return Status; + } + } + } + } - return TRUE; + if (index != 0) { + LIST_ENTRY* le = fr->children.Flink; + + while (le != &fr->children) { + file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry); + + if (fr2->index == index) { + if (fr2->deleted || !FsRtlAreNamesEqual(&fr2->filepart, filename, TRUE, NULL)) { + goto byindex; + } + break; + } else if (fr2->index > index) + break; + + le = le->Flink; + } + } + +// TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode); + + if (pindex) + *pindex = index; + + return STATUS_SUCCESS; } } @@ -149,131 +650,14 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING } } - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = 2; - - Status = find_item(Vcb, r, &tp2, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return FALSE; +byindex: + Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8); + if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ERR("find_file_in_dir_index returned %08x\n", Status); + return Status; } - tp = tp2; - - TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - if (keycmp(&tp.item->key, &searchkey) == -1) { - if (find_next_item(Vcb, &tp, &next_tp, FALSE)) { - tp = next_tp; - - TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } - } - - if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) - return FALSE; - - b = TRUE; - do { - TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - di = (DIR_ITEM*)tp.item->data; - - if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) { - WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } else { - TRACE("%.*s\n", di->n, di->name); - - Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n); - if (!NT_SUCCESS(Status)) { - ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); - } else { - WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG); - UNICODE_STRING us; - - if (!utf16) { - ERR("out of memory\n"); - return FALSE; - } - - Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n); - - if (!NT_SUCCESS(Status)) { - ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); - } else { - us.Buffer = utf16; - us.Length = us.MaximumLength = (USHORT)stringlen; - - if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) { - if (di->key.obj_type == TYPE_ROOT_ITEM) { - LIST_ENTRY* le = Vcb->roots.Flink; - - if (subvol) { - *subvol = NULL; - - while (le != &Vcb->roots) { - root* r2 = CONTAINING_RECORD(le, root, list_entry); - - if (r2->id == di->key.obj_id) { - *subvol = r2; - break; - } - - le = le->Flink; - } - } - - if (inode) - *inode = SUBVOL_ROOT_INODE; - - if (type) - *type = BTRFS_TYPE_DIRECTORY; - } else { - if (subvol) - *subvol = r; - - if (inode) - *inode = di->key.obj_id; - - if (type) - *type = di->type; - } -// TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode); - - if (utf8) { - utf8->MaximumLength = di->n; - utf8->Length = utf8->MaximumLength; - utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG); - if (!utf8->Buffer) { - ERR("out of memory\n"); - ExFreePool(utf16); - - return FALSE; - } - - RtlCopyMemory(utf8->Buffer, di->name, di->n); - } - - ExFreePool(utf16); - - return TRUE; - } - } - - ExFreePool(utf16); - } - } - - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - - if (b) { - tp = next_tp; - - b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX; - } - } while (b); - - return FALSE; + return Status; } fcb* create_fcb() { @@ -315,8 +699,14 @@ fcb* create_fcb() { ExInitializeResourceLite(&fcb->nonpaged->resource); fcb->Header.Resource = &fcb->nonpaged->resource; + ExInitializeResourceLite(&fcb->nonpaged->index_lock); + FsRtlInitializeFileLock(&fcb->lock, NULL, NULL); + InitializeListHead(&fcb->extents); + InitializeListHead(&fcb->index_list); + InitializeListHead(&fcb->hardlinks); + return fcb; } @@ -331,42 +721,50 @@ file_ref* create_fileref() { RtlZeroMemory(fr, sizeof(file_ref)); + fr->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(file_ref_nonpaged), ALLOC_TAG); + if (!fr->nonpaged) { + ERR("out of memory\n"); + ExFreePool(fr); + return NULL; + } + fr->refcount = 1; #ifdef DEBUG_FCB_REFCOUNTS - WARN("fileref %p: refcount now %i\n", fr, fr->refcount); + WARN("fileref %p: refcount now 1\n", fr); #endif InitializeListHead(&fr->children); + ExInitializeResourceLite(&fr->nonpaged->children_lock); + return fr; } -static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r, - UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) { +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) { char* fn; UINT32 crc32; - BOOL ret; ULONG utf8len; NTSTATUS Status; Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length); if (!NT_SUCCESS(Status)) { ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status); - return FALSE; + return Status; } fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG); if (!fn) { ERR("out of memory\n"); - return FALSE; + return STATUS_INSUFFICIENT_RESOURCES; } Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length); if (!NT_SUCCESS(Status)) { ExFreePool(fn); ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status); - return FALSE; + return Status; } TRACE("%.*s\n", utf8len, fn); @@ -374,12 +772,10 @@ static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING file crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len); TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32); - ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8); - - return ret; + return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8); } -static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, 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) { NTSTATUS Status; ULONG utf8len; char* utf8; @@ -451,7 +847,6 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) { TRACE("found exact match for %s\n", utf8); - *size = di->m; *hash = tp.item->key.offset; xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG); @@ -532,7 +927,6 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, TRACE("found case-insensitive match for %s\n", utf8); *newstreamname = us; - *size = di->m; *hash = tp.item->key.offset; xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG); @@ -695,15 +1089,24 @@ static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) { LIST_ENTRY* le; file_ref *c, *deleted = NULL; + NTSTATUS Status; + UNICODE_STRING ucus; #ifdef DEBUG_FCB_REFCOUNTS ULONG rc; #endif + + Status = RtlUpcaseUnicodeString(&ucus, name, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + return NULL; + } le = dir->children.Flink; while (le != &dir->children) { c = CONTAINING_RECORD(le, file_ref, list_entry); - if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) { + if (c->refcount > 0 && c->filepart_uc.Length == ucus.Length && + RtlCompareMemory(c->filepart_uc.Buffer, ucus.Buffer, ucus.Length) == ucus.Length) { if (c->deleted) { deleted = c; } else { @@ -713,6 +1116,8 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) { #else InterlockedIncrement(&c->refcount); #endif + ExFreePool(ucus.Buffer); + return c; } } @@ -720,23 +1125,56 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) { le = le->Flink; } - if (deleted) { -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&deleted->refcount); - WARN("fileref %p: refcount now %i (%S)\n", deleted, rc, file_desc_fileref(deleted)); -#else - InterlockedIncrement(&deleted->refcount); -#endif - } + if (deleted) + increase_fileref_refcount(deleted); + + ExFreePool(ucus.Buffer); return deleted; } -static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) { +static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + EXTENT_ITEM* ei; + + searchkey.obj_id = address; + searchkey.obj_type = TYPE_EXTENT_ITEM; + searchkey.offset = size; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return 0; + } + + if (keycmp(&searchkey, &tp.item->key)) { + ERR("couldn't find (%llx,%x,%llx) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + return 0; + } + + if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { + EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data; + + return eiv0->refcount; + } else if (tp.item->size < sizeof(EXTENT_ITEM)) { + ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, + tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); + return 0; + } + + ei = (EXTENT_ITEM*)tp.item->data; + + return ei->refcount; +} + +NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) { KEY searchkey; traverse_ptr tp; NTSTATUS Status; fcb* fcb; + BOOL b; if (!IsListEmpty(&subvol->fcbs)) { LIST_ENTRY* le = subvol->fcbs.Flink; @@ -785,19 +1223,47 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT } if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id); + WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id); free_fcb(fcb); - return STATUS_INTERNAL_ERROR; + return STATUS_INVALID_PARAMETER; } if (tp.item->size > 0) RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size)); - fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8->Buffer[0] == '.', FALSE); + if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already + if (fcb->inode_item.st_mode & __S_IFDIR) + fcb->type = BTRFS_TYPE_DIRECTORY; + else if (fcb->inode_item.st_mode & __S_IFCHR) + fcb->type = BTRFS_TYPE_CHARDEV; + else if (fcb->inode_item.st_mode & __S_IFBLK) + fcb->type = BTRFS_TYPE_BLOCKDEV; + else if (fcb->inode_item.st_mode & __S_IFIFO) + fcb->type = BTRFS_TYPE_FIFO; + else if (fcb->inode_item.st_mode & __S_IFLNK) + fcb->type = BTRFS_TYPE_SYMLINK; + else if (fcb->inode_item.st_mode & __S_IFSOCK) + fcb->type = BTRFS_TYPE_SOCKET; + else + 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_get_sd(fcb, parent); + if (fcb->type == BTRFS_TYPE_DIRECTORY) { + UINT8* xattrdata; + UINT16 xattrlen; + + if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) { + fcb->reparse_xattr.Buffer = (char*)xattrdata; + fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen; + } + } + InsertTailList(&subvol->fcbs, &fcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); fcb->Header.IsFastIoPossible = fast_io_possible(fcb); @@ -806,11 +1272,12 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT fcb->Header.FileSize.QuadPart = 0; fcb->Header.ValidDataLength.QuadPart = 0; } else { - EXTENT_DATA* ed; + EXTENT_DATA* ed = NULL; + traverse_ptr next_tp; searchkey.obj_id = fcb->inode; searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = 0xffffffffffffffff; + searchkey.offset = 0; Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { @@ -819,23 +1286,71 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT return Status; } - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); - free_fcb(fcb); - return STATUS_INTERNAL_ERROR; - } - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, - tp.item->size, sizeof(EXTENT_DATA)); + do { + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + extent* ext; + BOOL unique = FALSE; + + ed = (EXTENT_DATA*)tp.item->data; + + if (tp.item->size < sizeof(EXTENT_DATA)) { + ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, + tp.item->size, sizeof(EXTENT_DATA)); + + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } + + if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0]; + + if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { + ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, + tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); + + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } + + if (ed2->size != 0) + unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1; + } + + ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!ext) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ext->data = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); + if (!ext->data) { + ERR("out of memory\n"); + ExFreePool(ext); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ext->offset = tp.item->key.offset; + RtlCopyMemory(ext->data, tp.item->data, tp.item->size); + ext->datalen = tp.item->size; + ext->unique = unique; + ext->ignore = FALSE; + + InsertTailList(&fcb->extents, &ext->list_entry); + } - free_fcb(fcb); - return STATUS_INTERNAL_ERROR; - } + b = find_next_item(Vcb, &tp, &next_tp, FALSE); + + if (b) { + tp = next_tp; + + 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)) + break; + } + } while (b); - ed = (EXTENT_DATA*)tp.item->data; - - if (ed->type == EXTENT_TYPE_INLINE) + if (ed && ed->type == EXTENT_TYPE_INLINE) fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size; else fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size); @@ -844,13 +1359,175 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size; } + // FIXME - only do if st_nlink > 1? + + searchkey.obj_id = inode; + searchkey.obj_type = TYPE_INODE_REF; + searchkey.offset = 0; + + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + free_fcb(fcb); + return Status; + } + + do { + traverse_ptr next_tp; + + if (tp.item->key.obj_id == searchkey.obj_id) { + if (tp.item->key.obj_type == TYPE_INODE_REF) { + ULONG len; + INODE_REF* ir; + + len = tp.item->size; + ir = (INODE_REF*)tp.item->data; + + while (len >= sizeof(INODE_REF) - 1) { + hardlink* hl; + ULONG stringlen; + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + hl->parent = tp.item->key.offset; + hl->index = ir->index; + + hl->utf8.Length = hl->utf8.MaximumLength = ir->n; + + if (hl->utf8.Length > 0) { + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); + RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n); + } + + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + ExFreePool(hl); + free_fcb(fcb); + return Status; + } + + hl->name.Length = hl->name.MaximumLength = stringlen; + + if (stringlen == 0) + hl->name.Buffer = NULL; + else { + hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); + + if (!hl->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + ExFreePool(hl->name.Buffer); + ExFreePool(hl); + free_fcb(fcb); + return Status; + } + } + + InsertTailList(&fcb->hardlinks, &hl->list_entry); + + len -= sizeof(INODE_REF) - 1 + ir->n; + ir = (INODE_REF*)&ir->name[ir->n]; + } + } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { + ULONG len; + INODE_EXTREF* ier; + + len = tp.item->size; + ier = (INODE_EXTREF*)tp.item->data; + + while (len >= sizeof(INODE_EXTREF) - 1) { + hardlink* hl; + ULONG stringlen; + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + hl->parent = ier->dir; + hl->index = ier->index; + + hl->utf8.Length = hl->utf8.MaximumLength = ier->n; + + if (hl->utf8.Length > 0) { + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); + RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n); + } + + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + ExFreePool(hl); + free_fcb(fcb); + return Status; + } + + hl->name.Length = hl->name.MaximumLength = stringlen; + + if (stringlen == 0) + hl->name.Buffer = NULL; + else { + hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); + + if (!hl->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + ExFreePool(hl->name.Buffer); + ExFreePool(hl); + free_fcb(fcb); + return Status; + } + } + + InsertTailList(&fcb->hardlinks, &hl->list_entry); + + len -= sizeof(INODE_EXTREF) - 1 + ier->n; + ier = (INODE_EXTREF*)&ier->name[ier->n]; + } + } + } + + b = find_next_item(Vcb, &tp, &next_tp, FALSE); + + if (b) { + tp = next_tp; + + if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > TYPE_INODE_EXTREF)) + break; + } + } while (b); + *pfcb = fcb; return STATUS_SUCCESS; } -static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, - UINT32 streamsize, UINT32 streamhash, fcb* parent, fcb** pfcb) { +NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, + UINT32 streamhash, fcb* parent, fcb** pfcb) { fcb* fcb; + UINT8* xattrdata; + UINT16 xattrlen; if (!IsListEmpty(&subvol->fcbs)) { LIST_ENTRY* le = subvol->fcbs.Flink; @@ -881,6 +1558,12 @@ static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inod ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } + + if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) { + ERR("get_xattr failed\n"); + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } fcb->Vcb = Vcb; @@ -888,24 +1571,58 @@ static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inod fcb->inode = parent->inode; fcb->type = parent->type; fcb->ads = TRUE; - fcb->adssize = streamsize; fcb->adshash = streamhash; fcb->adsxattr = *xattr; - fcb->Header.IsFastIoPossible = fast_io_possible(fcb); - fcb->Header.AllocationSize.QuadPart = fcb->adssize; - fcb->Header.FileSize.QuadPart = fcb->adssize; - fcb->Header.ValidDataLength.QuadPart = fcb->adssize; + fcb->adsdata.Buffer = (char*)xattrdata; + fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen; - TRACE("stream found: size = %x, hash = %08x\n", fcb->adssize, fcb->adshash); + fcb->Header.IsFastIoPossible = fast_io_possible(fcb); + fcb->Header.AllocationSize.QuadPart = xattrlen; + fcb->Header.FileSize.QuadPart = xattrlen; + fcb->Header.ValidDataLength.QuadPart = xattrlen; + + TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash); InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); *pfcb = fcb; return STATUS_SUCCESS; } +void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) { + if (do_lock) + ExAcquireResourceExclusiveLite(&parent->nonpaged->children_lock, TRUE); + + if (IsListEmpty(&parent->children)) + InsertTailList(&parent->children, &child->list_entry); + else { + LIST_ENTRY* le = parent->children.Flink; + file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry); + + if (child->index < fr1->index) + InsertHeadList(&parent->children, &child->list_entry); + else { + while (le != &parent->children) { + file_ref* fr2 = (le->Flink == &parent->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry); + + if (child->index >= fr1->index && (!fr2 || fr2->index > child->index)) { + InsertHeadList(&fr1->list_entry, &child->list_entry); + break; + } + + fr1 = fr2; + le = le->Flink; + } + } + } + + if (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) { UNICODE_STRING fnus2; file_ref *dir, *sf, *sf2; @@ -916,7 +1633,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed); - if (Vcb->removing) + if (Vcb->removing || Vcb->locked) return STATUS_ACCESS_DENIED; fnus2 = *fnus; @@ -927,13 +1644,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu } if (related && fnus->Length == 0) { -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc = InterlockedIncrement(&related->refcount); - WARN("fileref %p: refcount now %i\n", related, rc); -#else - InterlockedIncrement(&related->refcount); -#endif - + increase_fileref_refcount(related); *pfr = related; return STATUS_SUCCESS; @@ -948,12 +1659,17 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu } if (fnus2.Length == sizeof(WCHAR)) { -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc = InterlockedIncrement(&Vcb->root_fileref->refcount); - WARN("fileref %p: refcount now %i (root)\n", Vcb->root_fileref, rc); -#else - InterlockedIncrement(&Vcb->root_fileref->refcount); -#endif + if (Vcb->root_fileref->fcb->open_count == 0) { // don't allow root to be opened on unmounted FS + ULONG cc; + IO_STATUS_BLOCK iosb; + + Status = dev_ioctl(Vcb->devices[0].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) + return Status; + } + + increase_fileref_refcount(Vcb->root_fileref); *pfr = Vcb->root_fileref; return STATUS_SUCCESS; } @@ -982,10 +1698,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu } sf = dir; - dir->refcount++; -#ifdef DEBUG_FCB_REFCOUNTS - WARN("fileref %p: refcount now %i (%S)\n", dir, dir->refcount, file_desc_fileref(dir)); -#endif + increase_fileref_refcount(dir); if (parent) { num_parts--; @@ -1002,6 +1715,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu goto end2; } + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + for (i = 0; i < num_parts; i++) { BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream); @@ -1019,7 +1734,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu if (has_stream && i == num_parts - 1) { UNICODE_STRING streamname; ANSI_STRING xattr; - UINT32 streamsize, streamhash; + UINT32 streamhash; streamname.Buffer = NULL; streamname.Length = streamname.MaximumLength = 0; @@ -1028,18 +1743,14 @@ 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, &streamsize, &streamhash, &xattr)) { + if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) { TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer); Status = STATUS_OBJECT_NAME_NOT_FOUND; goto end; } else { - ULONG fnlen; fcb* fcb; -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc; -#endif - + if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) && RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) { WARN("not allowing user.DOSATTRIB to be opened as stream\n"); @@ -1048,7 +1759,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, streamsize, streamhash, sf->fcb, &fcb); + Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb); if (!NT_SUCCESS(Status)) { ERR("open_fcb_stream returned %08x\n", Status); goto end; @@ -1079,57 +1790,38 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length); } - sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR); - - if (sf != Vcb->root_fileref) - sf2->name_offset++; - - fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length; - - sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG); - if (!sf2->full_filename.Buffer) { - ERR("out of memory\n"); + Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); free_fileref(sf2); - Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen; - RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length); - - sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':'; - - RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length); - // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM sf2->parent = (struct _file_ref*)sf; - InsertTailList(&sf->children, &sf2->list_entry); + insert_fileref_child(sf, sf2, TRUE); -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&sf->refcount); - WARN("fileref %p: refcount now %i\n", sf, rc); -#else - InterlockedIncrement(&sf->refcount); -#endif + increase_fileref_refcount(sf); } } else { root* subvol; - UINT64 inode; + UINT64 inode, index; UINT8 type; ANSI_STRING utf8; -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc; -#endif - if (!find_file_in_dir(Vcb, &parts[i], sf->fcb->subvol, sf->fcb->inode, &subvol, &inode, &type, &utf8)) { + Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8); + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer); Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND; goto end; + } else if (!NT_SUCCESS(Status)) { + ERR("find_file_in_dir returned %08x\n", Status); + goto end; } else { fcb* fcb; - ULONG strlen, fnlen; + ULONG strlen; if (type != BTRFS_TYPE_DIRECTORY && !lastpart) { WARN("passed path including file as subdirectory\n"); @@ -1154,6 +1846,10 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu sf2->fcb = fcb; + if (type == BTRFS_TYPE_DIRECTORY) + fcb->fileref = sf2; + + sf2->index = index; sf2->utf8 = utf8; Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length); @@ -1179,37 +1875,18 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu goto end; } - sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR); - - if (sf != Vcb->root_fileref) - sf2->name_offset++; - - fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length; - - sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG); - if (!sf2->full_filename.Buffer) { - ERR("out of memory\n"); + Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); free_fileref(sf2); - Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen; - RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length); - - if (sf != Vcb->root_fileref) - sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\'; - - RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length); - sf2->parent = (struct _file_ref*)sf; - InsertTailList(&sf->children, &sf2->list_entry); -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&sf->refcount); - WARN("fileref %p: refcount now %i\n", sf, rc); -#else - InterlockedIncrement(&sf->refcount); -#endif + + insert_fileref_child(sf, sf2, TRUE); + + increase_fileref_refcount(sf); } } } @@ -1235,6 +1912,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu *pfr = sf2; end: + ExReleaseResourceLite(&Vcb->fcb_lock); free_fileref(sf); end2: @@ -1246,25 +1924,64 @@ end2: return Status; } +NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) { + KEY searchkey; + traverse_ptr tp, prev_tp; + NTSTATUS Status; + + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + if (fcb->last_dir_index != 0) { + *index = fcb->last_dir_index; + fcb->last_dir_index++; + Status = STATUS_SUCCESS; + goto end; + } + + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_DIR_INDEX + 1; + searchkey.offset = 0; + + 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 (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)) + tp = prev_tp; + } + + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) { + fcb->last_dir_index = tp.item->key.offset + 1; + } else + fcb->last_dir_index = 2; + + *index = fcb->last_dir_index; + fcb->last_dir_index++; + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(fcb->Header.Resource); + + return Status; +} + static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options, file_ref** pfr, LIST_ENTRY* rollback) { NTSTATUS Status; fcb* fcb; ULONG utf8len; char* utf8 = NULL; - UINT32 crc32; UINT64 dirpos, inode; - KEY searchkey; - traverse_ptr tp; - INODE_ITEM *dirii, *ii; UINT8 type; - ULONG disize; - DIR_ITEM *di, *di2; LARGE_INTEGER time; BTRFS_TIME now; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - ANSI_STRING utf8as; ULONG defda; file_ref* fileref; + hardlink* hl; #ifdef DEBUG_FCB_REFCOUNTS LONG rc; #endif @@ -1287,56 +2004,28 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S utf8[utf8len] = 0; - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len); - - dirpos = find_next_dir_index(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode); - if (dirpos == 0) { - Status = STATUS_INTERNAL_ERROR; + Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos); + if (!NT_SUCCESS(Status)) { + ERR("fcb_get_last_dir_index returned %08x\n", Status); ExFreePool(utf8); return Status; } - TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos); - KeQuerySystemTime(&time); win_time_to_unix(time, &now); -// TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size); + TRACE("create file %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer); + ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE); + TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size); parfileref->fcb->inode_item.st_size += utf8len * 2; -// TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size); + TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size); parfileref->fcb->inode_item.transid = Vcb->superblock.generation; parfileref->fcb->inode_item.sequence++; parfileref->fcb->inode_item.st_ctime = now; parfileref->fcb->inode_item.st_mtime = now; + ExReleaseResourceLite(parfileref->fcb->Header.Resource); - searchkey.obj_id = parfileref->fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - ExFreePool(utf8); - return Status; - } - - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfileref->fcb->inode, parfileref->fcb->subvol->id); - ExFreePool(utf8); - return STATUS_INTERNAL_ERROR; - } - - dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!dirii) { - ERR("out of memory\n"); - ExFreePool(utf8); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(dirii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM)); - delete_tree_item(Vcb, &tp, rollback); - - insert_tree_item(Vcb, parfileref->fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, dirii, sizeof(INODE_ITEM), NULL, rollback); + mark_fcb_dirty(parfileref->fcb); if (parfileref->fcb->subvol->lastinode == 0) get_last_inode(Vcb, parfileref->fcb->subvol); @@ -1345,51 +2034,6 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE; - disize = sizeof(DIR_ITEM) - 1 + utf8len; - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - ExFreePool(utf8); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = (UINT16)utf8len; - di->type = type; - RtlCopyMemory(di->name, utf8, utf8len); - - insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback); - - di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di2) { - ERR("out of memory\n"); - ExFreePool(utf8); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(di2, di, disize); - - Status = add_dir_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, crc32, di2, disize, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); - ExFreePool(utf8); - return Status; - } - - utf8as.Buffer = utf8; - utf8as.Length = utf8as.MaximumLength = utf8len; - - Status = add_inode_ref(Vcb, parfileref->fcb->subvol, inode, parfileref->fcb->inode, dirpos, &utf8as, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_inode_ref returned %08x\n", Status); - ExFreePool(utf8); - return Status; - } - // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes); @@ -1411,19 +2055,6 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL) IrpSp->Parameters.Create.FileAttributes = defda; - if (IrpSp->Parameters.Create.FileAttributes != defda) { - char val[64]; - - sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes); - - Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - ExFreePool(utf8); - return Status; - } - } - parfileref->fcb->subvol->lastinode++; fcb = create_fcb(); @@ -1473,6 +2104,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S fcb->Header.ValidDataLength.QuadPart = 0; fcb->atts = IrpSp->Parameters.Create.FileAttributes; + fcb->atts_changed = fcb->atts != defda; #ifdef DEBUG_FCB_REFCOUNTS rc = InterlockedIncrement(&parfileref->fcb->refcount); @@ -1492,6 +2124,44 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S return Status; } + fcb->sd_dirty = TRUE; + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + hl->parent = parfileref->fcb->inode; + hl->index = dirpos; + + hl->utf8.Length = hl->utf8.MaximumLength = utf8len; + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG); + + if (!hl->utf8.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl); + free_fcb(fcb); + 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); + + if (!hl->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl->utf8.Buffer); + ExFreePool(hl); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(hl->name.Buffer, fpus->Buffer, fpus->Length); + + InsertTailList(&fcb->hardlinks, &hl->list_entry); + fileref = create_fileref(); if (!fileref) { ERR("out of memory\n"); @@ -1500,45 +2170,22 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S } fileref->fcb = fcb; + fileref->index = dirpos; fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len; fileref->utf8.Buffer = utf8; fileref->filepart = *fpus; - - Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback); + + Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); free_fileref(fileref); return Status; } - - fileref->full_filename.Length = parfileref->full_filename.Length + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fileref->filepart.Length; - fileref->full_filename.MaximumLength = fileref->full_filename.Length; - fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length, ALLOC_TAG); - if (!fileref->full_filename.Buffer) { - ERR("out of memory\n"); - free_fileref(fileref); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length); - - if (parfileref->full_filename.Length > sizeof(WCHAR)) - fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = '\\'; - - RtlCopyMemory(&fileref->full_filename.Buffer[(parfileref->full_filename.Length / sizeof(WCHAR)) + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], - fileref->filepart.Buffer, fileref->filepart.Length); - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - free_fileref(fileref); - return STATUS_INSUFFICIENT_RESOURCES; - } - + if (Irp->Overlay.AllocationSize.QuadPart > 0) { - Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback); + Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback); if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08x\n", Status); @@ -1547,25 +2194,50 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S } } - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - insert_tree_item(Vcb, fcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback); + fcb->created = TRUE; + mark_fcb_dirty(fcb); + + fileref->created = TRUE; + mark_fileref_dirty(fileref); fcb->subvol->root_item.ctransid = Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; fileref->parent = parfileref; - InsertTailList(&parfileref->children, &fileref->list_entry); -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&parfileref->refcount); - WARN("fileref %p: refcount now %i\n", parfileref, rc); -#else - InterlockedIncrement(&parfileref->refcount); -#endif + + 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); + + increase_fileref_refcount(parfileref); InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); *pfr = fileref; + if (type == BTRFS_TYPE_DIRECTORY) + fileref->fcb->fileref = fileref; + TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode); return STATUS_SUCCESS; @@ -1581,7 +2253,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC static WCHAR datasuf[] = {':','$','D','A','T','A',0}; UNICODE_STRING dsus, fpus, stream; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - ULONG access; PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState; #ifdef DEBUG_FCB_REFCOUNTS LONG oc; @@ -1603,9 +2274,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC } else related = NULL; - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL); - ExReleaseResourceLite(&Vcb->fcb_lock); if (!NT_SUCCESS(Status)) goto end; @@ -1675,14 +2344,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC file_ref* newpar; fcb* fcb; static char xapref[] = "user."; - ULONG xapreflen = strlen(xapref), fnlen; + ULONG xapreflen = strlen(xapref); LARGE_INTEGER time; BTRFS_TIME now; - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* ii; ULONG utf8len; - UINT64 offset; #ifdef DEBUG_FCB_REFCOUNTS LONG rc; #endif @@ -1690,19 +2355,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer); TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer); - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL); - ExReleaseResourceLite(&Vcb->fcb_lock); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { UNICODE_STRING fpus2; - if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, - IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) { - WARN("SeAccessCheck failed, returning %08x\n", Status); - goto end; - } - if (!is_file_name_valid(&fpus)) return STATUS_OBJECT_NAME_INVALID; @@ -1725,7 +2382,8 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC goto end; } - // FIXME - send notification + 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; @@ -1734,12 +2392,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC free_fileref(parfileref); parfileref = newpar; - if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL, - IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) { - WARN("SeAccessCheck failed, returning %08x\n", Status); - goto end; - } - if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) { WARN("parent not file or symlink\n"); Status = STATUS_INVALID_PARAMETER; @@ -1777,7 +2429,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC fcb->type = parfileref->fcb->type; fcb->ads = TRUE; - fcb->adssize = 0; Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length); if (!NT_SUCCESS(Status)) { @@ -1821,10 +2472,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC } fileref->fcb = fcb; - - fileref->name_offset = parfileref->full_filename.Length / sizeof(WCHAR); - if (parfileref != Vcb->root_fileref) - fileref->name_offset++; fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length; fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG); @@ -1837,32 +2484,17 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length); - fnlen = (fileref->name_offset * sizeof(WCHAR)) + fileref->filepart.Length; - - fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG); - if (!fileref->full_filename.Buffer) { - ERR("out of memory\n"); - free_fileref(fileref); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - fileref->full_filename.Length = fileref->full_filename.MaximumLength = fnlen; - RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length); - - fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = ':'; - - RtlCopyMemory(&fileref->full_filename.Buffer[fileref->name_offset], fileref->filepart.Buffer, fileref->filepart.Length); - TRACE("full_filename = %.*S\n", fileref->full_filename.Length / sizeof(WCHAR), fileref->full_filename.Buffer); - - Status = set_xattr(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback); + Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", 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); @@ -1871,58 +2503,22 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC parfileref->fcb->inode_item.sequence++; parfileref->fcb->inode_item.st_ctime = now; - searchkey.obj_id = parfileref->fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - free_fileref(fileref); - goto end; - } - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { - delete_tree_item(Vcb, &tp, rollback); - offset = tp.item->key.offset; - } else { - WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfileref->fcb->subvol->id); - offset = 0; - } - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - free_fileref(fileref); - goto end; - } - - RtlCopyMemory(ii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM)); - - insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_INODE_ITEM, offset, ii, sizeof(INODE_ITEM), NULL, rollback); + 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); -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&parfileref->refcount); - WARN("fileref %p: refcount now %i\n", parfileref, rc); -#else - InterlockedIncrement(&parfileref->refcount); -#endif + ExReleaseResourceLite(&parfileref->nonpaged->children_lock); + + increase_fileref_refcount(parfileref); ExFreePool(fpus.Buffer); fpus.Buffer = NULL; } else { - if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, - IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) { - WARN("SeAccessCheck failed, returning %08x\n", Status); - goto end; - } - if (!is_file_name_valid(&fpus)) { Status = STATUS_OBJECT_NAME_INVALID; goto end; @@ -1934,6 +2530,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC ERR("file_create2 returned %08x\n", Status); goto end; } + + send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); + send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); } FileObject->FsContext = fileref->fcb; @@ -1958,7 +2557,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC RtlInitUnicodeString(&ccb->query_string, NULL); ccb->has_wildcard = FALSE; ccb->specific_file = FALSE; - ccb->access = access; + ccb->access = access_state->OriginalDesiredAccess; #ifdef DEBUG_FCB_REFCOUNTS oc = InterlockedIncrement(&fileref->fcb->open_count); @@ -1973,37 +2572,31 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC // TRACE("returning FCB %p with parent %p\n", fcb, parfcb); - Status = consider_write(Vcb); - - if (NT_SUCCESS(Status)) { -// ULONG fnlen; +// ULONG fnlen; // -// fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR); -// -// if (fcb->par != Vcb->root_fcb) -// fcb->name_offset++; -// -// fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length; -// -// fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG); -// if (!fcb->full_filename.Buffer) { -// ERR("out of memory\n"); -// Status = STATUS_INSUFFICIENT_RESOURCES; -// goto end; -// } -// -// fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen; -// RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length); -// -// if (fcb->par != Vcb->root_fcb) -// fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\'; -// -// RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length); +// fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR); +// +// if (fcb->par != Vcb->root_fcb) +// fcb->name_offset++; +// +// fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length; +// +// fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG); +// if (!fcb->full_filename.Buffer) { +// ERR("out of memory\n"); +// Status = STATUS_INSUFFICIENT_RESOURCES; +// goto end; +// } +// +// fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen; +// RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length); +// +// if (fcb->par != Vcb->root_fcb) +// fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\'; +// +// RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length); - send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); - - goto end2; - } + goto end2; end: if (fpus.Buffer) @@ -2141,44 +2734,6 @@ static __inline void debug_create_options(ULONG RequestedOptions) { } } -NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* newii; - NTSTATUS Status; - UINT64 offset = 0; - - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, subvol, &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) { - delete_tree_item(Vcb, &tp, rollback); - - offset = tp.item->key.offset; - } else { - WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id); - } - - newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!newii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(newii, ii, sizeof(INODE_ITEM)); - - insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, offset, newii, sizeof(INODE_ITEM), NULL, rollback); - - return STATUS_SUCCESS; -} - static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) { NTSTATUS Status; @@ -2199,9 +2754,9 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) { return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, *data, 0, size, &bytes_read); + Status = read_file(fcb, *data, 0, size, &bytes_read, NULL); if (!NT_SUCCESS(Status)) { - ERR("read_file returned %08x\n", Status); + ERR("read_file_fcb returned %08x\n", Status); ExFreePool(*data); return Status; } @@ -2270,27 +2825,28 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) { return Status; } } - } else { - UINT16 datalen; - - if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, data, &datalen)) + } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { + if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0) return STATUS_INTERNAL_ERROR; - - if (!*data) - return STATUS_INTERNAL_ERROR; - - if (datalen < sizeof(ULONG)) { + + if (fcb->reparse_xattr.Length < sizeof(ULONG)) { WARN("xattr was too short to be a reparse point\n"); - ExFreePool(*data); return STATUS_INTERNAL_ERROR; } - Status = FsRtlValidateReparsePointBuffer(datalen, (REPARSE_DATA_BUFFER*)*data); + Status = FsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer); if (!NT_SUCCESS(Status)) { ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status); - ExFreePool(*data); return Status; } + + *data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG); + if (!*data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length); } return STATUS_SUCCESS; @@ -2304,8 +2860,6 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN ccb* ccb; device_extension* Vcb = DeviceObject->DeviceExtension; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); - ULONG access; - PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState; USHORT unparsed; file_ref *related, *fileref; #ifdef DEBUG_FCB_REFCOUNTS @@ -2322,15 +2876,16 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN Status = STATUS_INVALID_PARAMETER; goto exit; } - - if (options & FILE_OPEN_BY_FILE_ID) { - WARN("FILE_OPEN_BY_FILE_ID not supported\n"); - Status = STATUS_NOT_IMPLEMENTED; - goto exit; - } FileObject = Stack->FileObject; - + + if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) { + struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2; + + related = relatedccb->fileref; + } else + related = NULL; + debug_create_options(options); switch (RequestedDisposition) { @@ -2374,21 +2929,42 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags - if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) { - struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2; + if (options & FILE_OPEN_BY_FILE_ID) { + if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) { + UINT64 inode; + + RtlCopyMemory(&inode, FileObject->FileName.Buffer, sizeof(UINT64)); + + if (related->fcb == Vcb->root_fileref->fcb && inode == 0) + inode = Vcb->root_fileref->fcb->inode; + + if (inode == 0) { // we use 0 to mean the parent of a subvolume + fileref = related->parent; + increase_fileref_refcount(fileref); + Status = STATUS_SUCCESS; + } else + Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref); + } else { + WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n"); + Status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + } else { + if (related && FileObject->FileName.Length != 0 && FileObject->FileName.Buffer[0] == '\\') { + Status = STATUS_OBJECT_NAME_INVALID; + goto exit; + } - related = relatedccb->fileref; - } else - related = NULL; - - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); - Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed); - ExReleaseResourceLite(&Vcb->fcb_lock); + Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed); + } if (Status == STATUS_REPARSE) { REPARSE_DATA_BUFFER* data; + ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE); Status = get_reparse_block(fileref->fcb, (UINT8**)&data); + ExReleaseResourceLite(fileref->fcb->Header.Resource); + if (!NT_SUCCESS(Status)) { ERR("get_reparse_block returned %08x\n", Status); @@ -2417,7 +2993,6 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN ExReleaseResourceLite(&Vcb->fcb_lock); Status = STATUS_OBJECT_NAME_NOT_FOUND; - goto exit; // FIXME? } if (NT_SUCCESS(Status)) { @@ -2440,17 +3015,18 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN if (NT_SUCCESS(Status)) { // file already exists file_ref* sf; - if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) { - Status = STATUS_MEDIA_WRITE_PROTECTED; - free_fileref(fileref); - goto exit; - } - - if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE || - RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) { - Status = STATUS_ACCESS_DENIED; - free_fileref(fileref); - goto exit; + 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; + free_fileref(fileref); + goto exit; + } + + if (Vcb->readonly) { + Status = STATUS_MEDIA_WRITE_PROTECTED; + free_fileref(fileref); + goto exit; + } } TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE"); @@ -2469,12 +3045,49 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN sf = sf->parent; } - if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) { - Status = STATUS_ACCESS_DENIED; + if (fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { + ACCESS_MASK allowed = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | + SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | + FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY | + FILE_TRAVERSE; + + if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY) + allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD; + + if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) { + Status = STATUS_ACCESS_DENIED; + free_fileref(fileref); + 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; free_fileref(fileref); goto exit; } + if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) { UINT8* data; @@ -2506,18 +3119,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN goto exit; } - if (!SeAccessCheck(fileref->fcb->ads ? fileref->parent->fcb->sd : fileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL, - IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) { - WARN("SeAccessCheck failed, returning %08x\n", Status); - - ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); - free_fileref(fileref); - ExReleaseResourceLite(&Vcb->fcb_lock); - goto exit; - } - if (fileref->fcb->open_count > 0) { - Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE); + Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, + Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE); if (!NT_SUCCESS(Status)) { WARN("IoCheckShareAccess failed, returning %08x\n", Status); @@ -2528,10 +3132,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN goto exit; } } else { - IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access); + IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, + Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access); } - if (access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) { + if (Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) { if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) { Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; @@ -2543,7 +3148,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN } if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { - ULONG defda; + ULONG defda, oldatts, filter; + LARGE_INTEGER time; + BTRFS_TIME now; if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { WARN("cannot overwrite readonly file\n"); @@ -2569,7 +3176,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN } if (Irp->Overlay.AllocationSize.QuadPart > 0) { - Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback); + Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback); if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08x\n", Status); @@ -2578,12 +3185,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN } } - Status = update_inode_item(Vcb, fileref->fcb->subvol, fileref->fcb->inode, &fileref->fcb->inode_item, rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_inode_item returned %08x\n", Status); - free_fileref(fileref); - goto exit; - } + filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE; + + mark_fcb_dirty(fileref->fcb); + + 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); @@ -2593,48 +3199,24 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN else fileref->fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE; - if (Stack->Parameters.Create.FileAttributes != defda) { - char val[64]; + if (fileref->fcb->atts != oldatts) { + fileref->fcb->atts_changed = TRUE; + fileref->fcb->atts_deleted = Stack->Parameters.Create.FileAttributes == defda; + filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + } - sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes); - - Status = set_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - free_fileref(fileref); - goto exit; - } - } else - delete_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback); + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + fileref->fcb->inode_item.transid = Vcb->superblock.generation; + fileref->fcb->inode_item.sequence++; + fileref->fcb->inode_item.st_ctime = now; + fileref->fcb->inode_item.st_mtime = now; + // FIXME - truncate streams // FIXME - do we need to alter parent directory's times? - // FIXME - send notifications - Status = consider_write(Vcb); - if (!NT_SUCCESS(Status)) { - ERR("consider_write returned %08x\n", Status); - free_fileref(fileref); - 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; + send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED); } FileObject->FsContext = fileref->fcb; @@ -2661,7 +3243,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN RtlInitUnicodeString(&ccb->query_string, NULL); ccb->has_wildcard = FALSE; ccb->specific_file = FALSE; - ccb->access = access; + ccb->access = Stack->Parameters.Create.SecurityContext->DesiredAccess; ccb->fileref = fileref; @@ -2716,8 +3298,8 @@ exit: NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; - device_extension* Vcb = NULL; - BOOL top_level; + device_extension* Vcb = DeviceObject->DeviceExtension; + BOOL top_level, locked = FALSE; LIST_ENTRY rollback; TRACE("create (flags = %x)\n", Irp->Flags); @@ -2729,7 +3311,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { top_level = is_top_level(Irp); /* return success if just called for FS device object */ - if (DeviceObject == devobj) { + if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) { TRACE("create called for FS device object\n"); Irp->IoStatus.Information = FILE_OPENED; @@ -2740,6 +3322,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Vcb = DeviceObject->DeviceExtension; ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE); + locked = TRUE; IrpSp = IoGetCurrentIrpStackLocation(Irp); @@ -2820,27 +3403,24 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object; + if (!IrpSp->FileObject->Vpb) + IrpSp->FileObject->Vpb = DeviceObject->Vpb; + Irp->IoStatus.Information = FILE_OPENED; Status = STATUS_SUCCESS; } else { - BOOL exclusive, skip_lock; - ULONG disposition; + BOOL skip_lock; TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer); if (IrpSp->FileObject->RelatedFileObject) TRACE("related file = %S\n", file_desc(IrpSp->FileObject->RelatedFileObject)); - disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff); - - // We acquire the lock exclusively if there's the possibility we might be writing - exclusive = disposition != FILE_OPEN; - // Don't lock again if we're being called from within CcCopyRead etc. skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock); if (!skip_lock) - acquire_tree_lock(Vcb, exclusive); + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); // ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE); // Status = NtfsCreateFile(DeviceObject, @@ -2848,13 +3428,13 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Status = open_file(DeviceObject, Irp, &rollback); // ExReleaseResourceLite(&Vpb->DirResource); - if (exclusive && !NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) do_rollback(Vcb, &rollback); else clear_rollback(&rollback); if (!skip_lock) - release_tree_lock(Vcb, exclusive); + ExReleaseResourceLite(&Vcb->tree_lock); // Status = STATUS_ACCESS_DENIED; } @@ -2866,7 +3446,8 @@ exit: TRACE("create returning %08x\n", Status); - ExReleaseResourceLite(&Vcb->load_lock); + if (locked) + ExReleaseResourceLite(&Vcb->load_lock); if (top_level) IoSetTopLevelIrp(NULL); diff --git a/reactos/drivers/filesystems/btrfs/dirctrl.c b/reactos/drivers/filesystems/btrfs/dirctrl.c index 30bca36599a..972bc6f1b10 100644 --- a/reactos/drivers/filesystems/btrfs/dirctrl.c +++ b/reactos/drivers/filesystems/btrfs/dirctrl.c @@ -25,14 +25,16 @@ enum DirEntryType { typedef struct { KEY key; + BOOL name_alloc; char* name; ULONG namelen; UINT8 type; enum DirEntryType dir_entry_type; } dir_entry; -ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type) { - ULONG att, tag, br; +static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) { + fcb* fcb; + ULONG tag = 0, br; NTSTATUS Status; // FIXME - will this slow things down? @@ -43,43 +45,41 @@ ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY) return 0; - att = get_file_attributes(Vcb, NULL, subvol, inode, type, FALSE, FALSE); - - if (!(att & FILE_ATTRIBUTE_REPARSE_POINT)) + if (!(atts & FILE_ATTRIBUTE_REPARSE_POINT)) return 0; + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb); + if (!NT_SUCCESS(Status)) { + ERR("open_fcb returned %08x\n", Status); + ExReleaseResourceLite(&Vcb->fcb_lock); + return 0; + } + ExReleaseResourceLite(&Vcb->fcb_lock); + + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); + if (type == BTRFS_TYPE_DIRECTORY) { - UINT8* data; - UINT16 datalen; + if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) + goto end; - if (!get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen)) - return 0; - - if (!data) - return 0; - - if (datalen < sizeof(ULONG)) { - ExFreePool(data); - return 0; - } - - RtlCopyMemory(&tag, data, sizeof(ULONG)); - - ExFreePool(data); + RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG)); } else { - // FIXME - see if file loaded and cached, and do CcCopyRead if it is - - Status = read_file(Vcb, subvol, inode, (UINT8*)&tag, 0, sizeof(ULONG), &br); - + Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL); if (!NT_SUCCESS(Status)) { ERR("read_file returned %08x\n", Status); - return 0; + goto end; } if (br < sizeof(ULONG)) - return 0; + goto end; } +end: + ExReleaseResourceLite(fcb->Header.Resource); + + free_fcb(fcb); + return tag; } @@ -90,7 +90,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L INODE_ITEM ii; NTSTATUS Status; ULONG stringlen; - BOOL dotfile; + ULONG atts; IrpSp = IoGetCurrentIrpStackLocation(Irp); @@ -128,24 +128,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L LIST_ENTRY* le; BOOL found = FALSE; - if (fileref) { - ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE); - - le = fileref->children.Flink; - while (le != &fileref->children) { - file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry); + ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE); + if (!IsListEmpty(&r->fcbs)) { + le = r->fcbs.Flink; + while (le != &r->fcbs) { + struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry); - if (c->fcb->subvol == r && c->fcb->inode == inode && !c->fcb->ads) { - ii = c->fcb->inode_item; + if (fcb2->inode == inode && !fcb2->ads) { + ii = fcb2->inode_item; + atts = fcb2->atts; found = TRUE; break; } le = le->Flink; } - - ExReleaseResourceLite(&fcb->Vcb->fcb_lock); } + ExReleaseResourceLite(&fcb->Vcb->fcb_lock); if (!found) { KEY searchkey; @@ -170,6 +169,16 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L if (tp.item->size > 0) RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size)); + + if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || + IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation || + IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation || + IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) { + + BOOL dotfile = de->namelen > 1 && de->name[0] == '.'; + + atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + } } break; @@ -179,6 +188,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L ii = fcb->inode_item; r = fcb->subvol; inode = fcb->inode; + atts = fcb->atts; break; case DirEntryType_Parent: @@ -186,6 +196,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L ii = fileref->parent->fcb->inode_item; r = fileref->parent->fcb->subvol; inode = fileref->parent->fcb->inode; + atts = fileref->parent->fcb->atts; } else { ERR("no fileref\n"); return STATUS_INTERNAL_ERROR; @@ -208,8 +219,6 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L } } - dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0); - switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { case FileBothDirectoryInformation: { @@ -220,7 +229,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen; if (needed > *len) { - WARN("buffer overflow - %u > %u\n", needed, *len); + TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } @@ -232,9 +241,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L fbdi->ChangeTime.QuadPart = 0; fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks; - fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + fbdi->FileAttributes = atts; fbdi->FileNameLength = stringlen; - fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type); + fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); fbdi->ShortNameLength = 0; // fibdi->ShortName[12]; @@ -259,7 +268,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen; if (needed > *len) { - WARN("buffer overflow - %u > %u\n", needed, *len); + TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } @@ -271,7 +280,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L fdi->ChangeTime.QuadPart = 0; fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks; - fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + fdi->FileAttributes = atts; fdi->FileNameLength = stringlen; Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen); @@ -295,7 +304,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen; if (needed > *len) { - WARN("buffer overflow - %u > %u\n", needed, *len); + TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } @@ -307,9 +316,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L ffdi->ChangeTime.QuadPart = 0; ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks; - ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + ffdi->FileAttributes = atts; ffdi->FileNameLength = stringlen; - ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type); + ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen); @@ -332,7 +341,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen; if (needed > *len) { - WARN("buffer overflow - %u > %u\n", needed, *len); + TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } @@ -347,9 +356,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L fibdi->ChangeTime.QuadPart = 0; fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks; - fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE); + fibdi->FileAttributes = atts; fibdi->FileNameLength = stringlen; - fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type); + fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts); fibdi->ShortNameLength = 0; // fibdi->ShortName[12]; fibdi->FileId.QuadPart = inode; @@ -379,7 +388,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen; if (needed > *len) { - WARN("buffer overflow - %u > %u\n", needed, *len); + TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } @@ -419,19 +428,23 @@ 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(fcb* fcb, file_ref* fileref, UINT64* offset, dir_entry* de, traverse_ptr* tp) { +static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) { KEY searchkey; - traverse_ptr next_tp; + traverse_ptr tp, next_tp; DIR_ITEM* di; NTSTATUS Status; + file_ref* fr; + LIST_ENTRY* le; + char* name; - if (fileref && fileref->parent) { // don't return . and .. if root directory + if (fileref->parent) { // don't return . and .. if root directory if (*offset == 0) { - de->key.obj_id = fcb->inode; + de->key.obj_id = fileref->fcb->inode; de->key.obj_type = TYPE_INODE_ITEM; de->key.offset = 0; de->dir_entry_type = DirEntryType_Self; de->name = "."; + de->name_alloc = FALSE; de->namelen = 1; de->type = BTRFS_TYPE_DIRECTORY; @@ -444,6 +457,7 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offs de->key.offset = 0; de->dir_entry_type = DirEntryType_Parent; de->name = ".."; + de->name_alloc = FALSE; de->namelen = 2; de->type = BTRFS_TYPE_DIRECTORY; @@ -453,77 +467,171 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offs } } - if (!tp->tree) { - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = *offset; + if (*offset < 2) + *offset = 2; + + ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE); + + fr = NULL; + le = fileref->children.Flink; + + // skip entries before offset + while (le != &fileref->children) { + file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry); - Status = find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - tp->tree = NULL; - return Status; + if (fr2->index >= *offset) { + fr = fr2; + break; } - TRACE("found item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); - - if (keycmp(&tp->item->key, &searchkey) == -1) { - if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) { - *tp = next_tp; + le = le->Flink; + } + + do { + if (fr && fr->index == *offset) { + if (!fr->deleted) { + if (fr->fcb->subvol == fileref->fcb->subvol) { + de->key.obj_id = fr->fcb->inode; + de->key.obj_type = TYPE_INODE_ITEM; + de->key.offset = 0; + } else { + de->key.obj_id = fr->fcb->subvol->id; + de->key.obj_type = TYPE_ROOT_ITEM; + de->key.offset = 0; + } - TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); + name = fr->utf8.Buffer; + de->namelen = fr->utf8.Length; + de->type = fr->fcb->type; + de->dir_entry_type = DirEntryType_File; + + (*offset)++; + + Status = STATUS_SUCCESS; + goto end; + } else { + (*offset)++; + fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry); + continue; } } - if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) { - tp->tree = NULL; - return STATUS_NO_MORE_FILES; + searchkey.obj_id = fileref->fcb->inode; + searchkey.obj_type = TYPE_DIR_INDEX; + searchkey.offset = *offset; + + Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; } - *offset = tp->item->key.offset + 1; - - di = (DIR_ITEM*)tp->item->data; - - if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) { - 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(DIR_ITEM)); - - tp->tree = NULL; - return STATUS_INTERNAL_ERROR; + if (keycmp(&tp.item->key, &searchkey) == -1) { + if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE)) + tp = next_tp; } - de->key = di->key; - de->name = di->name; - de->namelen = di->n; - de->type = di->type; - de->dir_entry_type = DirEntryType_File; - - return STATUS_SUCCESS; - } else { - if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) { - if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) { - *tp = next_tp; - - *offset = tp->item->key.offset + 1; - - di = (DIR_ITEM*)tp->item->data; - - if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) { - 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(DIR_ITEM)); - return STATUS_INTERNAL_ERROR; + if (keycmp(&tp.item->key, &searchkey) != -1 && tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + do { + if (fr) { + if (fr->index <= tp.item->key.offset && !fr->deleted) { + if (fr->fcb->subvol == fileref->fcb->subvol) { + de->key.obj_id = fr->fcb->inode; + de->key.obj_type = TYPE_INODE_ITEM; + de->key.offset = 0; + } else { + de->key.obj_id = fr->fcb->subvol->id; + de->key.obj_type = TYPE_ROOT_ITEM; + de->key.offset = 0; + } + + name = fr->utf8.Buffer; + de->namelen = fr->utf8.Length; + de->type = fr->fcb->type; + de->dir_entry_type = DirEntryType_File; + + *offset = fr->index + 1; + + Status = STATUS_SUCCESS; + goto end; + } + + if (fr->index == tp.item->key.offset && fr->deleted) + break; + + fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry); } - - de->key = di->key; - de->name = di->name; - de->namelen = di->n; - de->type = di->type; + } while (fr && fr->index < tp.item->key.offset); + + if (fr && fr->index == tp.item->key.offset && fr->deleted) { + *offset = fr->index + 1; + fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry); + continue; + } + + *offset = tp.item->key.offset + 1; + + di = (DIR_ITEM*)tp.item->data; + + if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) { + 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(DIR_ITEM)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + de->key = di->key; + name = di->name; + de->namelen = di->n; + de->type = di->type; + de->dir_entry_type = DirEntryType_File; + + Status = STATUS_SUCCESS; + goto end; + } else { + if (fr) { + if (fr->fcb->subvol == fileref->fcb->subvol) { + de->key.obj_id = fr->fcb->inode; + de->key.obj_type = TYPE_INODE_ITEM; + de->key.offset = 0; + } else { + de->key.obj_id = fr->fcb->subvol->id; + de->key.obj_type = TYPE_ROOT_ITEM; + de->key.offset = 0; + } + + name = fr->utf8.Buffer; + de->namelen = fr->utf8.Length; + de->type = fr->fcb->type; de->dir_entry_type = DirEntryType_File; - return STATUS_SUCCESS; - } else - return STATUS_NO_MORE_FILES; - } else - return STATUS_NO_MORE_FILES; - } + *offset = fr->index + 1; + + Status = STATUS_SUCCESS; + goto end; + } else { + Status = STATUS_NO_MORE_FILES; + goto end; + } + } + } while (TRUE); + +end: + ExReleaseResourceLite(&fileref->nonpaged->children_lock); + + if (NT_SUCCESS(Status)) { + de->name_alloc = TRUE; + + de->name = ExAllocatePoolWithTag(PagedPool, de->namelen, ALLOC_TAG); + if (!de->name) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(de->name, name, de->namelen); + } else + de->name_alloc = FALSE; + + return Status; } static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { @@ -538,8 +646,9 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ULONG count; BOOL has_wildcard = FALSE, specific_file = FALSE, initial; // UINT64 num_reads_orig; - traverse_ptr tp; dir_entry de; + UINT64 newoffset; + ANSI_STRING utf8; TRACE("query directory\n"); @@ -552,7 +661,22 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ccb = IrpSp->FileObject->FsContext2; fileref = ccb ? ccb->fileref : NULL; - acquire_tree_lock(fcb->Vcb, FALSE); + utf8.Buffer = NULL; + + if (!fileref) + return STATUS_INVALID_PARAMETER; + + if (!ccb) { + ERR("ccb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!(ccb->access & FILE_LIST_DIRECTORY)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); TRACE("%S\n", file_desc(IrpSp->FileObject)); @@ -593,82 +717,65 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } } - if (IrpSp->Parameters.QueryDirectory.FileName) { -// int i; -// WCHAR* us; - + if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) { TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer); -// if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') { -// specific_file = TRUE; -// for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) { -// if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') { -// has_wildcard = TRUE; -// specific_file = FALSE; -// } -// } -// } - has_wildcard = TRUE; + if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') { + specific_file = TRUE; + if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) { + has_wildcard = TRUE; + specific_file = FALSE; + } + } if (ccb->query_string.Buffer) RtlFreeUnicodeString(&ccb->query_string); -// us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG); -// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length); -// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0; - -// ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG); -// utf16_to_utf8(us, ccb->query_string); - -// ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG); -// RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, -// IrpSp->Parameters.QueryDirectory.FileName->Length); -// ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length; -// ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length; + if (has_wildcard) RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE); + else { + ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG); + if (!ccb->query_string.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length; + RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length); + } ccb->has_wildcard = has_wildcard; ccb->specific_file = specific_file; - -// ExFreePool(us); } else { has_wildcard = ccb->has_wildcard; specific_file = ccb->specific_file; - if (!(IrpSp->Flags & SL_RESTART_SCAN)) + if (!(IrpSp->Flags & SL_RESTART_SCAN)) { initial = FALSE; + + if (specific_file) { + Status = STATUS_NO_MORE_FILES; + goto end; + } + } } if (ccb->query_string.Buffer) { TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer); } - tp.tree = NULL; - Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp); + newoffset = ccb->query_dir_offset; + Status = next_dir_entry(fileref, &newoffset, &de); if (!NT_SUCCESS(Status)) { if (Status == STATUS_NO_MORE_FILES && initial) Status = STATUS_NO_SUCH_FILE; goto end; } - - // FIXME - make this work -// if (specific_file) { -// UINT64 filesubvol, fileinode; -// WCHAR* us; -// -// us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG); -// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length); -// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0; -// -// if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) { -// ExFreePool(us); -// return STATUS_NO_MORE_FILES; -// } -// -// ExFreePool(us); -// } + ccb->query_dir_offset = newoffset; + buf = map_user_buffer(Irp); if (Irp->MdlAddress && !buf) { @@ -679,8 +786,89 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { length = IrpSp->Parameters.QueryDirectory.Length; -// if (specific_file) { - if (has_wildcard) { + if (specific_file) { + BOOL found = FALSE; + root* found_subvol; + UINT64 found_inode, found_index; + UINT8 found_type; + UNICODE_STRING us; + LIST_ENTRY* le; + + Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + goto end; + } + + ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE); + + le = fileref->children.Flink; + while (le != &fileref->children) { + file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry); + + if (!fr2->deleted && fr2->filepart_uc.Length == us.Length && + RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) { + found = TRUE; + + if (fr2->fcb->subvol == fcb->subvol) { + de.key.obj_id = fr2->fcb->inode; + de.key.obj_type = TYPE_INODE_ITEM; + de.key.offset = 0; + } else { + de.key.obj_id = fr2->fcb->subvol->id; + de.key.obj_type = TYPE_ROOT_ITEM; + de.key.offset = 0; + } + + de.name = ExAllocatePoolWithTag(PagedPool, fr2->utf8.Length, ALLOC_TAG); + if (!de.name) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(de.name, fr2->utf8.Buffer, fr2->utf8.Length); + + de.name_alloc = TRUE; + de.namelen = fr2->utf8.Length; + de.type = fr2->fcb->type; + de.dir_entry_type = DirEntryType_File; + break; + } + + le = le->Flink; + } + + ExReleaseResourceLite(&fileref->nonpaged->children_lock); + + if (us.Buffer) + 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); + + if (!NT_SUCCESS(Status)) { + Status = STATUS_NO_SUCH_FILE; + goto end; + } + + if (found_subvol == fcb->subvol) { + de.key.obj_id = found_inode; + de.key.obj_type = TYPE_INODE_ITEM; + de.key.offset = 0; + } else { + de.key.obj_id = found_subvol->id; + de.key.obj_type = TYPE_ROOT_ITEM; + de.key.offset = 0; + } + + de.name = utf8.Buffer; + de.name_alloc = FALSE; + de.namelen = utf8.Length; + de.type = found_type; + de.dir_entry_type = DirEntryType_File; + } + } else if (has_wildcard) { WCHAR* uni_fn; ULONG stringlen; UNICODE_STRING di_uni_fn; @@ -709,10 +897,16 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { di_uni_fn.Buffer = uni_fn; while (!FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) { - Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp); + if (de.name_alloc) + ExFreePool(de.name); + + newoffset = ccb->query_dir_offset; + Status = next_dir_entry(fileref, &newoffset, &de); ExFreePool(uni_fn); if (NT_SUCCESS(Status)) { + ccb->query_dir_offset = newoffset; + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen); if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN returned %08x\n", Status); @@ -752,6 +946,9 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = query_dir_item(fcb, fileref, buf, &length, Irp, &de, fcb->subvol); + if (de.name_alloc) + ExFreePool(de.name); + count = 0; if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) { lastitem = (UINT8*)buf; @@ -778,7 +975,8 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { WCHAR* uni_fn = NULL; UNICODE_STRING di_uni_fn; - Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp); + newoffset = ccb->query_dir_offset; + Status = next_dir_entry(fileref, &newoffset, &de); if (NT_SUCCESS(Status)) { if (has_wildcard) { ULONG stringlen; @@ -786,6 +984,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen); if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN returned %08x\n", Status); + if (de.name_alloc) ExFreePool(de.name); goto end; } @@ -793,6 +992,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (!uni_fn) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; + if (de.name_alloc) ExFreePool(de.name); goto end; } @@ -801,6 +1001,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (!NT_SUCCESS(Status)) { ERR("RtlUTF8ToUnicodeN returned %08x\n", Status); ExFreePool(uni_fn); + if (de.name_alloc) ExFreePool(de.name); goto end; } @@ -821,19 +1022,24 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ULONG* lastoffset = (ULONG*)lastitem; *lastoffset = (ULONG)(curitem - lastitem); + ccb->query_dir_offset = newoffset; lastitem = curitem; } else { if (uni_fn) ExFreePool(uni_fn); - + if (de.name_alloc) ExFreePool(de.name); break; } - } + } else + ccb->query_dir_offset = newoffset; if (uni_fn) { ExFreePool(uni_fn); uni_fn = NULL; } + + if (de.name_alloc) + ExFreePool(de.name); } else { if (Status == STATUS_NO_MORE_FILES) Status = STATUS_SUCCESS; @@ -848,10 +1054,12 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length; end: - release_tree_lock(fcb->Vcb, FALSE); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); -// TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig)); TRACE("returning %08x\n", Status); + + if (utf8.Buffer) + ExFreePool(utf8.Buffer); return Status; } @@ -863,16 +1071,26 @@ static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) ccb* ccb = FileObject->FsContext2; file_ref* fileref = ccb->fileref; NTSTATUS Status; -// WCHAR fn[MAX_PATH]; TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n"); + if (!ccb) { + ERR("ccb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + if (!fileref) { ERR("no fileref\n"); return STATUS_INVALID_PARAMETER; } - acquire_tree_lock(fcb->Vcb, FALSE); + if (!(ccb->access & FILE_LIST_DIRECTORY)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); if (fcb->type != BTRFS_TYPE_DIRECTORY) { Status = STATUS_INVALID_PARAMETER; @@ -882,14 +1100,24 @@ static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) // FIXME - raise exception if FCB marked for deletion? TRACE("%S\n", file_desc(FileObject)); + + if (ccb->filename.Length == 0) { + Status = fileref_get_filename(fileref, &ccb->filename, NULL); + if (!NT_SUCCESS(Status)) { + ERR("fileref_get_filename returned %08x\n", Status); + goto end; + } + } - FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fileref->full_filename, - IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL); + FsRtlNotifyFilterChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&ccb->filename, + IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, + NULL, NULL, NULL); Status = STATUS_PENDING; end: - release_tree_lock(fcb->Vcb, FALSE); + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); return Status; } @@ -899,6 +1127,7 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I NTSTATUS Status; ULONG func; BOOL top_level; + device_extension* Vcb = DeviceObject->DeviceExtension; TRACE("directory control\n"); @@ -906,6 +1135,11 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + IrpSp = IoGetCurrentIrpStackLocation(Irp); Irp->IoStatus.Information = 0; @@ -914,7 +1148,7 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I switch (func) { case IRP_MN_NOTIFY_CHANGE_DIRECTORY: - Status = notify_change_directory(DeviceObject->DeviceExtension, Irp); + Status = notify_change_directory(Vcb, Irp); break; case IRP_MN_QUERY_DIRECTORY: @@ -927,16 +1161,18 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I Irp->IoStatus.Status = Status; break; } - - if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) { - Irp->IoStatus.Status = Status; - - if (Irp->UserIosb) - *Irp->UserIosb = Irp->IoStatus; - - IoCompleteRequest( Irp, IO_DISK_INCREMENT ); - } + if (Status == STATUS_PENDING) + goto exit; + + Irp->IoStatus.Status = Status; + +// if (Irp->UserIosb) +// *Irp->UserIosb = Irp->IoStatus; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + +exit: if (top_level) IoSetTopLevelIrp(NULL); diff --git a/reactos/drivers/filesystems/btrfs/extent-tree.c b/reactos/drivers/filesystems/btrfs/extent-tree.c index 2699301eb46..2bc760e6781 100644 --- a/reactos/drivers/filesystems/btrfs/extent-tree.c +++ b/reactos/drivers/filesystems/btrfs/extent-tree.c @@ -25,7 +25,9 @@ static __inline ULONG get_extent_data_len(UINT8 type) { case TYPE_EXTENT_DATA_REF: return sizeof(EXTENT_DATA_REF); - // FIXME - TYPE_EXTENT_REF_V0 + case TYPE_EXTENT_REF_V0: + return sizeof(EXTENT_REF_V0); + // FIXME - TYPE_SHARED_BLOCK_REF case TYPE_SHARED_DATA_REF: @@ -47,7 +49,12 @@ static __inline UINT64 get_extent_data_refcount(UINT8 type, void* data) { return edr->count; } - // FIXME - TYPE_EXTENT_REF_V0 + case TYPE_EXTENT_REF_V0: + { + EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)data; + return erv0->count; + } + // FIXME - TYPE_SHARED_BLOCK_REF case TYPE_SHARED_DATA_REF: @@ -61,16 +68,20 @@ static __inline UINT64 get_extent_data_refcount(UINT8 type, void* data) { } } -static UINT64 get_extent_data_ref_hash(EXTENT_DATA_REF* edr) { +static UINT64 get_extent_data_ref_hash2(UINT64 root, UINT64 objid, UINT64 offset) { UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff; - high_crc = calc_crc32c(high_crc, (UINT8*)&edr->root, sizeof(UINT64)); - low_crc = calc_crc32c(low_crc, (UINT8*)&edr->objid, sizeof(UINT64)); - low_crc = calc_crc32c(low_crc, (UINT8*)&edr->offset, sizeof(UINT64)); + high_crc = calc_crc32c(high_crc, (UINT8*)&root, sizeof(UINT64)); + low_crc = calc_crc32c(low_crc, (UINT8*)&objid, sizeof(UINT64)); + low_crc = calc_crc32c(low_crc, (UINT8*)&offset, sizeof(UINT64)); return ((UINT64)high_crc << 31) ^ (UINT64)low_crc; } +static __inline UINT64 get_extent_data_ref_hash(EXTENT_DATA_REF* edr) { + return get_extent_data_ref_hash2(edr->root, edr->objid, edr->offset); +} + static UINT64 get_extent_hash(UINT8 type, void* data) { if (type == TYPE_EXTENT_DATA_REF) { return get_extent_data_ref_hash((EXTENT_DATA_REF*)data); @@ -153,17 +164,40 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, } else if (tp.item->key.offset != size) { ERR("extent %llx exists, but with size %llx rather than %llx expected\n", tp.item->key.obj_id, tp.item->key.offset, size); return STATUS_INTERNAL_ERROR; - } else if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { + } + + if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { + EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data; + TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - Status = convert_old_data_extent(Vcb, address, size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_old_data_extent returned %08x\n", Status); - return Status; + ei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM), ALLOC_TAG); + + if (!ei) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; } - return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback); - } else if (tp.item->size < sizeof(EXTENT_ITEM)) { + ei->refcount = eiv0->refcount; + ei->generation = Vcb->superblock.generation; + ei->flags = EXTENT_ITEM_DATA; + + 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)) { + ERR("insert_tree_item failed\n"); + ExFreePool(ei); + return STATUS_INTERNAL_ERROR; + } + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + } + + if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); return STATUS_INTERNAL_ERROR; } @@ -204,18 +238,6 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, return STATUS_INTERNAL_ERROR; } - if (secttype == TYPE_SHARED_DATA_REF) { - TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id); - - Status = convert_shared_data_extent(Vcb, address, size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_shared_data_extent returned %08x\n", Status); - return Status; - } - - return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback); - } - // If inline extent already present, increase refcount and return if (secttype == type) { @@ -286,8 +308,6 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, if (secttype > type) break; - len--; - if (secttype == type) { UINT64 sectoff = get_extent_hash(secttype, ptr + 1); @@ -295,7 +315,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, break; } - len -= sectlen; + len -= sectlen + sizeof(UINT8); ptr += sizeof(UINT8) + sectlen; } @@ -306,7 +326,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, newei->refcount += get_extent_data_refcount(type, data); if (len > 0) - RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data) + sizeof(UINT8) + datalen, ptr, len + 1); + RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data) + sizeof(UINT8) + datalen, ptr, len); ptr = (ptr - tp.item->data) + (UINT8*)newei; @@ -409,10 +429,10 @@ 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, root* subvol, 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, LIST_ENTRY* rollback) { EXTENT_DATA_REF edr; - edr.root = subvol->id; + edr.root = root; edr.objid = inode; edr.offset = offset; edr.count = refcount; @@ -426,57 +446,16 @@ void decrease_chunk_usage(chunk* c, UINT64 delta) { TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta); } -static NTSTATUS remove_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* changed_sector_list) { - chunk* c; - LIST_ENTRY* le; - - if (changed_sector_list) { - changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); - if (!sc) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - sc->ol.key = address; - sc->checksums = NULL; - sc->length = size / Vcb->superblock.sector_size; - - sc->deleted = TRUE; - - insert_into_ordered_list(changed_sector_list, &sc->ol); - } - - c = NULL; - le = Vcb->chunks.Flink; - while (le != &Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); - - if (address >= c->offset && address + size < c->offset + c->chunk_item->size) - break; - - le = le->Flink; - } - if (le == &Vcb->chunks) c = NULL; - - if (c) { - decrease_chunk_usage(c, size); - - add_to_space_list(c, address, size, SPACE_TYPE_DELETING); - } - - return STATUS_SUCCESS; -} - static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, - UINT8 level, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { + UINT8 level, UINT64 parent, LIST_ENTRY* rollback) { KEY searchkey; NTSTATUS Status; traverse_ptr tp, tp2; EXTENT_ITEM* ei; ULONG len; - UINT64 inline_rc, offset; + UINT64 inline_rc; UINT8* ptr; - UINT32 rc = get_extent_data_refcount(type, data); + UINT32 rc = data ? get_extent_data_refcount(type, data) : 1; ULONG datalen = get_extent_data_len(type); // FIXME - handle trees @@ -502,15 +481,34 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, } if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { + EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data; + TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - Status = convert_old_data_extent(Vcb, address, size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_old_data_extent returned %08x\n", Status); - return Status; + ei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM), ALLOC_TAG); + + if (!ei) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; } - return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback); + ei->refcount = eiv0->refcount; + ei->generation = Vcb->superblock.generation; + ei->flags = EXTENT_ITEM_DATA; + + 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)) { + ERR("insert_tree_item failed\n"); + ExFreePool(ei); + return STATUS_INTERNAL_ERROR; + } + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } } if (tp.item->size < sizeof(EXTENT_ITEM)) { @@ -559,18 +557,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, return STATUS_INTERNAL_ERROR; } - if (secttype == TYPE_SHARED_DATA_REF) { - TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id); - - Status = convert_shared_data_extent(Vcb, address, size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_shared_data_extent returned %08x\n", Status); - return Status; - } - - return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback); - } - if (secttype == type) { if (type == TYPE_EXTENT_DATA_REF) { EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8)); @@ -580,12 +566,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) { if (ei->refcount == edr->count) { - Status = remove_extent(Vcb, address, size, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("remove_extent returned %08x\n", Status); - return Status; - } - delete_tree_item(Vcb, &tp, rollback); return STATUS_SUCCESS; } @@ -631,9 +611,45 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, return STATUS_SUCCESS; } -// } else if (type == TYPE_TREE_BLOCK_REF) { -// ERR("trying to increase refcount of tree extent\n"); -// return STATUS_INTERNAL_ERROR; + } else if (type == TYPE_SHARED_DATA_REF) { + SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(UINT8)); + SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data; + ULONG neweilen; + EXTENT_ITEM* newei; + + if (sectsdr->offset == sdr->offset) { + // We ignore sdr->count, and assume that we want to remove the whole bit + + if (ei->refcount == sectsdr->count) { + delete_tree_item(Vcb, &tp, rollback); + return STATUS_SUCCESS; + } + + neweilen = tp.item->size - sizeof(UINT8) - sectlen; + + newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG); + if (!newei) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(newei, ei, ptr - tp.item->data); + + if (len > sectlen) + RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(UINT8), len - sectlen); + + newei->generation = Vcb->superblock.generation; + newei->refcount -= rc; + + 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)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; + } } else { ERR("unhandled extent type %x\n", type); return STATUS_INTERNAL_ERROR; @@ -650,11 +666,9 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, return STATUS_INTERNAL_ERROR; } - offset = get_extent_hash(type, data); - searchkey.obj_id = address; searchkey.obj_type = type; - searchkey.offset = offset; + 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); if (!NT_SUCCESS(Status)) { @@ -679,12 +693,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) { if (ei->refcount == edr->count) { - Status = remove_extent(Vcb, address, size, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("remove_extent returned %08x\n", Status); - return Status; - } - delete_tree_item(Vcb, &tp, rollback); delete_tree_item(Vcb, &tp2, rollback); return STATUS_SUCCESS; @@ -738,25 +746,105 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, ERR("error - hash collision?\n"); return STATUS_INTERNAL_ERROR; } -// } else if (type == TYPE_TREE_BLOCK_REF) { -// ERR("trying to increase refcount of tree extent\n"); -// return STATUS_INTERNAL_ERROR; + } else if (type == TYPE_SHARED_DATA_REF) { + SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)tp2.item->data; + SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data; + EXTENT_ITEM* newei; + + if (sectsdr->offset == sdr->offset) { + // As above, we assume that we want to remove the whole shared data ref + + if (ei->refcount == sectsdr->count) { + delete_tree_item(Vcb, &tp, rollback); + delete_tree_item(Vcb, &tp2, rollback); + return STATUS_SUCCESS; + } + + delete_tree_item(Vcb, &tp2, rollback); + + newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); + if (!newei) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(newei, tp.item->data, tp.item->size); + + newei->generation = Vcb->superblock.generation; + newei->refcount -= rc; + + 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)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; + } else { + ERR("error - collision?\n"); + return STATUS_INTERNAL_ERROR; + } + } else if (type == TYPE_EXTENT_REF_V0) { + EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp2.item->data; + EXTENT_ITEM* newei; + + if (ei->refcount == erv0->count) { + delete_tree_item(Vcb, &tp, rollback); + delete_tree_item(Vcb, &tp2, rollback); + return STATUS_SUCCESS; + } + + delete_tree_item(Vcb, &tp2, rollback); + + newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); + if (!newei) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(newei, tp.item->data, tp.item->size); + + newei->generation = Vcb->superblock.generation; + newei->refcount -= rc; + + 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)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; } else { ERR("unhandled extent type %x\n", type); return STATUS_INTERNAL_ERROR; } } -NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, - UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, 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) { EXTENT_DATA_REF edr; - edr.root = subvol->id; + edr.root = root; edr.objid = inode; edr.offset = offset; edr.count = refcount; - return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, changed_sector_list, rollback); + return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback); +} + +NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, 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); +} + +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); } typedef struct { @@ -954,9 +1042,9 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_tree(Vcb, tree_address, buf); + Status = read_data(Vcb, tree_address, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL); if (!NT_SUCCESS(Status)) { - ERR("read_tree returned %08x\n", Status); + ERR("read_data returned %08x\n", Status); ExFreePool(buf); return Status; } @@ -993,177 +1081,6 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre return STATUS_SUCCESS; } -NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - LIST_ENTRY extent_refs; - LIST_ENTRY *le, *next_le; - EXTENT_ITEM* ei; - UINT64 eiflags, inline_rc; - UINT8* siptr; - ULONG len; - NTSTATUS Status; - - searchkey.obj_id = address; - searchkey.obj_type = TYPE_EXTENT_ITEM; - searchkey.offset = size; - - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (keycmp(&tp.item->key, &searchkey)) { - WARN("extent item not found for address %llx, size %llx\n", address, size); - return STATUS_SUCCESS; - } - - if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); - return STATUS_INTERNAL_ERROR; - } - - ei = (EXTENT_ITEM*)tp.item->data; - len = tp.item->size - sizeof(EXTENT_ITEM); - eiflags = ei->flags; - - InitializeListHead(&extent_refs); - - inline_rc = 0; - siptr = (UINT8*)&ei[1]; - - do { - extent_ref* er; - ULONG extlen; - - extlen = get_extent_data_len(*siptr); - - if (extlen == 0) { - ERR("unrecognized extent subitem %x\n", *siptr); - free_extent_refs(&extent_refs); - return STATUS_INTERNAL_ERROR; - } - - if (extlen > len - 1) { - ERR("extent %llx was truncated\n", address); - free_extent_refs(&extent_refs); - return STATUS_INTERNAL_ERROR; - } - - er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG); - if (!er) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - er->type = *siptr; - - er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG); - if (!er->data) { - ERR("out of memory\n"); - ExFreePool(er); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(er->data, siptr+1, extlen); - er->allocated = TRUE; - - InsertTailList(&extent_refs, &er->list_entry); - - siptr += extlen; - len -= extlen + 1; - - inline_rc += get_extent_data_refcount(er->type, er->data); - } while (len > 0); - - delete_tree_item(Vcb, &tp, rollback); - - if (inline_rc < ei->refcount) { - BOOL b; - traverse_ptr next_tp; - - do { - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - - if (tp.item->key.obj_id == address) { - ULONG extlen; - - extlen = get_extent_data_len(tp.item->key.obj_type); - - if (extlen != 0 && tp.item->size >= extlen) { - extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG); - if (!er) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - er->type = tp.item->key.obj_type; - - er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG); - if (!er->data) { - ERR("out of memory\n"); - ExFreePool(er); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(er->data, siptr+1, extlen); - er->allocated = TRUE; - - InsertTailList(&extent_refs, &er->list_entry); - - delete_tree_item(Vcb, &tp, rollback); - } - } - - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > address) - break; - } - } while (b); - } - - le = extent_refs.Flink; - while (le != &extent_refs) { - extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry); - next_le = le->Flink; - - if (er->type == TYPE_SHARED_DATA_REF) { - SHARED_DATA_REF* sdr = er->data; - - Status = populate_extent_refs_from_tree(Vcb, sdr->offset, address, &extent_refs); - if (!NT_SUCCESS(Status)) { - ERR("populate_extent_refs_from_tree returned %08x\n", Status); - free_extent_refs(&extent_refs); - return Status; - } - - RemoveEntryList(&er->list_entry); - - if (er->allocated) - ExFreePool(er->data); - - ExFreePool(er); - } - // FIXME - also do for SHARED_BLOCK_REF? - - le = next_le; - } - - Status = construct_extent_item(Vcb, address, size, eiflags, &extent_refs, rollback); - if (!NT_SUCCESS(Status)) { - ERR("construct_extent_item returned %08x\n", Status); - free_extent_refs(&extent_refs); - return Status; - } - - free_extent_refs(&extent_refs); - - return STATUS_SUCCESS; -} - NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp, next_tp; @@ -1237,3 +1154,241 @@ 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) { + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; + EXTENT_DATA_REF* edr; + BOOL old = FALSE; + + searchkey.obj_id = address; + searchkey.obj_type = TYPE_EXTENT_ITEM; + searchkey.offset = 0xffffffffffffffff; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + 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); + return 0; + } + + if (tp.item->key.offset != size) { + ERR("extent %llx had size %llx, not %llx as expected\n", address, tp.item->key.offset, size); + return 0; + } + + if (tp.item->size >= sizeof(EXTENT_ITEM)) { + EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data; + UINT32 len = tp.item->size - sizeof(EXTENT_ITEM); + UINT8* ptr = (UINT8*)&ei[1]; + + while (len > 0) { + UINT8 secttype = *ptr; + ULONG sectlen = get_extent_data_len(secttype); + UINT64 sectcount = get_extent_data_refcount(secttype, ptr + sizeof(UINT8)); + + len--; + + if (sectlen > len) { + ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen); + return 0; + } + + if (sectlen == 0) { + ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype); + return 0; + } + + if (secttype == TYPE_EXTENT_DATA_REF) { + EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8)); + + if (sectedr->root == root && sectedr->objid == objid && sectedr->offset == offset) + return sectcount; + } else if (secttype == TYPE_SHARED_DATA_REF) { + SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(UINT8)); + BOOL found = FALSE; + LIST_ENTRY* le; + + le = Vcb->shared_extents.Flink; + while (le != &Vcb->shared_extents) { + shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry); + + if (sd->address == sectsdr->offset) { + LIST_ENTRY* le2 = sd->entries.Flink; + while (le2 != &sd->entries) { + shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry); + + if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset) + return sde->edr.count; + + le2 = le2->Flink; + } + found = TRUE; + break; + } + + le = le->Flink; + } + + if (!found) + WARN("shared data extents not loaded for tree at %llx\n", sectsdr->offset); + } + + len -= sectlen; + ptr += sizeof(UINT8) + sectlen; + } + } else if (tp.item->size == sizeof(EXTENT_ITEM_V0)) + old = TRUE; + + searchkey.obj_id = address; + 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); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return 0; + } + + if (!keycmp(&searchkey, &tp.item->key)) { + if (tp.item->size < sizeof(EXTENT_DATA_REF)) + ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF)); + else { + edr = (EXTENT_DATA_REF*)tp.item->data; + + return edr->count; + } + } + + if (old) { + BOOL b; + + searchkey.obj_id = address; + searchkey.obj_type = TYPE_EXTENT_REF_V0; + searchkey.offset = 0; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return 0; + } + + do { + traverse_ptr next_tp; + + b = find_next_item(Vcb, &tp, &next_tp, FALSE); + + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + if (tp.item->size >= sizeof(EXTENT_REF_V0)) { + EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp.item->data; + + if (erv0->root == root && erv0->objid == objid) { + LIST_ENTRY* le; + BOOL found = FALSE; + + le = Vcb->shared_extents.Flink; + while (le != &Vcb->shared_extents) { + shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry); + + if (sd->address == tp.item->key.offset) { + LIST_ENTRY* le2 = sd->entries.Flink; + while (le2 != &sd->entries) { + shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry); + + if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset) + return sde->edr.count; + + le2 = le2->Flink; + } + found = TRUE; + break; + } + + le = le->Flink; + } + + if (!found) + WARN("shared data extents not loaded for tree at %llx\n", tp.item->key.offset); + } + } else { + ERR("(%llx,%x,%llx) was %x bytes, not %x as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, + tp.item->key.offset, tp.item->size, sizeof(EXTENT_REF_V0)); + } + } + + if (b) { + tp = next_tp; + + 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)) + break; + } + } while (b); + } else { + BOOL b; + + searchkey.obj_id = address; + searchkey.obj_type = TYPE_SHARED_DATA_REF; + searchkey.offset = 0; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return 0; + } + + do { + traverse_ptr next_tp; + + b = find_next_item(Vcb, &tp, &next_tp, FALSE); + + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + if (tp.item->size >= sizeof(SHARED_DATA_REF)) { + SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)tp.item->data; + LIST_ENTRY* le; + BOOL found = FALSE; + + le = Vcb->shared_extents.Flink; + while (le != &Vcb->shared_extents) { + shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry); + + if (sd->address == sdr->offset) { + LIST_ENTRY* le2 = sd->entries.Flink; + while (le2 != &sd->entries) { + shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry); + + if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset) + return sde->edr.count; + + le2 = le2->Flink; + } + found = TRUE; + break; + } + + le = le->Flink; + } + + if (!found) + WARN("shared data extents not loaded for tree at %llx\n", sdr->offset); + } else { + ERR("(%llx,%x,%llx) was %x bytes, not %x as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, + tp.item->key.offset, tp.item->size, sizeof(SHARED_DATA_REF)); + } + } + + if (b) { + tp = next_tp; + + 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)) + break; + } + } while (b); + } + + return 0; +} diff --git a/reactos/drivers/filesystems/btrfs/fastio.c b/reactos/drivers/filesystems/btrfs/fastio.c index f895fedc1c1..682489a3806 100644 --- a/reactos/drivers/filesystems/btrfs/fastio.c +++ b/reactos/drivers/filesystems/btrfs/fastio.c @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public Licence * along with WinBtrfs. If not, see . */ -#include #include "btrfs_drv.h" FAST_IO_DISPATCH FastIoDispatch; @@ -28,20 +27,128 @@ static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) { TRACE("STUB: release_file_for_create_section\n"); } -static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION buf, - PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { +static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION fbi, + PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { + fcb* fcb; + ccb* ccb; - TRACE("STUB: fast_query_basic_info\n"); + TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fbi, IoStatus, DeviceObject); - return FALSE; + if (!FileObject) + return FALSE; + + fcb = FileObject->FsContext; + + if (!fcb) + return FALSE; + + ccb = FileObject->FsContext2; + + if (!ccb) + return FALSE; + + if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) + return FALSE; + + if (fcb->ads) { + if (!ccb || !ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb) + return FALSE; + + fcb = ccb->fileref->parent->fcb; + } + + FsRtlEnterFileSystem(); + + if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { + FsRtlExitFileSystem(); + return FALSE; + } + + 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->FileAttributes = fcb->atts; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_BASIC_INFORMATION); + + ExReleaseResourceLite(fcb->Header.Resource); + + FsRtlExitFileSystem(); + + return TRUE; } -static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION buf, - PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { +static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION fsi, + PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { + fcb* fcb; + ccb* ccb; + BOOL ads; + ULONG adssize; - TRACE("STUB: fast_query_standard_info\n"); + TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fsi, IoStatus, DeviceObject); - return FALSE; + if (!FileObject) + return FALSE; + + fcb = FileObject->FsContext; + ccb = FileObject->FsContext2; + + if (!fcb) + return FALSE; + + FsRtlEnterFileSystem(); + + if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { + FsRtlExitFileSystem(); + return FALSE; + } + + ads = fcb->ads; + + if (ads) { + struct _fcb* fcb2; + + if (!ccb || !ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb) { + ExReleaseResourceLite(fcb->Header.Resource); + FsRtlExitFileSystem(); + return FALSE; + } + + adssize = fcb->adsdata.Length; + + fcb2 = ccb->fileref->parent->fcb; + + ExReleaseResourceLite(fcb->Header.Resource); + + fcb = fcb2; + + if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { + FsRtlExitFileSystem(); + return FALSE; + } + + fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = adssize; + fsi->NumberOfLinks = fcb->inode_item.st_nlink; + fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); + } else { + fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size); + fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; + fsi->NumberOfLinks = fcb->inode_item.st_nlink; + fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); + } + + fsi->DeletePending = ccb->fileref ? ccb->fileref->delete_on_close : FALSE; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION); + + ExReleaseResourceLite(fcb->Header.Resource); + + FsRtlExitFileSystem(); + + return TRUE; } static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, PDEVICE_OBJECT DeviceObject) { diff --git a/reactos/drivers/filesystems/btrfs/fileinfo.c b/reactos/drivers/filesystems/btrfs/fileinfo.c index 02221d41d3e..4d0e323a830 100644 --- a/reactos/drivers/filesystems/btrfs/fileinfo.c +++ b/reactos/drivers/filesystems/btrfs/fileinfo.c @@ -17,15 +17,21 @@ #include "btrfs_drv.h" -static NTSTATUS STDCALL move_subvol(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, - UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback); +#if (NTDDI_VERSION >= NTDDI_WIN10) +// not currently in mingw - introduced with Windows 10 +#ifndef FileIdInformation +#define FileIdInformation (enum _FILE_INFORMATION_CLASS)59 +#endif +#endif -static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) { +static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us); + +static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; fcb* fcb = FileObject->FsContext; ccb* ccb = FileObject->FsContext2; file_ref* fileref = ccb ? ccb->fileref : NULL; - ULONG defda; + ULONG defda, filter = 0; BOOL inode_item_changed = FALSE; NTSTATUS Status; @@ -34,15 +40,18 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P fcb = fileref->parent->fcb; else { ERR("stream did not have fileref\n"); - return STATUS_INSUFFICIENT_RESOURCES; + return STATUS_INTERNAL_ERROR; } } TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) { WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n"); - return STATUS_INVALID_PARAMETER; + Status = STATUS_INVALID_PARAMETER; + goto end; } // FIXME - what if FCB is volume or root? @@ -63,20 +72,10 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P else if (fcb->type == BTRFS_TYPE_SYMLINK) fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; - // create new xattr - if (defda != fbi->FileAttributes) { - char val[64]; - - TRACE("inserting new DOSATTRIB xattr\n"); - sprintf(val, "0x%lx", fbi->FileAttributes); + fcb->atts_changed = TRUE; - Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - return Status; - } - } else - delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback); + if (defda == fbi->FileAttributes) + fcb->atts_deleted = TRUE; fcb->atts = fbi->FileAttributes; @@ -88,6 +87,8 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P fcb->subvol->root_item.ctime = now; inode_item_changed = TRUE; + + filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; } // FIXME - CreationTime @@ -96,44 +97,21 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P // FIXME - ChangeTime if (inode_item_changed) { - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* ii; - fcb->inode_item.transid = Vcb->superblock.generation; fcb->inode_item.sequence++; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, fcb->subvol, &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) - delete_tree_item(Vcb, &tp, rollback); - else - WARN("couldn't find old INODE_ITEM\n"); - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("error - failed to insert item\n"); - ExFreePool(ii); - return STATUS_INTERNAL_ERROR; - } + mark_fcb_dirty(fcb); } - return STATUS_SUCCESS; + if (filter != 0) + send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED); + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(fcb->Header.Resource); + + return Status; } static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { @@ -142,10 +120,13 @@ static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP ccb* ccb = FileObject->FsContext2; file_ref* fileref = ccb ? ccb->fileref : NULL; ULONG atts; + NTSTATUS Status; if (!fileref) return STATUS_INVALID_PARAMETER; + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb); if (fcb->ads) { @@ -153,30 +134,41 @@ static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP atts = fileref->parent->fcb->atts; else { ERR("no fileref for stream\n"); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + goto end; } } else atts = fcb->atts; TRACE("atts = %x\n", atts); - if (atts & FILE_ATTRIBUTE_READONLY) - return STATUS_CANNOT_DELETE; + if (atts & FILE_ATTRIBUTE_READONLY) { + Status = STATUS_CANNOT_DELETE; + goto end; + } // FIXME - can we skip this bit for subvols? - if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) - return STATUS_DIRECTORY_NOT_EMPTY; + if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) { + Status = STATUS_DIRECTORY_NOT_EMPTY; + goto end; + } if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) { WARN("trying to delete file which is being mapped as an image\n"); - return STATUS_CANNOT_DELETE; + Status = STATUS_CANNOT_DELETE; + goto end; } ccb->fileref->delete_on_close = fdi->DeleteFile; FileObject->DeletePending = fdi->DeleteFile; - return STATUS_SUCCESS; + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(fcb->Header.Resource); + + return Status; } static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) { @@ -318,1004 +310,6 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 return STATUS_SUCCESS; } -static NTSTATUS get_fileref_from_dir_item(device_extension* Vcb, file_ref** pfr, file_ref* parent, root* subvol, DIR_ITEM* di) { - LIST_ENTRY* le; - file_ref* fileref; - fcb* sf2; - KEY searchkey; - traverse_ptr tp; - NTSTATUS Status; - - le = parent->children.Flink; - - while (le != &parent->children) { - file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry); - - if (c->fcb->inode == di->key.obj_id && c->fcb->subvol == subvol) { -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc = InterlockedIncrement(&c->refcount); - WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c)); -#else - InterlockedIncrement(&c->refcount); -#endif - *pfr = c; - return STATUS_SUCCESS; - } - - le = le->Flink; - } - - fileref = create_fileref(); - if (!fileref) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - sf2 = create_fcb(); - if (!sf2) { - ERR("out of memory\n"); - free_fileref(fileref); - return STATUS_INSUFFICIENT_RESOURCES; - } - - fileref->fcb = sf2; - sf2->Vcb = Vcb; - - fileref->utf8.Length = fileref->utf8.MaximumLength = di->n; - fileref->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG); - if (!fileref->utf8.Buffer) { - ERR("out of memory\n"); - free_fileref(fileref); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(fileref->utf8.Buffer, di->name, di->n); - - parent->refcount++; -#ifdef DEBUG_FCB_REFCOUNTS - WARN("fileref %p: refcount now %i (%S)\n", parent, parent->refcount, file_desc_fileref(parent)); -#endif - - if (di->key.obj_type == TYPE_ROOT_ITEM) { - root* fcbroot = NULL; - - le = Vcb->roots.Flink; - while (le != &Vcb->roots) { - root* r = CONTAINING_RECORD(le, root, list_entry); - - if (r->id == di->key.obj_id) { - fcbroot = r; - break; - } - - le = le->Flink; - } - - sf2->subvol = fcbroot; - sf2->inode = SUBVOL_ROOT_INODE; - } else { - sf2->subvol = subvol; - sf2->inode = di->key.obj_id; - } - - sf2->type = di->type; - - fileref->name_offset = parent->full_filename.Length / sizeof(WCHAR); - - if (parent != Vcb->root_fileref) - fileref->name_offset++; - - InsertTailList(&parent->children, &fileref->list_entry); - - searchkey.obj_id = sf2->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, sf2->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - free_fileref(fileref); - return Status; - } - - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id); - free_fileref(fileref); - return STATUS_INTERNAL_ERROR; - } - - if (tp.item->size > 0) - RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size)); - - // This is just a quick function for the sake of move_across_subvols. As such, we don't bother - // with sf2->atts, sf2->sd, or sf2->full_filename. - - fileref->parent = (struct _file_ref*)parent; - InsertTailList(&parent->children, &fileref->list_entry); - - *pfr = fileref; - - return STATUS_SUCCESS; -} - -// static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) { -// LONG rc = 0; -// LIST_ENTRY* le = Vcb->trees.Flink; -// -// while (le != &Vcb->trees) { -// tree* t = CONTAINING_RECORD(le, tree, list_entry); -// -// rc += t->refcount; -// -// le = le->Flink; -// } -// -// le = tc->Flink; -// while (le != tc) { -// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry); -// tree* t; -// -// rc--; -// -// t = tc2->tree->parent; -// while (t) { -// rc--; -// t = t->parent; -// } -// -// le = le->Flink; -// } -// -// return rc; -// } - -static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, UINT64 inode, - UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) { - UINT64 oldindex, index; - UINT32 oldcrc32; - INODE_ITEM* ii; - BOOL has_hardlink = FALSE; - DIR_ITEM* di; - KEY searchkey; - traverse_ptr tp, next_tp; - NTSTATUS Status; - BOOL b; - - // move INODE_ITEM - - fileref->fcb->inode_item.transid = Vcb->superblock.generation; - fileref->fcb->inode_item.sequence++; - fileref->fcb->inode_item.st_ctime = *now; - - searchkey.obj_id = fileref->fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - delete_tree_item(Vcb, &tp, rollback); - - if (fileref->fcb->inode_item.st_nlink > 1) { - fileref->fcb->inode_item.st_nlink--; - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &fileref->fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - has_hardlink = TRUE; - } - } else { - WARN("couldn't find old INODE_ITEM\n"); - } - - fileref->fcb->inode_item.st_nlink = 1; - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &fileref->fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, (ULONG)fileref->utf8.Length); - - // delete old DIR_ITEM - - Status = delete_dir_item(Vcb, fileref->fcb->subvol, oldparinode, oldcrc32, &fileref->utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); - return Status; - } - - // create new DIR_ITEM - - di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8->Length; - di->type = fileref->fcb->type; - RtlCopyMemory(di->name, utf8->Buffer, utf8->Length); - - Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); - return Status; - } - - Status = delete_inode_ref(Vcb, fileref->fcb->subvol, fileref->fcb->inode, oldparinode, &fileref->utf8, &oldindex, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_inode_ref returned %08x\n", Status); - return Status; - } - - // delete DIR_INDEX - - if (oldindex == 0) { - WARN("couldn't find old INODE_REF\n"); - } else { - searchkey.obj_id = oldparinode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = oldindex; - - Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) - delete_tree_item(Vcb, &tp, rollback); - else - WARN("couldn't find old DIR_INDEX\n"); - } - - // get new index - - searchkey.obj_id = destinode; - searchkey.obj_type = TYPE_DIR_INDEX + 1; - searchkey.offset = 0; - - Status = find_item(Vcb, destsubvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) { - tp = next_tp; - - TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } - } - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) { - index = tp.item->key.offset + 1; - } else - index = 2; - - // create INODE_REF - - Status = add_inode_ref(Vcb, destsubvol, inode, destinode, index, utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_inode_ref returned %08x\n", Status); - return Status; - } - - // create DIR_INDEX - - di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8->Length; - di->type = fileref->fcb->type; - RtlCopyMemory(di->name, utf8->Buffer, utf8->Length); - - if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - // move XATTR_ITEMs - - searchkey.obj_id = fileref->fcb->inode; - searchkey.obj_type = TYPE_XATTR_ITEM; - searchkey.offset = 0; - - Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->size > 0) { - di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(di, tp.item->data, tp.item->size); - - if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_XATTR_ITEM, tp.item->key.offset, di, tp.item->size, NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - if (!has_hardlink) - delete_tree_item(Vcb, &tp, rollback); - } - - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM) - break; - } - } while (b); - - // do extents - - searchkey.obj_id = fileref->fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = 0; - - Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) { - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - } else { - EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!ed) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ed, tp.item->data, tp.item->size); - - // FIXME - update ed's generation - - if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ed, tp.item->size, NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - - if (ed2->address != 0) { - Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset - ed2->offset, 1, rollback); - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - return Status; - } - - if (!has_hardlink) { - Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, fileref->fcb->subvol, fileref->fcb->inode, - tp.item->key.offset - ed2->offset, 1, NULL, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("decrease_extent_refcount_data returned %08x\n", Status); - return Status; - } - } - } - } - - if (!has_hardlink) - delete_tree_item(Vcb, &tp, rollback); - } - } - - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA) - break; - } - } while (b); - - return STATUS_SUCCESS; -} - -typedef struct { - file_ref* fileref; - UINT8 level; - UINT32 crc32; - UINT64 newinode; - UINT64 newparinode; - BOOL subvol; - ANSI_STRING utf8; - LIST_ENTRY list_entry; -} dir_list; - -static NTSTATUS add_to_dir_list(file_ref* fileref, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) { - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b; - NTSTATUS Status; - - *empty = TRUE; - - searchkey.obj_id = fileref->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = 2; - - Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) { - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - } else { - DIR_ITEM* di = (DIR_ITEM*)tp.item->data; - file_ref* child; - dir_list* dl2; - - if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n + di->m) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } else { - if (di->key.obj_type == TYPE_INODE_ITEM || di->key.obj_type == TYPE_ROOT_ITEM) { - if (di->key.obj_type == TYPE_ROOT_ITEM) - TRACE("moving subvol %llx\n", di->key.obj_id); - else - TRACE("moving inode %llx\n", di->key.obj_id); - - *empty = FALSE; - - Status = get_fileref_from_dir_item(fileref->fcb->Vcb, &child, fileref, fileref->fcb->subvol, di); - if (!NT_SUCCESS(Status)) { - ERR("get_fileref_from_dir_item returned %08x\n", Status); - return Status; - } - - dl2 = ExAllocatePoolWithTag(PagedPool, sizeof(dir_list), ALLOC_TAG); - if (!dl2) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - dl2->fileref = child; - dl2->level = level; - dl2->newparinode = newparinode; - dl2->subvol = di->key.obj_type == TYPE_ROOT_ITEM; - - dl2->utf8.Length = dl2->utf8.MaximumLength = di->n; - dl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dl2->utf8.MaximumLength, ALLOC_TAG); - if (!dl2->utf8.Buffer) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(dl2->utf8.Buffer, di->name, dl2->utf8.Length); - dl2->crc32 = calc_crc32c(0xfffffffe, (UINT8*)dl2->utf8.Buffer, (ULONG)dl2->utf8.Length); - - InsertTailList(dl, &dl2->list_entry); - } - } - } - } - - b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type) - break; - } - } while (b); - - return STATUS_SUCCESS; -} - -static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) { - UINT64 inode, oldparinode; - NTSTATUS Status; - LIST_ENTRY dl; - - if (fileref->fcb->inode_item.st_nlink > 1 && fileref->fcb->open_count > 1) { - WARN("not moving hard-linked inode across subvols when open more than once\n"); - // FIXME - don't do this if only one fileref? - return STATUS_ACCESS_DENIED; - } - - if (destsubvol->lastinode == 0) - get_last_inode(Vcb, destsubvol); - - inode = destsubvol->lastinode + 1; - destsubvol->lastinode++; - - oldparinode = fileref->fcb->subvol == fileref->parent->fcb->subvol ? fileref->parent->fcb->inode : SUBVOL_ROOT_INODE; - - Status = move_inode_across_subvols(Vcb, fileref, destsubvol, destinode, inode, oldparinode, utf8, crc32, now, rollback); - if (!NT_SUCCESS(Status)) { - ERR("move_inode_across_subvols returned %08x\n", Status); - return Status; - } - - if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) { - BOOL b, empty; - UINT8 level, max_level; - LIST_ENTRY* le; - - InitializeListHead(&dl); - - add_to_dir_list(fileref, 0, &dl, inode, &b); - - level = 0; - do { - empty = TRUE; - - le = dl.Flink; - while (le != &dl) { - dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry); - - if (dl2->level == level && !dl2->subvol) { - inode++; - destsubvol->lastinode++; - - dl2->newinode = inode; - - if (dl2->fileref->fcb->type == BTRFS_TYPE_DIRECTORY) { - add_to_dir_list(dl2->fileref, level+1, &dl, dl2->newinode, &b); - if (!b) empty = FALSE; - } - } - - le = le->Flink; - } - - if (!empty) level++; - } while (!empty); - - max_level = level; - - for (level = 0; level <= max_level; level++) { - TRACE("level %u\n", level); - - le = dl.Flink; - while (le != &dl) { - dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry); - - if (dl2->level == level) { - if (dl2->subvol) { - TRACE("subvol %llx\n", dl2->fileref->fcb->subvol->id); - - Status = move_subvol(Vcb, dl2->fileref, destsubvol, dl2->newparinode, &dl2->utf8, dl2->crc32, dl2->crc32, now, FALSE, rollback); - if (!NT_SUCCESS(Status)) { - ERR("move_subvol returned %08x\n", Status); - return Status; - } - } else { - TRACE("inode %llx\n", dl2->fileref->fcb->inode); - - Status = move_inode_across_subvols(Vcb, dl2->fileref, destsubvol, dl2->newparinode, dl2->newinode, dl2->fileref->parent->fcb->inode, &dl2->utf8, dl2->crc32, now, rollback); - if (!NT_SUCCESS(Status)) { - ERR("move_inode_across_subvols returned %08x\n", Status); - return Status; - } - } - } - - le = le->Flink; - } - } - - while (!IsListEmpty(&dl)) { - dir_list* dl2; - - le = RemoveHeadList(&dl); - dl2 = CONTAINING_RECORD(le, dir_list, list_entry); - - ExFreePool(dl2->utf8.Buffer); - free_fileref(dl2->fileref); - - ExFreePool(dl2); - } - } - - fileref->fcb->inode = inode; - fileref->fcb->subvol = destsubvol; - - fileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; - fileref->fcb->subvol->root_item.ctime = *now; - - RemoveEntryList(&fileref->fcb->list_entry); - InsertTailList(&fileref->fcb->subvol->fcbs, &fileref->fcb->list_entry); - - if (fileref->fcb->debug_desc) { - ExFreePool(fileref->fcb->debug_desc); - fileref->fcb->debug_desc = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - NTSTATUS Status; - - searchkey.obj_id = parsubvolid; - searchkey.obj_type = TYPE_ROOT_REF; - searchkey.offset = subvolid; - - 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 (!keycmp(&searchkey, &tp.item->key)) { - if (tp.item->size < sizeof(ROOT_REF)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF)); - } else { - ROOT_REF* rr; - ULONG len; - - rr = (ROOT_REF*)tp.item->data; - len = tp.item->size; - - do { - ULONG itemlen; - - if (len < sizeof(ROOT_REF) || len < sizeof(ROOT_REF) - 1 + rr->n) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n; - - if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) { - ULONG newlen = tp.item->size - itemlen; - - delete_tree_item(Vcb, &tp, rollback); - - if (newlen == 0) { - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - } else { - UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff; - - if (!newrr) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - if ((UINT8*)rr > tp.item->data) { - RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data); - rroff = newrr + ((UINT8*)rr - tp.item->data); - } else { - rroff = newrr; - } - - if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size) - RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data)); - - insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, rollback); - } - - if (index) - *index = rr->index; - - break; - } - - if (len > itemlen) { - len -= itemlen; - rr = (ROOT_REF*)&rr->name[rr->n]; - } else - break; - } while (len > 0); - } - } else { - WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id); - return STATUS_NOT_FOUND; - } - - return STATUS_SUCCESS; -} - -static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - NTSTATUS Status; - - searchkey.obj_id = parsubvolid; - searchkey.obj_type = TYPE_ROOT_REF; - searchkey.offset = subvolid; - - 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 (!keycmp(&searchkey, &tp.item->key)) { - ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n; - UINT8* rr2; - - rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); - if (!rr2) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - if (tp.item->size > 0) - RtlCopyMemory(rr2, tp.item->data, tp.item->size); - - RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n); - ExFreePool(rr); - - delete_tree_item(Vcb, &tp, rollback); - - if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, 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)) { - ERR("error - failed to insert item\n"); - ExFreePool(rr); - return STATUS_INTERNAL_ERROR; - } - } - - return STATUS_SUCCESS; -} - -NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp; - UINT8* data; - ULONG datalen; - NTSTATUS Status; - - searchkey.obj_id = parsubvolid; - searchkey.obj_type = TYPE_ROOT_REF; - searchkey.offset = subvolid; - - 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 (!keycmp(&tp.item->key, &searchkey) && tp.item->size > 0) { - datalen = tp.item->size; - - data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG); - if (!data) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(data, tp.item->data, datalen); - } else { - datalen = 0; - } - - searchkey.obj_id = subvolid; - searchkey.obj_type = TYPE_ROOT_BACKREF; - searchkey.offset = parsubvolid; - - 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 (!keycmp(&tp.item->key, &searchkey)) - 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)) { - ERR("error - failed to insert item\n"); - ExFreePool(data); - return STATUS_INTERNAL_ERROR; - } - } - - return STATUS_SUCCESS; -} - -static NTSTATUS STDCALL move_subvol(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, - UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback) { - DIR_ITEM* di; - NTSTATUS Status; - KEY searchkey; - traverse_ptr tp; - UINT64 oldindex, index; - ROOT_REF* rr; - - // delete old DIR_ITEM - - Status = delete_dir_item(Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); - return Status; - } - - // create new DIR_ITEM - - di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = fileref->fcb->subvol->id; - di->key.obj_type = TYPE_ROOT_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8->Length; - di->type = fileref->fcb->type; - RtlCopyMemory(di->name, utf8->Buffer, utf8->Length); - - Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); - return Status; - } - - // delete old ROOT_REF - - oldindex = 0; - - Status = delete_root_ref(Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->utf8, &oldindex, rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_root_ref returned %08x\n", Status); - return Status; - } - - TRACE("root index = %llx\n", oldindex); - - // delete old DIR_INDEX - - if (oldindex != 0) { - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = oldindex; - - Status = find_item(Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&searchkey, &tp.item->key)) { - TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - delete_tree_item(Vcb, &tp, rollback); - } else { - WARN("could not find old DIR_INDEX entry\n"); - } - } - - // create new DIR_INDEX - - if (fileref->parent->fcb->subvol == destsubvol && fileref->parent->fcb->inode == destinode) { - index = oldindex; - } else { - index = find_next_dir_index(Vcb, destsubvol, destinode); - } - - di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = fileref->fcb->subvol->id; - di->key.obj_type = TYPE_ROOT_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8->Length; - di->type = fileref->fcb->type; - RtlCopyMemory(di->name, utf8->Buffer, utf8->Length); - - if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) { - ERR("error - failed to insert item\n"); - return STATUS_INTERNAL_ERROR; - } - - // create new ROOT_REF - - rr = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_REF) - 1 + utf8->Length, ALLOC_TAG); - if (!rr) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - rr->dir = destinode; - rr->index = index; - rr->n = utf8->Length; - RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length); - - Status = add_root_ref(Vcb, fileref->fcb->subvol->id, destsubvol->id, rr, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_root_ref returned %08x\n", Status); - return Status; - } - - Status = update_root_backref(Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_root_backref 1 returned %08x\n", Status); - return Status; - } - - if (fileref->parent->fcb->subvol != destsubvol) { - Status = update_root_backref(Vcb, fileref->fcb->subvol->id, destsubvol->id, rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_root_backref 1 returned %08x\n", Status); - return Status; - } - - fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; - fileref->parent->fcb->subvol->root_item.ctime = *now; - } - - destsubvol->root_item.ctransid = Vcb->superblock.generation; - destsubvol->root_item.ctime = *now; - - return STATUS_SUCCESS; -} - BOOL has_open_children(file_ref* fileref) { LIST_ENTRY* le = fileref->children.Flink; @@ -1337,68 +331,1053 @@ BOOL has_open_children(file_ref* fileref) { return FALSE; } -static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists, LIST_ENTRY* rollback) { - FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer; - fcb *fcb = FileObject->FsContext, *tfofcb/*, *oldfcb*/; - file_ref *fileref, *oldfileref = NULL, *related; - ccb* ccb = FileObject->FsContext2; - root* parsubvol; - UINT64 parinode, dirpos; - WCHAR* fn; - UNICODE_STRING fnus; - ULONG fnlen, utf8len, disize; - NTSTATUS Status; - ANSI_STRING utf8; - UINT32 crc32, oldcrc32; - KEY searchkey; - traverse_ptr tp, next_tp; - DIR_ITEM* di; - LARGE_INTEGER time; - BTRFS_TIME now; - BOOL across_directories; - INODE_ITEM* ii; - LONG i; +static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { + fcb* fcb; + LIST_ENTRY* le; - // FIXME - MSDN says we should be able to rename streams here, but I can't get it to work. + // FIXME - we can skip a lot of this if the inode is about to be deleted - // FIXME - don't ignore fri->RootDirectory - TRACE(" tfo = %p\n", tfo); - TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists); - TRACE(" RootDirectory = %p\n", fri->RootDirectory); - TRACE(" FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName); + fcb = create_fcb(); + if (!fcb) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } - if (!ccb->fileref) { - ERR("tried to rename file with no fileref\n"); - Status = STATUS_INVALID_PARAMETER; + fcb->Vcb = oldfcb->Vcb; + + fcb->Header.IsFastIoPossible = fast_io_possible(fcb); + fcb->Header.AllocationSize = oldfcb->Header.AllocationSize; + fcb->Header.FileSize = oldfcb->Header.FileSize; + fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength; + + fcb->type = oldfcb->type; + + if (oldfcb->ads) { + fcb->ads = TRUE; + fcb->adshash = oldfcb->adshash; + + if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) { + fcb->adsxattr.Length = oldfcb->adsxattr.Length; + 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); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length); + fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0; + } + + 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); + } + goto end; } - fileref = ccb->fileref; + RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM)); + + if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) { + fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG); + if (!fcb->sd) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd)); + } + + fcb->atts = oldfcb->atts; + + le = oldfcb->extents.Flink; + while (le != &oldfcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext->ignore) { + extent* ext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + + if (!ext2) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ext2->offset = ext->offset; + ext2->datalen = ext->datalen; + + if (ext2->datalen > 0) { + ext2->data = ExAllocatePoolWithTag(PagedPool, ext2->datalen, ALLOC_TAG); + + if (!ext2->data) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(ext2->data, ext->data, ext2->datalen); + } else + ext2->data = NULL; + + ext2->unique = FALSE; + ext2->ignore = FALSE; + + InsertTailList(&fcb->extents, &ext2->list_entry); + } + + le = le->Flink; + } + + le = oldfcb->hardlinks.Flink; + while (le != &oldfcb->hardlinks) { + hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2; + + hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + + if (!hl2) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + hl2->parent = hl->parent; + hl2->index = hl->index; + + hl2->name.Length = hl2->name.MaximumLength = hl->name.Length; + hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG); + + if (!hl2->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl2); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length); + + hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length; + hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG); + + if (!hl2->utf8.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl2->name.Buffer); + ExFreePool(hl2); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length); + + InsertTailList(&fcb->hardlinks, &hl2->list_entry); + + le = le->Flink; + } + + fcb->last_dir_index = oldfcb->last_dir_index; + + if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) { + fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length; + + fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG); + if (!fcb->reparse_xattr.Buffer) { + ERR("out of memory\n"); + free_fcb(fcb); + 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; + + return STATUS_SUCCESS; +} + +typedef struct _move_entry { + file_ref* fileref; + fcb* dummyfcb; + file_ref* dummyfileref; + struct _move_entry* parent; + LIST_ENTRY list_entry; +} move_entry; + +static NTSTATUS add_children_to_move_list(move_entry* me) { + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; + BOOL b; + LIST_ENTRY* le; + move_entry* me2; + + static char xapref[] = "user."; + ULONG xapreflen = strlen(xapref); + + ExAcquireResourceSharedLite(&me->fileref->nonpaged->children_lock, TRUE); + + le = me->fileref->children.Flink; + while (le != &me->fileref->children) { + file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry); + + if (!fr->deleted) { + me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); + if (!me) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + me2->fileref = fr; + + increase_fileref_refcount(fr); + + me2->dummyfcb = NULL; + me2->dummyfileref = NULL; + me2->parent = me; + + InsertHeadList(&me->list_entry, &me2->list_entry); + } + + le = le->Flink; + } + + searchkey.obj_id = me->fileref->fcb->inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = 0; + + Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; + } + + do { + traverse_ptr next_tp; + + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + DIR_ITEM* xa = (DIR_ITEM*)tp.item->data; + ULONG len; + + if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + len = tp.item->size; + + do { + if (len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + if (xa->n > xapreflen && RtlCompareMemory(xa->name, xapref, xapreflen) == xapreflen && + (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n) + ) { + BOOL found = FALSE; + + le = me->fileref->children.Flink; + + while (le != &me->fileref->children) { + file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry); + + if (fr->fcb->ads && fr->fcb->adshash == tp.item->key.offset && fr->fcb->adsxattr.Length == xa->n && + RtlCompareMemory(fr->fcb->adsxattr.Buffer, xa->name, xa->n) == xa->n) { + found = TRUE; + break; + } + + le = le->Flink; + } + + if (!found) { + fcb* fcb; + file_ref* fr; + ANSI_STRING xattr; + ULONG stringlen; + + xattr.Length = xa->n; + xattr.MaximumLength = xattr.Length + 1; + xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG); + + if (!xattr.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(xattr.Buffer, xa->name, xa->n); + xattr.Buffer[xa->n] = 0; + + 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); + if (!NT_SUCCESS(Status)) { + ERR("open_fcb_stream returned %08x\n", Status); + ExFreePool(xattr.Buffer); + goto end; + } + + fr = create_fileref(); + if (!fr) { + ERR("out of memory\n"); + free_fcb(fcb); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + fr->fcb = fcb; + + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + free_fileref(fr); + goto end; + } + + fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG); + if (!fr->filepart.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + free_fileref(fr); + 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); + free_fileref(fr); + goto end; + } + + fr->filepart.Length = fr->filepart.MaximumLength = stringlen; + + Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + free_fileref(fr); + goto end; + } + + fr->parent = (struct _file_ref*)me->fileref; + increase_fileref_refcount(fr->parent); + + insert_fileref_child(me->fileref, fr, FALSE); + + me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); + if (!me) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + free_fileref(fr); + goto end; + } + + me2->fileref = fr; + me2->dummyfcb = NULL; + me2->dummyfileref = NULL; + me2->parent = me; + + InsertHeadList(&me->list_entry, &me2->list_entry); + } + } + + len -= sizeof(DIR_ITEM) - 1 + xa->m + xa->n; + + if (len > 0) + xa = (DIR_ITEM*)&xa->name[xa->m + xa->n]; + } while (len > 0); + } + + b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE); + if (b) { + tp = next_tp; + + if (next_tp.item->key.obj_id > searchkey.obj_id || (next_tp.item->key.obj_id == searchkey.obj_id && next_tp.item->key.obj_type > searchkey.obj_type)) + break; + } + } while (b); + + if (me->fileref->fcb->type == BTRFS_TYPE_DIRECTORY && me->fileref->fcb->inode_item.st_size != 0) { + searchkey.obj_id = me->fileref->fcb->inode; + searchkey.obj_type = TYPE_DIR_INDEX; + searchkey.offset = 2; + + Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; + } + + do { + traverse_ptr next_tp; + + // FIXME - both lists are ordered; we can make this more efficient + + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + BOOL found = FALSE; + + le = me->fileref->children.Flink; + + while (le != &me->fileref->children) { + file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry); + + if (!fr->fcb->ads) { + if (fr->index == tp.item->key.offset) { + found = TRUE; + break; + } else if (fr->index > tp.item->key.offset) + break; + } + + le = le->Flink; + } + + if (!found) { + DIR_ITEM* di = (DIR_ITEM*)tp.item->data; + + if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) { + ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->m + di->n); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + if (di->n == 0) { + ERR("(%llx,%x,%llx): filename length was 0\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + if (di->key.obj_type == TYPE_INODE_ITEM || di->key.obj_type == TYPE_ROOT_ITEM) { + ANSI_STRING utf8; + fcb* fcb; + file_ref* fr; + ULONG stringlen; + root* subvol; + UINT64 inode; + + utf8.Length = utf8.MaximumLength = di->n; + utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); + if (!utf8.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(utf8.Buffer, di->name, di->n); + + if (di->key.obj_type == TYPE_ROOT_ITEM) { + LIST_ENTRY* le2; + + subvol = NULL; + + le2 = me->fileref->fcb->Vcb->roots.Flink; + while (le2 != &me->fileref->fcb->Vcb->roots) { + root* r2 = CONTAINING_RECORD(le2, root, list_entry); + + if (r2->id == di->key.obj_id) { + subvol = r2; + break; + } + + le2 = le2->Flink; + } + + if (!subvol) { + ERR("could not find subvol %llx\n", di->key.obj_id); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + inode = SUBVOL_ROOT_INODE; + } else { + subvol = me->fileref->fcb->subvol; + inode = di->key.obj_id; + } + + Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb); + + if (!NT_SUCCESS(Status)) { + ERR("open_fcb returned %08x\n", Status); + ExFreePool(utf8.Buffer); + goto end; + } + + fr = create_fileref(); + if (!fr) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(utf8.Buffer); + free_fcb(fcb); + goto end; + } + + fr->fcb = fcb; + fr->utf8 = utf8; + + Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length); + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); + free_fileref(fr); + goto end; + } + + fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG); + if (!fr->filepart.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + free_fileref(fr); + goto end; + } + + Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, utf8.Buffer, utf8.Length); + + if (!NT_SUCCESS(Status)) { + ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); + free_fileref(fr); + goto end; + } + + fr->filepart.Length = fr->filepart.MaximumLength = stringlen; + + Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); + + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + free_fileref(fr); + goto end; + } + + fr->parent = me->fileref; + + fr->index = tp.item->key.offset; + increase_fileref_refcount(me->fileref); + + insert_fileref_child(fr->parent, fr, FALSE); + + if (fr->fcb->type == BTRFS_TYPE_DIRECTORY) + fr->fcb->fileref = fr; + + me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); + if (!me) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + free_fileref(fr); + goto end; + } + + me2->fileref = fr; + me2->dummyfcb = NULL; + me2->dummyfileref = NULL; + me2->parent = me; + + InsertHeadList(&me->list_entry, &me2->list_entry); + } else { + ERR("unrecognized key (%llx,%x,%llx)\n", di->key.obj_id, di->key.obj_type, di->key.offset); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + } + } + + b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE); + if (b) { + tp = next_tp; + + if (next_tp.item->key.obj_id > searchkey.obj_id || (next_tp.item->key.obj_id == searchkey.obj_id && next_tp.item->key.obj_type > searchkey.obj_type)) + break; + } + } while (b); + } + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(&me->fileref->nonpaged->children_lock); + + return Status; +} + +static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, LIST_ENTRY* rollback) { + NTSTATUS Status; + LIST_ENTRY move_list, *le; + move_entry* me; + LARGE_INTEGER time; + BTRFS_TIME now; + file_ref* origparent; + + InitializeListHead(&move_list); + + me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); + + if (!me) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + origparent = fileref->parent; + + me->fileref = fileref; + increase_fileref_refcount(me->fileref); + me->dummyfcb = NULL; + me->dummyfileref = NULL; + me->parent = NULL; + + InsertTailList(&move_list, &me->list_entry); + + le = move_list.Flink; + while (le != &move_list) { + me = CONTAINING_RECORD(le, move_entry, list_entry); + + 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); + + if (!NT_SUCCESS(Status)) { + ERR("add_children_to_move_list returned %08x\n", Status); + goto end; + } + } + + ExReleaseResourceLite(me->fileref->fcb->Header.Resource); + + le = le->Flink; + } + + // loop through list and create new inodes + + le = move_list.Flink; + while (le != &move_list) { + me = CONTAINING_RECORD(le, move_entry, list_entry); + + if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE) { + if (!me->dummyfcb) { + ULONG defda; + + ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE); + + Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb); + if (!NT_SUCCESS(Status)) { + ERR("duplicate_fcb returned %08x\n", Status); + ExReleaseResourceLite(me->fileref->fcb->Header.Resource); + goto end; + } + + me->dummyfcb->subvol = me->fileref->fcb->subvol; + me->dummyfcb->inode = me->fileref->fcb->inode; + + if (!me->dummyfcb->ads) { + me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty; + me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed; + me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted; + me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed; + me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed; + } + + me->dummyfcb->created = me->fileref->fcb->created; + me->dummyfcb->deleted = me->fileref->fcb->deleted; + mark_fcb_dirty(me->dummyfcb); + + if (!me->fileref->fcb->ads) { + LIST_ENTRY* le2; + + if (destdir->fcb->subvol->lastinode == 0) + get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol); + + 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->sd_dirty = !!me->fileref->fcb->sd; + me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts; + me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents); + me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer; + + le2 = me->fileref->fcb->extents.Flink; + while (le2 != &me->fileref->fcb->extents) { + extent* ext = CONTAINING_RECORD(le2, extent, list_entry); + + if (!ext->ignore && ext->datalen >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2) && + (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC)) { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data; + + if (ed2->size != 0) { + chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address); + + if (!c) { + 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); + + if (!NT_SUCCESS(Status)) { + ERR("update_changed_extent_ref returned %08x\n", Status); + ExReleaseResourceLite(me->fileref->fcb->Header.Resource); + goto end; + } + } + + } + } + + le2 = le2->Flink; + } + } else { + me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol; + me->fileref->fcb->inode = me->parent->fileref->fcb->inode; + } + + me->fileref->fcb->created = TRUE; + + InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry); + RemoveEntryList(&me->fileref->fcb->list_entry); + + InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry); + + InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all); + + while (!IsListEmpty(&me->fileref->fcb->hardlinks)) { + LIST_ENTRY* le = RemoveHeadList(&me->fileref->fcb->hardlinks); + hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); + + if (hl->name.Buffer) + ExFreePool(hl->name.Buffer); + + if (hl->utf8.Buffer) + ExFreePool(hl->utf8.Buffer); + + ExFreePool(hl); + } + + mark_fcb_dirty(me->fileref->fcb); + + if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) { + LIST_ENTRY* le2 = le->Flink; + + while (le2 != &move_list) { + move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry); + + if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) { + me2->dummyfcb = me->dummyfcb; + InterlockedIncrement(&me->dummyfcb->refcount); + } + + le2 = le2->Flink; + } + } + + ExReleaseResourceLite(me->fileref->fcb->Header.Resource); + } else { + ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE); + me->fileref->fcb->inode_item.st_nlink++; + ExReleaseResourceLite(me->fileref->fcb->Header.Resource); + } + } + + le = le->Flink; + } KeQuerySystemTime(&time); win_time_to_unix(time, &now); - utf8.Buffer = NULL; + fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->subvol->root_item.ctime = now; - if (!fileref->parent) { - ERR("error - tried to rename file with no parent\n"); - Status = STATUS_ACCESS_DENIED; - goto end; + // loop through list and create new filerefs + + le = move_list.Flink; + while (le != &move_list) { + hardlink* hl; + + me = CONTAINING_RECORD(le, move_entry, list_entry); + + me->dummyfileref = create_fileref(); + if (!me->dummyfileref) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) + me->dummyfileref->fcb = me->fileref->fcb; + else + me->dummyfileref->fcb = me->dummyfcb; + + InterlockedIncrement(&me->dummyfileref->fcb->refcount); + + me->dummyfileref->filepart = me->fileref->filepart; + + if (le == move_list.Flink) // first item + me->fileref->filepart.Length = me->fileref->filepart.MaximumLength = fnus->Length; + else + me->fileref->filepart.MaximumLength = me->fileref->filepart.MaximumLength; + + me->fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->filepart.MaximumLength, ALLOC_TAG); + + if (!me->fileref->filepart.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(me->fileref->filepart.Buffer, le == move_list.Flink ? fnus->Buffer : me->dummyfileref->filepart.Buffer, me->fileref->filepart.Length); + + Status = RtlUpcaseUnicodeString(&me->fileref->filepart_uc, &me->fileref->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + goto end; + } + + me->dummyfileref->utf8 = me->fileref->utf8; + me->dummyfileref->oldutf8 = me->fileref->oldutf8; + + if (le == move_list.Flink) + me->fileref->utf8.Length = me->fileref->utf8.MaximumLength = utf8->Length; + else + me->fileref->utf8.MaximumLength = me->fileref->utf8.Length; + + if (me->fileref->utf8.MaximumLength > 0) { + me->fileref->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->utf8.MaximumLength, ALLOC_TAG); + + if (!me->fileref->utf8.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(me->fileref->utf8.Buffer, le == move_list.Flink ? utf8->Buffer : me->dummyfileref->utf8.Buffer, me->fileref->utf8.Length); + } + + me->dummyfileref->delete_on_close = me->fileref->delete_on_close; + me->dummyfileref->deleted = me->fileref->deleted; + + me->dummyfileref->created = me->fileref->created; + me->fileref->created = TRUE; + + me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent; + increase_fileref_refcount(me->dummyfileref->parent); + + me->dummyfileref->index = me->fileref->index; + + insert_fileref_child(me->dummyfileref->parent, me->dummyfileref, TRUE); + + me->dummyfileref->debug_desc = me->fileref->debug_desc; + + if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY) + me->dummyfileref->fcb->fileref = me->dummyfileref; + + if (!me->parent) { + RemoveEntryList(&me->fileref->list_entry); + + free_fileref(me->fileref->parent); + + me->fileref->parent = destdir; + + increase_fileref_refcount(destdir); + + Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index); + if (!NT_SUCCESS(Status)) { + ERR("fcb_get_last_dir_index returned %08x\n", Status); + goto end; + } + + insert_fileref_child(me->fileref->parent, me->fileref, TRUE); + + TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); + me->fileref->parent->fcb->inode_item.st_size += me->fileref->utf8.Length * 2; + TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); + me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; + me->fileref->parent->fcb->inode_item.sequence++; + me->fileref->parent->fcb->inode_item.st_ctime = now; + me->fileref->parent->fcb->inode_item.st_mtime = now; + mark_fcb_dirty(me->fileref->parent->fcb); + } + + if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) + me->fileref->fcb->subvol->root_item.num_references++; + + if (!me->dummyfileref->fcb->ads) { + Status = delete_fileref(me->dummyfileref, NULL, rollback); + if (!NT_SUCCESS(Status)) { + ERR("delete_fileref returned %08x\n", Status); + goto end; + } + } + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + hl->parent = me->fileref->parent->fcb->inode; + hl->index = me->fileref->index; + + hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->utf8.Length; + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); + if (!hl->utf8.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(hl); + goto end; + } + + RtlCopyMemory(hl->utf8.Buffer, me->fileref->utf8.Buffer, me->fileref->utf8.Length); + + hl->name.Length = hl->name.MaximumLength = me->fileref->filepart.Length; + hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); + if (!hl->name.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(hl->utf8.Buffer); + ExFreePool(hl); + goto end; + } + + RtlCopyMemory(hl->name.Buffer, me->fileref->filepart.Buffer, me->fileref->filepart.Length); + + InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry); + + mark_fileref_dirty(me->fileref); + + le = le->Flink; } + // loop through, and only mark streams as deleted if their parent inodes are also deleted + + le = move_list.Flink; + while (le != &move_list) { + 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); + if (!NT_SUCCESS(Status)) { + ERR("delete_fileref returned %08x\n", Status); + goto end; + } + } + + le = le->Flink; + } + + destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation; + destdir->fcb->subvol->root_item.ctime = now; + + me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry); + send_notification_fileref(me->dummyfileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); + send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); + send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + + Status = STATUS_SUCCESS; + +end: + while (!IsListEmpty(&move_list)) { + le = RemoveHeadList(&move_list); + me = CONTAINING_RECORD(le, move_entry, list_entry); + + if (me->dummyfcb) + free_fcb(me->dummyfcb); + + if (me->dummyfileref) + free_fileref(me->dummyfileref); + + free_fileref(me->fileref); + ExFreePool(me); + } + + return Status; +} + +static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) { + FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer; + fcb *fcb = FileObject->FsContext; + ccb* ccb = FileObject->FsContext2; + file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; + UINT64 index; + WCHAR* fn; + ULONG fnlen, utf8len; + UNICODE_STRING fnus; + ANSI_STRING utf8; + NTSTATUS Status; + LARGE_INTEGER time; + BTRFS_TIME now; + LIST_ENTRY rollback, *le; + hardlink* hl; + + InitializeListHead(&rollback); + + // FIXME - check fri length + // FIXME - don't ignore fri->RootDirectory + + TRACE("tfo = %p\n", tfo); + TRACE("ReplaceIfExists = %u\n", ReplaceIfExists); + TRACE("RootDirectory = %p\n", fri->RootDirectory); + TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName); + fn = fri->FileName; fnlen = fri->FileNameLength / sizeof(WCHAR); if (!tfo) { - parsubvol = fileref->parent->fcb->subvol; - parinode = fileref->parent->fcb->inode; - tfofcb = NULL; - - across_directories = FALSE; + if (!fileref || !fileref->parent) { + ERR("no fileref set and no directory given\n"); + return STATUS_INVALID_PARAMETER; + } } else { - tfofcb = tfo->FsContext; - parsubvol = tfofcb->subvol; - parinode = tfofcb->inode; + LONG i; for (i = fnlen - 1; i >= 0; i--) { if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') { @@ -1407,8 +1386,15 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, break; } } - - across_directories = parsubvol != fileref->parent->fcb->subvol || parinode != fileref->parent->fcb->inode; + } + + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + if (fcb->ads) { + FIXME("FIXME - renaming streams\n"); // FIXME + Status = STATUS_NOT_IMPLEMENTED; + goto end; } fnus.Buffer = fn; @@ -1416,9 +1402,6 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer); - if (!is_file_name_valid(&fnus)) - return STATUS_OBJECT_NAME_INVALID; - Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); if (!NT_SUCCESS(Status)) goto end; @@ -1435,31 +1418,23 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, if (!NT_SUCCESS(Status)) goto end; - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length); - - // FIXME - set to crc32 if utf8 and oldutf8 are identical - oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, (ULONG)fileref->utf8.Length); - -// TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32); - if (tfo && tfo->FsContext2) { struct _ccb* relatedccb = tfo->FsContext2; related = relatedccb->fileref; - } else - related = NULL; + increase_fileref_refcount(related); + } -// Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE, NULL); Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL); if (NT_SUCCESS(Status)) { - WARN("destination file %S already exists\n", file_desc_fileref(oldfileref)); + TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref)); - if (fileref != oldfileref && !(oldfileref->fcb->open_count == 0 && oldfileref->deleted)) { + if (fileref != oldfileref && !oldfileref->deleted) { if (!ReplaceIfExists) { Status = STATUS_OBJECT_NAME_COLLISION; goto end; - } else if (oldfileref->fcb->open_count >= 1 && !oldfileref->deleted) { + } else if ((oldfileref->fcb->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) { WARN("trying to overwrite open file\n"); Status = STATUS_ACCESS_DENIED; goto end; @@ -1471,6 +1446,20 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, goto end; } } + + if (fileref == oldfileref || !oldfileref->deleted) { + free_fileref(oldfileref); + oldfileref = NULL; + } + } + + if (!related) { + Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL); + + if (!NT_SUCCESS(Status)) { + ERR("open_fileref returned %08x\n", Status); + goto end; + } } if (has_open_children(fileref)) { @@ -1480,456 +1469,376 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, } if (oldfileref) { - // FIXME - check we have permission to delete oldfileref - Status = delete_fileref(oldfileref, NULL, rollback); + // FIXME - check we have permissions for this + Status = delete_fileref(oldfileref, NULL, &rollback); if (!NT_SUCCESS(Status)) { ERR("delete_fileref returned %08x\n", Status); goto end; } } - if (fcb->inode == SUBVOL_ROOT_INODE) { - Status = move_subvol(Vcb, fileref, tfofcb->subvol, tfofcb->inode, &utf8, crc32, oldcrc32, &now, ReplaceIfExists, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("move_subvol returned %08x\n", Status); - goto end; - } - } else if (parsubvol != fcb->subvol) { - Status = move_across_subvols(Vcb, fileref, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now, rollback); - + if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) { + Status = move_across_subvols(fileref, related, &utf8, &fnus, &rollback); if (!NT_SUCCESS(Status)) { ERR("move_across_subvols returned %08x\n", Status); + } + goto end; + } + + if (related == fileref->parent) { // keeping file in same directory + UNICODE_STRING fnus2, oldfn, newfn; + USHORT name_offset; + ULONG oldutf8len; + + fnus2.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); + if (!fnus2.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - } else { - UINT64 oldindex; - INODE_ITEM* ii; - // delete old DIR_ITEM entry - - Status = delete_dir_item(Vcb, fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->utf8, rollback); + Status = fileref_get_filename(fileref, &oldfn, &name_offset); if (!NT_SUCCESS(Status)) { - ERR("delete_dir_item returned %08x\n", Status); - return Status; + ERR("fileref_get_filename returned %08x\n", Status); + goto end; } - // FIXME - make sure fcb's filepart matches the case on disk + fnus2.Length = fnus2.MaximumLength = fnus.Length; + RtlCopyMemory(fnus2.Buffer, fnus.Buffer, fnus.Length); - // create new DIR_ITEM entry + oldutf8len = fileref->utf8.Length; - di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8.Length, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } + if (!fileref->created && !fileref->oldutf8.Buffer) + fileref->oldutf8 = fileref->utf8; + else + ExFreePool(fileref->utf8.Buffer); - di->key.obj_id = fcb->inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8.Length; - di->type = fcb->type; - RtlCopyMemory(di->name, utf8.Buffer, utf8.Length); + TRACE("renaming %.*S to %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer, fnus2.Length / sizeof(WCHAR), fnus.Buffer); - Status = add_dir_item(Vcb, parsubvol, parinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8.Length, rollback); + fileref->utf8 = utf8; + fileref->filepart = fnus2; + + Status = fileref_get_filename(fileref, &newfn, &name_offset); if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); - return Status; + ERR("fileref_get_filename returned %08x\n", Status); + ExFreePool(oldfn.Buffer); + goto end; } - oldindex = 0; + if (fileref->filepart_uc.Buffer) + ExFreePool(fileref->filepart_uc.Buffer); - Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fileref->parent->fcb->inode, &fileref->utf8, &oldindex, rollback); + Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); if (!NT_SUCCESS(Status)) { - ERR("delete_inode_ref returned %08x\n", Status); - return Status; - } - - // delete old DIR_INDEX entry - - if (oldindex != 0) { - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = oldindex; - - Status = find_item(Vcb, fileref->parent->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)) - delete_tree_item(Vcb, &tp, rollback); - else { - WARN("couldn't find DIR_INDEX\n"); - } - } else { - WARN("couldn't get index from INODE_REF\n"); + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + ExFreePool(oldfn.Buffer); + ExFreePool(newfn.Buffer); + goto end; } - // create new DIR_INDEX entry + mark_fileref_dirty(fileref); - if (parsubvol != fileref->parent->fcb->subvol || parinode != fileref->parent->fcb->inode) { - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_INDEX + 1; - searchkey.offset = 0; - - Status = find_item(Vcb, parsubvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - dirpos = 2; - - do { - TRACE("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) { - dirpos = tp.item->key.offset + 1; - break; - } - - if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) { - tp = next_tp; - } else - break; - } while (tp.item->key.obj_id >= parinode && tp.item->key.obj_type >= TYPE_DIR_INDEX); - } else - dirpos = oldindex; - - disize = (ULONG)(sizeof(DIR_ITEM) - 1 + utf8.Length); - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - di->key.obj_id = fcb->inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = (UINT16)utf8.Length; - di->type = fcb->type; - RtlCopyMemory(di->name, utf8.Buffer, utf8.Length); - - if (!insert_tree_item(Vcb, parsubvol, parinode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback)) - ERR("error - failed to insert item\n"); - - // create new INODE_REF entry - - Status = add_inode_ref(Vcb, parsubvol, fcb->inode, parinode, dirpos, &utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_inode_ref returned %08x\n", Status); - return Status; - } + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); fcb->inode_item.transid = Vcb->superblock.generation; fcb->inode_item.sequence++; fcb->inode_item.st_ctime = now; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; + mark_fcb_dirty(fcb); - Status = find_item(Vcb, parsubvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } + // update parent's INODE_ITEM - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) - delete_tree_item(Vcb, &tp, rollback); + related->fcb->inode_item.transid = Vcb->superblock.generation; + TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); + related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len); + TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); + related->fcb->inode_item.sequence++; + related->fcb->inode_item.st_ctime = now; + related->fcb->inode_item.st_mtime = now; - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } + mark_fcb_dirty(related->fcb); + send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); + FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL, + fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL); + FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL, + fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL); - if (!insert_tree_item(Vcb, parsubvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { - WARN("insert_tree_item failed\n"); - } + ExFreePool(oldfn.Buffer); + ExFreePool(newfn.Buffer); + + Status = STATUS_SUCCESS; + goto end; } - // update directory INODE_ITEMs + // We move files by moving the existing fileref to the new directory, and + // replacing it with a dummy fileref with the same original values, but marked as deleted. - fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation; - fileref->parent->fcb->inode_item.sequence++; - fileref->parent->fcb->inode_item.st_ctime = now; - fileref->parent->fcb->inode_item.st_mtime = now; + fr2 = create_fileref(); - TRACE("fileref->parent->fcb->inode_item.st_size was %llx\n", fileref->parent->fcb->inode_item.st_size); - if (!tfofcb || (fileref->parent->fcb->inode == tfofcb->inode && fileref->parent->fcb->subvol == tfofcb->subvol)) { - fileref->parent->fcb->inode_item.st_size += 2 * (utf8.Length - fileref->utf8.Length); - } else { - fileref->parent->fcb->inode_item.st_size -= 2 * fileref->utf8.Length; - TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb->inode_item.st_size); - tfofcb->inode_item.st_size += 2 * utf8.Length; - TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb->inode_item.st_size); - tfofcb->inode_item.transid = Vcb->superblock.generation; - tfofcb->inode_item.sequence++; - tfofcb->inode_item.st_ctime = now; - tfofcb->inode_item.st_mtime = now; - } - TRACE("fileref->parent->fcb->inode_item.st_size now %llx\n", fileref->parent->fcb->inode_item.st_size); + fr2->fcb = fileref->fcb; + fr2->fcb->refcount++; - if (oldfileref && oldfileref->fcb && oldfileref->parent->fcb != fileref->parent->fcb) { - TRACE("oldfileref->parent->fcb->inode_item.st_size was %llx\n", oldfileref->parent->fcb->inode_item.st_size); - oldfileref->parent->fcb->inode_item.st_size -= 2 * oldfileref->utf8.Length; - TRACE("oldfileref->parent->fcb->inode_item.st_size now %llx\n", oldfileref->parent->fcb->inode_item.st_size); - } + fr2->filepart = fileref->filepart; + fr2->filepart_uc = fileref->filepart_uc; + fr2->utf8 = fileref->utf8; + fr2->oldutf8 = fileref->oldutf8; + fr2->index = fileref->index; + fr2->delete_on_close = fileref->delete_on_close; + fr2->deleted = TRUE; + fr2->created = fileref->created; + fr2->parent = fileref->parent; - searchkey.obj_id = fileref->parent->fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, fileref->parent->fcb->subvol, &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) - delete_tree_item(Vcb, &tp, rollback); - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) - WARN("insert_tree_item failed\n"); - - if (tfofcb && (fileref->parent->fcb->inode != tfofcb->inode || fileref->parent->fcb->subvol != tfofcb->subvol)) { - searchkey.obj_id = tfofcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, tfofcb->subvol, &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) - delete_tree_item(Vcb, &tp, rollback); - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &tfofcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, tfofcb->subvol, tfofcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) - WARN("insert_tree_item failed\n"); - } - - fcb->subvol->root_item.ctransid = Vcb->superblock.generation; - fcb->subvol->root_item.ctime = now; - - // FIXME - handle overwrite by rename here - send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, - across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME); + if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY) + fr2->fcb->fileref = fr2; - // FIXME - change full_filename and name_offset of open children - - if (fnlen != fileref->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fileref->filepart.Buffer, fileref->filepart.Length) != fileref->filepart.Length) { - ExFreePool(fileref->filepart.Buffer); - fileref->filepart.Length = fileref->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR)); - fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG); - - if (!fileref->filepart.Buffer) { - ERR("out of memory\n"); - - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(fileref->filepart.Buffer, fn, fileref->filepart.Length); + Status = fcb_get_last_dir_index(related->fcb, &index); + if (!NT_SUCCESS(Status)) { + ERR("fcb_get_last_dir_index returned %08x\n", Status); + goto end; } - if (related && related != (file_ref*)fileref->parent) { - file_ref* oldpar = (file_ref*)fileref->parent; -#ifdef DEBUG_FCB_REFCOUNTS - LONG rc; -#endif - - RemoveEntryList(&fileref->list_entry); - - fileref->parent = (struct _file_ref*)related; -#ifdef DEBUG_FCB_REFCOUNTS - rc = InterlockedIncrement(&related->refcount); - WARN("fileref %p: refcount now %i (%S)\n", fileref->parent, rc, file_desc_fileref((file_ref*)fileref->parent)); -#else - InterlockedIncrement(&related->refcount); -#endif - - InsertTailList(&related->children, &fileref->list_entry); - - free_fileref(oldpar); - } - - ExFreePool(fileref->utf8.Buffer); - fileref->utf8 = utf8; - utf8.Buffer = NULL; - - // change fileref->full_filename - - fileref->full_filename.MaximumLength = fileref->parent->full_filename.Length + fileref->filepart.Length; - if (fileref->parent->parent) fileref->full_filename.MaximumLength += sizeof(WCHAR); - ExFreePool(fileref->full_filename.Buffer); - - fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.MaximumLength, ALLOC_TAG); - if (!fileref->full_filename.Buffer) { + fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); + if (!fileref->filepart.Buffer) { ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - RtlCopyMemory(fileref->full_filename.Buffer, fileref->parent->full_filename.Buffer, fileref->parent->full_filename.Length); - fileref->full_filename.Length = fileref->parent->full_filename.Length; + fileref->filepart.Length = fileref->filepart.MaximumLength = fnus.Length; + RtlCopyMemory(fileref->filepart.Buffer, fnus.Buffer, fnus.Length); - if (fileref->parent->parent) { - fileref->full_filename.Buffer[fileref->full_filename.Length / sizeof(WCHAR)] = '\\'; - fileref->full_filename.Length += sizeof(WCHAR); - } - fileref->name_offset = fileref->full_filename.Length / sizeof(WCHAR); - - RtlAppendUnicodeStringToString(&fileref->full_filename, &fileref->filepart); - - if (fileref->debug_desc) { - ExFreePool(fileref->debug_desc); - fileref->debug_desc = NULL; + Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + goto end; } - send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, - across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME); + fileref->utf8 = utf8; + fileref->oldutf8.Buffer = NULL; + fileref->index = index; + fileref->deleted = FALSE; + fileref->created = TRUE; + fileref->parent = related; + + ExAcquireResourceExclusiveLite(&fileref->parent->nonpaged->children_lock, TRUE); + InsertHeadList(&fileref->list_entry, &fr2->list_entry); + RemoveEntryList(&fileref->list_entry); + ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock); + insert_fileref_child(related, fileref, TRUE); + + mark_fileref_dirty(fr2); + mark_fileref_dirty(fileref); + + // add new hardlink entry to fcb + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + hl->parent = related->fcb->inode; + hl->index = index; + + hl->name.Length = hl->name.MaximumLength = fileref->filepart.Length; + hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); + + if (!hl->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(hl->name.Buffer, fileref->filepart.Buffer, fileref->filepart.Length); + + hl->utf8.Length = hl->utf8.MaximumLength = fileref->utf8.Length; + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); + + if (!hl->utf8.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl->name.Buffer); + ExFreePool(hl); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlCopyMemory(hl->utf8.Buffer, fileref->utf8.Buffer, fileref->utf8.Length); + + InsertTailList(&fcb->hardlinks, &hl->list_entry); + + // delete old hardlink entry from fcb + + le = fcb->hardlinks.Flink; + while (le != &fcb->hardlinks) { + hl = CONTAINING_RECORD(le, hardlink, list_entry); + + if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->index) { + RemoveEntryList(&hl->list_entry); + + if (hl->utf8.Buffer) + ExFreePool(hl->utf8.Buffer); + + if (hl->name.Buffer) + ExFreePool(hl->name.Buffer); + + ExFreePool(hl); + break; + } + + le = le->Flink; + } + + // update inode's INODE_ITEM + + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + fcb->inode_item.transid = Vcb->superblock.generation; + fcb->inode_item.sequence++; + fcb->inode_item.st_ctime = now; + + mark_fcb_dirty(fcb); + + // update new parent's INODE_ITEM + + related->fcb->inode_item.transid = Vcb->superblock.generation; + TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); + related->fcb->inode_item.st_size += 2 * utf8len; + TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); + related->fcb->inode_item.sequence++; + related->fcb->inode_item.st_ctime = now; + related->fcb->inode_item.st_mtime = now; + + mark_fcb_dirty(related->fcb); + + // update old parent's INODE_ITEM + + fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation; + TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); + fr2->parent->fcb->inode_item.st_size -= 2 * fr2->utf8.Length; + TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); + fr2->parent->fcb->inode_item.sequence++; + fr2->parent->fcb->inode_item.st_ctime = now; + fr2->parent->fcb->inode_item.st_mtime = now; + + free_fileref(fr2); + + mark_fcb_dirty(fr2->parent->fcb); + + send_notification_fileref(fr2, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); + send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); + send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + Status = STATUS_SUCCESS; end: - if (utf8.Buffer) - ExFreePool(utf8.Buffer); - if (oldfileref) free_fileref(oldfileref); + if (!NT_SUCCESS(Status) && related) + free_fileref(related); + + if (!NT_SUCCESS(Status) && fr2) + free_fileref(fr2); + + if (NT_SUCCESS(Status)) + clear_rollback(&rollback); + else + do_rollback(Vcb, &rollback); + + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&Vcb->tree_lock); + return Status; } 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) { LARGE_INTEGER time; BTRFS_TIME now; - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* ii; CC_FILE_SIZES ccfs; - UINT8* data = NULL; - UINT16 datalen; - NTSTATUS Status; - TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adssize); + TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adsdata.Length); if (!fileref || !fileref->parent) { ERR("no fileref for stream\n"); return STATUS_INTERNAL_ERROR; } - if (end < fcb->adssize) { + if (end < fcb->adsdata.Length) { if (advance_only) return STATUS_SUCCESS; TRACE("truncating stream to %llx bytes\n", end); - if (end > 0) { - if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) { - ERR("get_xattr failed\n"); - return STATUS_INTERNAL_ERROR; - } - } - - Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, end, rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - return Status; - } - - fcb->adssize = end; - - if (data) - ExFreePool(data); - } else if (end > fcb->adssize) { - UINT16 maxlen; - UINT8* data2; + 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; +// +// // 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; +// } - 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 (end > fcb->adsdata.MaximumLength) { + char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG); + if (!data) { + ERR("out of memory\n"); + ExFreePool(data); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (fcb->adsdata.Buffer) { + RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length); + ExFreePool(fcb->adsdata.Buffer); + } + + fcb->adsdata.Buffer = data; + fcb->adsdata.MaximumLength = end; } - if (keycmp(&tp.item->key, &searchkey)) { - ERR("error - could not find key for xattr\n"); - return STATUS_INTERNAL_ERROR; - } + RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length); - 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; - } - - data2 = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG); - if (!data2) { - ERR("out of memory\n"); - ExFreePool(data); - return STATUS_INSUFFICIENT_RESOURCES; - } - - if (data) { - RtlCopyMemory(data2, data, datalen); - ExFreePool(data); - } - - RtlZeroMemory(&data2[datalen], end - datalen); - - Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, end, rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - return Status; - } - - fcb->adssize = end; - - ExFreePool(data2); + fcb->adsdata.Length = end; } + + mark_fcb_dirty(fcb); + + fcb->Header.AllocationSize.QuadPart = end; + fcb->Header.FileSize.QuadPart = end; + fcb->Header.ValidDataLength.QuadPart = end; if (FileObject) { ccfs.AllocationSize = fcb->Header.AllocationSize; @@ -1946,53 +1855,44 @@ NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT6 fileref->parent->fcb->inode_item.sequence++; fileref->parent->fcb->inode_item.st_ctime = now; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - 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)) - delete_tree_item(Vcb, &tp, rollback); - else - WARN("couldn't find existing INODE_ITEM\n"); + mark_fcb_dirty(fileref->parent->fcb); - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM)); - insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback); - fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; fileref->parent->fcb->subvol->root_item.ctime = now; return STATUS_SUCCESS; } -static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) { +static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only) { FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer; fcb* fcb = FileObject->FsContext; ccb* ccb = FileObject->FsContext2; file_ref* fileref = ccb ? ccb->fileref : NULL; NTSTATUS Status; LARGE_INTEGER time; - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* ii; CC_FILE_SIZES ccfs; + LIST_ENTRY rollback; - if (fileref ? fileref->deleted : fcb->deleted) - return STATUS_FILE_CLOSED; + if (!fileref) { + ERR("fileref is NULL\n"); + return STATUS_INVALID_PARAMETER; + } - if (fcb->ads) - return stream_set_end_of_file_information(Vcb, feofi->EndOfFile.QuadPart, fcb, fileref, FileObject, advance_only, rollback); + InitializeListHead(&rollback); + + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); + + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + if (fileref ? fileref->deleted : fcb->deleted) { + Status = STATUS_FILE_CLOSED; + goto end; + } + + if (fcb->ads) { + Status = stream_set_end_of_file_information(Vcb, feofi->EndOfFile.QuadPart, fcb, fileref, FileObject, advance_only, &rollback); + goto end; + } TRACE("file: %S\n", file_desc(FileObject)); TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE"); @@ -2006,28 +1906,31 @@ static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP // int3; if (feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) { - if (advance_only) - return STATUS_SUCCESS; + if (advance_only) { + Status = STATUS_SUCCESS; + goto end; + } 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, &rollback); if (!NT_SUCCESS(Status)) { ERR("error - truncate_file failed\n"); - return Status; + goto end; } } else if (feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) { if (Irp->Flags & IRP_PAGING_IO) { TRACE("paging IO tried to extend file size\n"); - return STATUS_SUCCESS; + Status = STATUS_SUCCESS; + goto end; } TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart); - Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, TRUE, rollback); + Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, TRUE, NULL, &rollback); if (!NT_SUCCESS(Status)) { ERR("error - extend_file failed\n"); - return Status; + goto end; } } @@ -2042,31 +1945,22 @@ static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP win_time_to_unix(time, &fcb->inode_item.st_mtime); - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - 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)) - delete_tree_item(Vcb, &tp, rollback); - else - WARN("couldn't find existing INODE_ITEM\n"); + mark_fcb_dirty(fcb); + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } + Status = STATUS_SUCCESS; + +end: + if (NT_SUCCESS(Status)) + clear_rollback(&rollback); + else + do_rollback(Vcb, &rollback); + + ExReleaseResourceLite(fcb->Header.Resource); - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback); + ExReleaseResourceLite(&Vcb->tree_lock); - return STATUS_SUCCESS; + return Status; } // static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { @@ -2092,25 +1986,25 @@ static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp return STATUS_SUCCESS; } -static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, LIST_ENTRY* rollback) { +static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) { FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; - fcb *fcb = FileObject->FsContext, *tfofcb; + fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb; ccb* ccb = FileObject->FsContext2; - file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related; - root* parsubvol; - UINT64 parinode, dirpos; + file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; + UINT64 index; WCHAR* fn; - ULONG fnlen, utf8len, disize; + ULONG fnlen, utf8len; UNICODE_STRING fnus; ANSI_STRING utf8; NTSTATUS Status; - UINT32 crc32; - DIR_ITEM *di, *di2; LARGE_INTEGER time; BTRFS_TIME now; - KEY searchkey; - traverse_ptr tp; - INODE_ITEM *ii, *fcbii; + LIST_ENTRY rollback; + hardlink* hl; + ACCESS_MASK access; + SECURITY_SUBJECT_CONTEXT subjcont; + + InitializeListHead(&rollback); // FIXME - check fli length // FIXME - don't ignore fli->RootDirectory @@ -2129,15 +2023,13 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF return STATUS_INVALID_PARAMETER; } - parsubvol = fileref->parent->fcb->subvol; - parinode = fileref->parent->fcb->inode; + parfcb = fileref->parent->fcb; tfofcb = NULL; } else { LONG i; tfofcb = tfo->FsContext; - parsubvol = tfofcb->subvol; - parinode = tfofcb->inode; + parfcb = tfofcb; for (i = fnlen - 1; i >= 0; i--) { if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') { @@ -2148,7 +2040,8 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF } } - utf8.Buffer = NULL; + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); if (fcb->type == BTRFS_TYPE_DIRECTORY) { WARN("tried to create hard link on directory\n"); @@ -2183,21 +2076,19 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF if (!NT_SUCCESS(Status)) goto end; - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length); - if (tfo && tfo->FsContext2) { struct _ccb* relatedccb = tfo->FsContext2; related = relatedccb->fileref; - } else - related = NULL; + increase_fileref_refcount(related); + } Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL); if (NT_SUCCESS(Status)) { - WARN("destination file %S already exists\n", file_desc_fileref(oldfileref)); + if (!oldfileref->deleted) { + WARN("destination file %S already exists\n", file_desc_fileref(oldfileref)); - if (fileref != oldfileref && !(oldfileref->fcb->open_count == 0 && oldfileref->deleted)) { if (!fli->ReplaceIfExists) { Status = STATUS_OBJECT_NAME_COLLISION; goto end; @@ -2205,6 +2096,9 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF WARN("trying to overwrite open file\n"); Status = STATUS_ACCESS_DENIED; goto end; + } else if (fileref == oldfileref) { + Status = STATUS_ACCESS_DENIED; + goto end; } if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { @@ -2212,86 +2106,130 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF Status = STATUS_ACCESS_DENIED; goto end; } + } else { + free_fileref(oldfileref); + oldfileref = NULL; } } - if (fcb->subvol != parsubvol) { + if (!related) { + Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL); + + if (!NT_SUCCESS(Status)) { + ERR("open_fileref returned %08x\n", Status); + goto end; + } + } + + SeCaptureSubjectContext(&subjcont); + + if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL, + IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { + SeReleaseSubjectContext(&subjcont); + WARN("SeAccessCheck failed, returning %08x\n", Status); + goto end; + } + + SeReleaseSubjectContext(&subjcont); + + if (fcb->subvol != parfcb->subvol) { WARN("can't create hard link over subvolume boundary\n"); Status = STATUS_INVALID_PARAMETER; goto end; } if (oldfileref) { - // FIXME - check we have permissions for this + 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, rollback); + Status = delete_fileref(oldfileref, NULL, &rollback); if (!NT_SUCCESS(Status)) { - ERR("delete_fcb returned %08x\n", Status); + ERR("delete_fileref returned %08x\n", Status); goto end; } } - // add DIR_ITEM + Status = fcb_get_last_dir_index(related->fcb, &index); + if (!NT_SUCCESS(Status)) { + ERR("fcb_get_last_dir_index returned %08x\n", Status); + goto end; + } - disize = sizeof(DIR_ITEM) - 1 + utf8len; + fr2 = create_fileref(); - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { + fr2->fcb = fcb; + fcb->refcount++; + + fr2->utf8 = utf8; + fr2->index = index; + fr2->created = TRUE; + fr2->parent = related; + + fr2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); + if (!fr2->filepart.Buffer) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di2) { + fr2->filepart.Length = fr2->filepart.MaximumLength = fnus.Length; + RtlCopyMemory(fr2->filepart.Buffer, fnus.Buffer, fnus.Length); + + Status = RtlUpcaseUnicodeString(&fr2->filepart_uc, &fr2->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + goto end; + } + + insert_fileref_child(related, fr2, TRUE); + + hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); + if (!hl) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; - ExFreePool(di); goto end; } - di->key.obj_id = fcb->inode; - di->key.obj_type = TYPE_INODE_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8len; - di->type = fcb->type; - RtlCopyMemory(di->name, utf8.Buffer, di->n); - RtlCopyMemory(di2, di, disize); + hl->parent = related->fcb->inode; + hl->index = index; - Status = add_dir_item(Vcb, fcb->subvol, parinode, crc32, di, disize, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); - ExFreePool(di); - ExFreePool(di2); + hl->name.Length = hl->name.MaximumLength = fnus.Length; + hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); + + if (!hl->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - // add DIR_INDEX + RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); - dirpos = find_next_dir_index(Vcb, fcb->subvol, parinode); - if (dirpos == 0) { - ERR("find_next_dir_index failed\n"); - Status = STATUS_INTERNAL_ERROR; - ExFreePool(di2); + hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length; + hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); + + if (!hl->utf8.Buffer) { + ERR("out of memory\n"); + ExFreePool(hl->name.Buffer); + ExFreePool(hl); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - if (!insert_tree_item(Vcb, fcb->subvol, parinode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - ExFreePool(di2); - goto end; - } + RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length); - // add INODE_REF + InsertTailList(&fcb->hardlinks, &hl->list_entry); - Status = add_inode_ref(Vcb, fcb->subvol, fcb->inode, parinode, dirpos, &utf8, rollback); - if (!NT_SUCCESS(Status)) { - ERR("add_inode_ref returned %08x\n", Status); - goto end; - } + mark_fileref_dirty(fr2); + free_fileref(fr2); // update inode's INODE_ITEM @@ -2303,109 +2241,41 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF fcb->inode_item.st_nlink++; fcb->inode_item.st_ctime = now; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - 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_type == searchkey.obj_type) { - delete_tree_item(Vcb, &tp, rollback); - searchkey.offset = tp.item->key.offset; - } else - searchkey.offset = 0; - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - goto end; - } - - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } + mark_fcb_dirty(fcb); // update parent's INODE_ITEM - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0xffffffffffffffff; + parfcb->inode_item.transid = Vcb->superblock.generation; + TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size); + parfcb->inode_item.st_size += 2 * utf8len; + TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size); + parfcb->inode_item.sequence++; + parfcb->inode_item.st_ctime = now; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } + mark_fcb_dirty(parfcb); - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { - delete_tree_item(Vcb, &tp, rollback); - searchkey.offset = tp.item->key.offset; - } else { - ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", parinode, fcb->subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (tfofcb) - fcbii = &tfofcb->inode_item; - 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)); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - fcbii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!fcbii) { - ERR("out of memory\n"); - goto end; - } - - RtlCopyMemory(fcbii, tp.item->data, sizeof(INODE_ITEM)); - } - - fcbii->transid = Vcb->superblock.generation; - fcbii->st_size += 2 * utf8len; - fcbii->sequence++; - fcbii->st_ctime = now; - - if (tfofcb) { - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - goto end; - } - - RtlCopyMemory(ii, fcbii, sizeof(INODE_ITEM)); - } else - ii = fcbii; - - if (!insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - ExFreePool(ii); - goto end; - } - - // FIXME - notification + send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); Status = STATUS_SUCCESS; end: - if (utf8.Buffer) - ExFreePool(utf8.Buffer); - if (oldfileref) free_fileref(oldfileref); + if (!NT_SUCCESS(Status) && related) + free_fileref(related); + + if (!NT_SUCCESS(Status) && fr2) + free_fileref(fr2); + + if (NT_SUCCESS(Status)) + clear_rollback(&rollback); + else + do_rollback(Vcb, &rollback); + + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&Vcb->tree_lock); + return Status; } @@ -2414,20 +2284,35 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); device_extension* Vcb = DeviceObject->DeviceExtension; fcb* fcb = IrpSp->FileObject->FsContext; + ccb* ccb = IrpSp->FileObject->FsContext2; BOOL top_level; - LIST_ENTRY rollback; - - InitializeListHead(&rollback); - + FsRtlEnterFileSystem(); top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + if (Vcb->readonly) { Status = STATUS_MEDIA_WRITE_PROTECTED; goto end; } + if (!fcb) { + ERR("no fcb\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (!ccb) { + ERR("no ccb\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) { Status = STATUS_ACCESS_DENIED; goto end; @@ -2438,44 +2323,92 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp Status = STATUS_NOT_IMPLEMENTED; TRACE("set information\n"); - - acquire_tree_lock(Vcb, TRUE); switch (IrpSp->Parameters.SetFile.FileInformationClass) { case FileAllocationInformation: + { TRACE("FileAllocationInformation\n"); - Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, &rollback); + + if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + break; + } + + Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE); break; + } case FileBasicInformation: + { TRACE("FileBasicInformation\n"); - Status = set_basic_information(Vcb, Irp, IrpSp->FileObject, &rollback); + + if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + break; + } + + Status = set_basic_information(Vcb, Irp, IrpSp->FileObject); + break; + } case FileDispositionInformation: + { TRACE("FileDispositionInformation\n"); + + if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + break; + } + Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject); + break; + } case FileEndOfFileInformation: + { TRACE("FileEndOfFileInformation\n"); - Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, &rollback); + + if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + break; + } + + Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly); + break; + } case FileLinkInformation: TRACE("FileLinkInformation\n"); - Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, &rollback); + Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); break; case FilePositionInformation: + { TRACE("FilePositionInformation\n"); + + if (Irp->RequestorMode == UserMode && + (!(ccb->access & (FILE_READ_DATA | FILE_WRITE_DATA)) || !(ccb->options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + break; + } + Status = set_position_information(Vcb, Irp, IrpSp->FileObject); + break; + } case FileRenameInformation: TRACE("FileRenameInformation\n"); // FIXME - make this work with streams - Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists, &rollback); + Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists); break; case FileValidDataLengthInformation: @@ -2502,21 +2435,12 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass); } - if (NT_SUCCESS(Status)) - Status = consider_write(Vcb); - - if (NT_SUCCESS(Status)) - clear_rollback(&rollback); - else - do_rollback(Vcb, &rollback); - - release_tree_lock(Vcb, TRUE); - end: Irp->IoStatus.Status = Status; - IoCompleteRequest( Irp, IO_NO_INCREMENT ); + IoCompleteRequest(Irp, IO_NO_INCREMENT); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -2575,7 +2499,7 @@ static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_ fnoi->ChangeTime.QuadPart = 0; if (fcb->ads) { - fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adssize; + fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; fnoi->FileAttributes = fileref->parent->fcb->atts; } else { fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size); @@ -2597,7 +2521,7 @@ static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMAT return STATUS_INTERNAL_ERROR; } - fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adssize; + fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length; fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink; fsi->Directory = S_ISDIR(fileref->parent->fcb->inode_item.st_mode); } else { @@ -2685,10 +2609,107 @@ static NTSTATUS STDCALL fill_in_file_alignment_information(FILE_ALIGNMENT_INFORM return STATUS_SUCCESS; } +typedef struct { + file_ref* fileref; + LIST_ENTRY list_entry; +} fileref_list; + +NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset) { + LIST_ENTRY fr_list, *le; + file_ref* fr; + NTSTATUS Status; + ULONG len, i; + + // FIXME - we need a lock on filerefs' filepart + + if (fileref == fileref->fcb->Vcb->root_fileref) { + fn->Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG); + if (!fn->Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + fn->Length = fn->MaximumLength = sizeof(WCHAR); + fn->Buffer[0] = '\\'; + return STATUS_SUCCESS; + } + + InitializeListHead(&fr_list); + + len = 0; + fr = fileref; + + do { + fileref_list* frl; + + frl = ExAllocatePoolWithTag(PagedPool, sizeof(fileref_list), ALLOC_TAG); + if (!frl) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + frl->fileref = fr; + InsertTailList(&fr_list, &frl->list_entry); + + len += fr->filepart.Length; + + if (fr != fileref->fcb->Vcb->root_fileref) + len += sizeof(WCHAR); + + fr = fr->parent; + } while (fr); + + fn->Buffer = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); + if (!fn->Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + fn->Length = fn->MaximumLength = len; + + i = 0; + + le = fr_list.Blink; + while (le != &fr_list) { + fileref_list* frl = CONTAINING_RECORD(le, fileref_list, list_entry); + + if (frl->fileref != fileref->fcb->Vcb->root_fileref) { + fn->Buffer[i] = frl->fileref->fcb->ads ? ':' : '\\'; + i++; + + if (name_offset && frl->fileref == fileref) + *name_offset = i * sizeof(WCHAR); + + RtlCopyMemory(&fn->Buffer[i], frl->fileref->filepart.Buffer, frl->fileref->filepart.Length); + i += frl->fileref->filepart.Length / sizeof(WCHAR); + } + + le = le->Blink; + } + + Status = STATUS_SUCCESS; + +end: + while (!IsListEmpty(&fr_list)) { + fileref_list* frl; + + le = RemoveHeadList(&fr_list); + frl = CONTAINING_RECORD(le, fileref_list, list_entry); + + ExFreePool(frl); + } + + return Status; +} + static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) { #ifdef _DEBUG ULONG retlen = 0; #endif + UNICODE_STRING fn; + NTSTATUS Status; static WCHAR datasuf[] = {':','$','D','A','T','A',0}; ULONG datasuflen = wcslen(datasuf) * sizeof(WCHAR); @@ -2696,7 +2717,7 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni ERR("called without fileref\n"); return STATUS_INVALID_PARAMETER; } - + RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION)); *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]); @@ -2706,15 +2727,21 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni fni->FileName[0] = 0; - if (*length >= (LONG)fileref->full_filename.Length) { - RtlCopyMemory(fni->FileName, fileref->full_filename.Buffer, fileref->full_filename.Length); + Status = fileref_get_filename(fileref, &fn, NULL); + if (!NT_SUCCESS(Status)) { + ERR("fileref_get_filename returned %08x\n", Status); + return Status; + } + + if (*length >= (LONG)fn.Length) { + RtlCopyMemory(fni->FileName, fn.Buffer, fn.Length); #ifdef _DEBUG - retlen = fileref->full_filename.Length; + retlen = fn.Length; #endif - *length -= fileref->full_filename.Length; + *length -= fn.Length; } else { if (*length > 0) { - RtlCopyMemory(fni->FileName, fileref->full_filename.Buffer, *length); + RtlCopyMemory(fni->FileName, fn.Buffer, *length); #ifdef _DEBUG retlen = *length; #endif @@ -2722,18 +2749,18 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni *length = -1; } - fni->FileNameLength = fileref->full_filename.Length; + fni->FileNameLength = fn.Length; if (fcb->ads) { if (*length >= (LONG)datasuflen) { - RtlCopyMemory(&fni->FileName[fileref->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen); + RtlCopyMemory(&fni->FileName[fn.Length / sizeof(WCHAR)], datasuf, datasuflen); #ifdef _DEBUG retlen += datasuflen; #endif *length -= datasuflen; } else { if (*length > 0) { - RtlCopyMemory(&fni->FileName[fileref->full_filename.Length / sizeof(WCHAR)], datasuf, *length); + RtlCopyMemory(&fni->FileName[fn.Length / sizeof(WCHAR)], datasuf, *length); #ifdef _DEBUG retlen += *length; #endif @@ -2742,6 +2769,8 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni } } + ExFreePool(fn.Buffer); + TRACE("%.*S\n", retlen / sizeof(WCHAR), fni->FileName); return STATUS_SUCCESS; @@ -2768,94 +2797,63 @@ static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_IN typedef struct { UNICODE_STRING name; UINT64 size; + BOOL ignore; + LIST_ENTRY list_entry; } stream_info; -static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fcb* fcb, LONG* length) { +static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) { ULONG reqsize; - UINT64 i, num_streams; - stream_info* streams; - FILE_STREAM_INFORMATION* entry; + LIST_ENTRY streamlist, *le; + FILE_STREAM_INFORMATION *entry, *lastentry; NTSTATUS Status; KEY searchkey; traverse_ptr tp, next_tp; BOOL b; + stream_info* si; static WCHAR datasuf[] = {':','$','D','A','T','A',0}; static char xapref[] = "user."; UNICODE_STRING suf; + if (!fileref) { + ERR("fileref was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + InitializeListHead(&streamlist); + + ExAcquireResourceSharedLite(&fileref->fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE); + suf.Buffer = datasuf; suf.Length = suf.MaximumLength = wcslen(datasuf) * sizeof(WCHAR); - num_streams = 1; - - searchkey.obj_id = fcb->inode; + searchkey.obj_id = fileref->fcb->inode; searchkey.obj_type = TYPE_XATTR_ITEM; searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) { - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - } else { - ULONG len = tp.item->size; - DIR_ITEM* xa = (DIR_ITEM*)tp.item->data; - - do { - if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref)) { - if (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n) { - num_streams++; - } - } - - len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m; - xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work - } while (len > 0); - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM) - break; - } - } while (b); - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); - return Status; + goto end; } - streams = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info) * num_streams, ALLOC_TAG); - if (!streams) { + si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG); + if (!si) { ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; } - reqsize = 0; + si->name.Length = si->name.MaximumLength = 0; + si->name.Buffer = NULL; + si->size = fileref->fcb->inode_item.st_size; + si->ignore = FALSE; - streams[0].name.Length = streams[0].name.MaximumLength = 0; - streams[0].name.Buffer = NULL; - streams[0].size = fcb->inode_item.st_size; - reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[0].name.Length; + InsertTailList(&streamlist, &si->list_entry); - i = 1; do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) { + if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) { if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); } else { @@ -2873,52 +2871,43 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n)) { Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref)); if (!NT_SUCCESS(Status)) { - UINT64 j; - ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); - - for (j = i; j < num_streams; j++) - streams[j].name.Buffer = NULL; - goto end; } - streams[i].name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG); - if (!streams[i].name.Buffer) { - UINT64 j; - + si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG); + if (!si) { ERR("out of memory\n"); - - for (j = i+1; j < num_streams; j++) - streams[j].name.Buffer = NULL; - Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } + + si->name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG); + if (!si->name.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(si); + goto end; + } - Status = RtlUTF8ToUnicodeN(streams[i].name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref)); + Status = RtlUTF8ToUnicodeN(si->name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref)); if (!NT_SUCCESS(Status)) { - UINT64 j; - ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); - - ExFreePool(streams[i].name.Buffer); - for (j = i; j < num_streams; j++) - streams[j].name.Buffer = NULL; - + ExFreePool(si->name.Buffer); + ExFreePool(si); goto end; } - streams[i].name.Length = streams[i].name.MaximumLength = stringlen; + si->name.Length = si->name.MaximumLength = stringlen; - streams[i].size = xa->m; - reqsize = sector_align(reqsize, sizeof(LONGLONG)); - reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length; + si->size = xa->m; - TRACE("streams[%llu].name = %.*S (length = %u)\n", i, streams[i].name.Length / sizeof(WCHAR), streams[i].name.Buffer, streams[i].name.Length / sizeof(WCHAR)); - - i++; + si->ignore = FALSE; + + TRACE("stream name = %.*S (length = %u)\n", si->name.Length / sizeof(WCHAR), si->name.Buffer, si->name.Length / sizeof(WCHAR)); + + InsertTailList(&streamlist, &si->list_entry); } len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m; @@ -2927,14 +2916,86 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* } } - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); + b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE); if (b) { tp = next_tp; - if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM) + if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM) break; } - } while (b); + } while (b); + + ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE); + + le = fileref->children.Flink; + while (le != &fileref->children) { + file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry); + + if (fr->fcb && fr->fcb->ads) { + LIST_ENTRY* le2 = streamlist.Flink; + BOOL found = FALSE; + + while (le2 != &streamlist) { + si = CONTAINING_RECORD(le2, stream_info, list_entry); + + if (si && si->name.Buffer && si->name.Length == fr->filepart.Length && + RtlCompareMemory(si->name.Buffer, fr->filepart.Buffer, si->name.Length) == si->name.Length) { + + si->size = fr->fcb->adsdata.Length; + si->ignore = fr->fcb->deleted; + + found = TRUE; + break; + } + + le2 = le2->Flink; + } + + if (!found && !fr->fcb->deleted) { + si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG); + if (!si) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + si->name.Length = si->name.MaximumLength = fr->filepart.Length; + + si->name.Buffer = ExAllocatePoolWithTag(PagedPool, si->name.MaximumLength, ALLOC_TAG); + if (!si->name.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(si); + goto end; + } + + RtlCopyMemory(si->name.Buffer, fr->filepart.Buffer, fr->filepart.Length); + + si->size = fr->fcb->adsdata.Length; + si->ignore = FALSE; + + InsertTailList(&streamlist, &si->list_entry); + } + } + + le = le->Flink; + } + + ExReleaseResourceLite(&fileref->nonpaged->children_lock); + + reqsize = 0; + + le = streamlist.Flink; + while (le != &streamlist) { + si = CONTAINING_RECORD(le, stream_info, list_entry); + + if (!si->ignore) { + reqsize = sector_align(reqsize, sizeof(LONGLONG)); + reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + si->name.Length; + } + + le = le->Flink; + } TRACE("length = %i, reqsize = %u\n", *length, reqsize); @@ -2944,29 +3005,41 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* } entry = fsi; - for (i = 0; i < num_streams; i++) { - entry->StreamNameLength = streams[i].name.Length + suf.Length + sizeof(WCHAR); - entry->StreamSize.QuadPart = streams[i].size; + lastentry = NULL; + + le = streamlist.Flink; + while (le != &streamlist) { + si = CONTAINING_RECORD(le, stream_info, list_entry); - if (i == 0) - entry->StreamAllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size); - else - entry->StreamAllocationSize.QuadPart = streams[i].size; - - entry->StreamName[0] = ':'; - - if (streams[i].name.Length > 0) - RtlCopyMemory(&entry->StreamName[1], streams[i].name.Buffer, streams[i].name.Length); - - RtlCopyMemory(&entry->StreamName[1 + (streams[i].name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length); - - if (i == num_streams - 1) + if (!si->ignore) { + ULONG off; + entry->NextEntryOffset = 0; - else { - entry->NextEntryOffset = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length, sizeof(LONGLONG)); + entry->StreamNameLength = si->name.Length + suf.Length + sizeof(WCHAR); + entry->StreamSize.QuadPart = si->size; + + if (le == streamlist.Flink) + entry->StreamAllocationSize.QuadPart = sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size); + else + entry->StreamAllocationSize.QuadPart = si->size; + + entry->StreamName[0] = ':'; + + if (si->name.Length > 0) + RtlCopyMemory(&entry->StreamName[1], si->name.Buffer, si->name.Length); + + RtlCopyMemory(&entry->StreamName[1 + (si->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length); + + if (lastentry) + lastentry->NextEntryOffset = (UINT8*)entry - (UINT8*)lastentry; + + off = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + si->name.Length, sizeof(LONGLONG)); - entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + entry->NextEntryOffset); + lastentry = entry; + entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off); } + + le = le->Flink; } *length -= reqsize; @@ -2974,12 +3047,18 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* Status = STATUS_SUCCESS; end: - for (i = 0; i < num_streams; i++) { - if (streams[i].name.Buffer) - ExFreePool(streams[i].name.Buffer); + while (!IsListEmpty(&streamlist)) { + le = RemoveHeadList(&streamlist); + si = CONTAINING_RECORD(le, stream_info, list_entry); + + if (si->name.Buffer) + ExFreePool(si->name.Buffer); + + ExFreePool(si); } - ExFreePool(streams); + ExReleaseResourceLite(fileref->fcb->Header.Resource); + ExReleaseResourceLite(&fileref->fcb->Vcb->tree_lock); return Status; } @@ -3007,8 +3086,6 @@ typedef struct { LIST_ENTRY list_entry; } name_bit; -static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us); - static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) { KEY searchkey; traverse_ptr tp; @@ -3282,15 +3359,108 @@ end: return Status; } -#ifndef __REACTOS__ -static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, fcb* fcb, LONG* length) { - KEY searchkey; - traverse_ptr tp, next_tp; +NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr) { NTSTATUS Status; - BOOL b; - LIST_ENTRY hardlinks, *le; - ULONG bytes_needed, num_entries = 0; + fcb* fcb; + hardlink* hl; + file_ref *parfr, *fr; + + Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb); + if (!NT_SUCCESS(Status)) { + ERR("open_fcb returned %08x\n", Status); + return Status; + } + + if (fcb->fileref) { + *pfr = fcb->fileref; + increase_fileref_refcount(fcb->fileref); + return STATUS_SUCCESS; + } + + if (IsListEmpty(&fcb->hardlinks)) { + ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode); + free_fcb(fcb); + return STATUS_INTERNAL_ERROR; + } + + hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry); + + // FIXME - does this work with subvols? + + if (hl->parent == inode) // root of subvol + parfr = NULL; + else { + Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr); + if (!NT_SUCCESS(Status)) { + ERR("open_fileref_by_inode returned %08x\n", Status); + free_fcb(fcb); + return Status; + } + } + + fr = create_fileref(); + if (!fr) { + ERR("out of memory\n"); + free_fcb(fcb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + fr->fcb = fcb; + fcb->fileref = fr; + + fr->index = hl->index; + + fr->utf8.Length = fr->utf8.MaximumLength = hl->utf8.Length; + if (fr->utf8.Length > 0) { + fr->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, fr->utf8.Length, ALLOC_TAG); + + if (!fr->utf8.Buffer) { + ERR("out of memory\n"); + free_fileref(fr); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fr->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length); + } + + fr->filepart.MaximumLength = fr->filepart.Length = hl->name.Length; + + if (fr->filepart.Length > 0) { + fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG); + if (!fr->filepart.Buffer) { + ERR("out of memory\n"); + free_fileref(fr); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(fr->filepart.Buffer, hl->name.Buffer, hl->name.Length); + } + + Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + free_fileref(fr); + return Status; + } + + fr->parent = parfr; + + insert_fileref_child(parfr, fr, TRUE); + + *pfr = fr; + + return STATUS_SUCCESS; +} + +#ifndef __REACTOS__ +static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, LONG* length) { + NTSTATUS Status; + LIST_ENTRY* le; + ULONG bytes_needed; FILE_LINK_ENTRY_INFORMATION* feli; + BOOL overflow = FALSE; + fcb* fcb = fileref->fcb; + ULONG len; if (fcb->ads) return STATUS_INVALID_PARAMETER; @@ -3300,176 +3470,155 @@ static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fl RtlZeroMemory(fli, *length); - InitializeListHead(&hardlinks); - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_REF; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry); - - do { - if (tp.item->key.obj_id == fcb->inode) { - if (tp.item->key.obj_type == TYPE_INODE_REF) { - ULONG len = tp.item->size; - INODE_REF* ir = (INODE_REF*)tp.item->data; - - if (tp.item->size >= sizeof(INODE_REF)) { - UNICODE_STRING dirpath; - - Status = get_inode_dir_path(fcb->Vcb, fcb->subvol, tp.item->key.offset, &dirpath); - if (!NT_SUCCESS(Status)) { - ERR("get_inode_dir_path returned %08x\n", Status); - goto end; - } - - if (fcb->inode == fcb->subvol->root_item.objid) { - name_bit* nb; - - nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG); - if (!nb) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - nb->inode = tp.item->key.offset; - nb->name = dirpath; - - InsertTailList(&hardlinks, &nb->list_entry); - } else { - while (len >= sizeof(INODE_REF) && len >= sizeof(INODE_REF) - 1 + ir->n) { - name_bit* nb; - ULONG namelen; - - Status = RtlUTF8ToUnicodeN(NULL, 0, &namelen, ir->name, ir->n); - if (!NT_SUCCESS(Status)) { - ERR("RtlUTF8ToUnicodeN returned %08x\n", Status); - goto end; - } - - if (namelen == 0) { - ERR("length was 0\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG); - if (!nb) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - nb->inode = tp.item->key.offset; - nb->name.Buffer = NULL; - - InsertTailList(&hardlinks, &nb->list_entry); - - nb->name.Length = nb->name.MaximumLength = namelen + dirpath.Length; - - nb->name.Buffer = ExAllocatePoolWithTag(PagedPool, nb->name.Length, ALLOC_TAG); - if (!nb->name.Buffer) { - ERR("out of memory\n"); - - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(nb->name.Buffer, dirpath.Buffer, dirpath.Length); - - Status = RtlUTF8ToUnicodeN(&nb->name.Buffer[dirpath.Length / sizeof(WCHAR)], namelen, &namelen, ir->name, ir->n); - if (!NT_SUCCESS(Status)) { - ERR("RtlUTF8ToUnicodeN returned %08x\n", Status); - goto end; - } - - ERR("nb->name = %.*S\n", nb->name.Length / sizeof(WCHAR), nb->name.Buffer); - - if (num_entries > 0) - bytes_needed = sector_align(bytes_needed, 8); - - num_entries++; - bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length); - - len -= sizeof(INODE_REF) - 1 + ir->n; - ir = (INODE_REF*)&ir->name[ir->n]; - } - - ExFreePool(dirpath.Buffer); - } - } - } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { - // FIXME - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_INODE_EXTREF) - break; - } - } while (b); - - bytes_needed = sector_align(bytes_needed, 8); - - fli->BytesNeeded = bytes_needed; - fli->EntriesReturned = 0; - - *length -= offsetof(FILE_LINKS_INFORMATION, Entry); + len = bytes_needed; feli = NULL; - le = hardlinks.Flink; - while (le != &hardlinks) { - name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry); + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); + + if (fcb->inode == SUBVOL_ROOT_INODE) { + ULONG namelen; - if (sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length), 8) > *length) { - Status = STATUS_BUFFER_OVERFLOW; - goto end; + if (fcb == fcb->Vcb->root_fileref->fcb) + namelen = sizeof(WCHAR); + else + namelen = fileref->filepart.Length; + + bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen; + + if (bytes_needed > *length) + overflow = TRUE; + + if (!overflow) { + feli = &fli->Entry; + + feli->NextEntryOffset = 0; + feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume + + if (fcb == fcb->Vcb->root_fileref->fcb) { + feli->FileNameLength = 1; + feli->FileName[0] = '.'; + } else { + feli->FileNameLength = fileref->filepart.Length / sizeof(WCHAR); + RtlCopyMemory(feli->FileName, fileref->filepart.Buffer, fileref->filepart.Length); + } + + fli->EntriesReturned++; + + len = bytes_needed; + } + } else { + ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE); + + le = fcb->hardlinks.Flink; + while (le != &fcb->hardlinks) { + hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); + file_ref* parfr; + + 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); + + if (!NT_SUCCESS(Status)) { + ERR("open_fileref_by_inode returned %08x\n", Status); + } else if (!parfr->deleted) { + LIST_ENTRY* le2; + BOOL found = FALSE, deleted = FALSE; + UNICODE_STRING* fn; + + le2 = parfr->children.Flink; + while (le2 != &parfr->children) { + file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); + + if (fr2->index == hl->index) { + found = TRUE; + deleted = fr2->deleted; + + if (!deleted) + fn = &fr2->filepart; + + break; + } + + le2 = le2->Flink; + } + + if (!found) + fn = &hl->name; + + if (!deleted) { + TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found); + + if (feli) + bytes_needed = sector_align(bytes_needed, 8); + + bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR); + + if (bytes_needed > *length) + overflow = TRUE; + + if (!overflow) { + if (feli) { + feli->NextEntryOffset = sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8); + feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset); + } else + feli = &fli->Entry; + + feli->NextEntryOffset = 0; + feli->ParentFileId = parfr->fcb->inode; + feli->FileNameLength = fn->Length / sizeof(WCHAR); + RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length); + + fli->EntriesReturned++; + + len = bytes_needed; + } + } + + free_fileref(parfr); + } + + le = le->Flink; } - if (feli) { - feli->NextEntryOffset = sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8); - feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset); - } else - feli = &fli->Entry; - - feli->NextEntryOffset = 0; - feli->ParentFileId = nb->inode; - feli->FileNameLength = nb->name.Length / sizeof(WCHAR); - RtlCopyMemory(feli->FileName, nb->name.Buffer, nb->name.Length); - - *length -= sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length), 8); - fli->EntriesReturned++; - - le = le->Flink; + ExReleaseResourceLite(&fcb->Vcb->fcb_lock); } - Status = STATUS_SUCCESS; + fli->BytesNeeded = bytes_needed; -end: - while (!IsListEmpty(&hardlinks)) { - name_bit* nb = CONTAINING_RECORD(hardlinks.Flink, name_bit, list_entry); - - if (nb->name.Buffer) - ExFreePool(nb->name.Buffer); - - RemoveEntryList(&nb->list_entry); - - ExFreePool(nb); - } + *length -= len; + + Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; + + ExReleaseResourceLite(fcb->Header.Resource); + return Status; } #endif /* __REACTOS__ */ +#if (NTDDI_VERSION >= NTDDI_WIN10) +#ifdef __MINGW32__ +typedef struct _FILE_ID_128 { + UCHAR Identifier[16]; +} FILE_ID_128, *PFILE_ID_128; + +typedef struct _FILE_ID_INFORMATION { + ULONGLONG VolumeSerialNumber; + FILE_ID_128 FileId; +} FILE_ID_INFORMATION, *PFILE_ID_INFORMATION; +#endif + +static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) { + RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64)); + RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64)); + RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64)); + + *length -= sizeof(FILE_ID_INFORMATION); + + return STATUS_SUCCESS; +} +#endif + static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); LONG length = IrpSp->Parameters.QueryFile.Length; @@ -3484,6 +3633,11 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec if (fcb == Vcb->volume_fcb) return STATUS_INVALID_PARAMETER; + if (!ccb) { + ERR("ccb is NULL\n"); + return STATUS_INVALID_PARAMETER; + } + switch (IrpSp->Parameters.QueryFile.FileInformationClass) { case FileAllInformation: { @@ -3492,6 +3646,12 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileAllInformation\n"); + if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + if (fcb->ads) { if (!fileref || !fileref->parent) { ERR("no fileref for stream\n"); @@ -3541,6 +3701,12 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileAttributeTagInformation\n"); + if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + Status = fill_in_file_attribute_information(ati, fcb, fileref, &length); break; @@ -3553,6 +3719,12 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileBasicInformation\n"); + if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) { WARN("overflow\n"); Status = STATUS_BUFFER_OVERFLOW; @@ -3618,6 +3790,12 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileNetworkOpenInformation\n"); + if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length); break; @@ -3629,6 +3807,12 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FilePositionInformation\n"); + if (!(ccb->access & (FILE_READ_DATA | FILE_WRITE_DATA)) || !(ccb->options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT))) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + Status = fill_in_file_position_information(fpi, FileObject, &length); break; @@ -3657,7 +3841,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileStreamInformation\n"); - Status = fill_in_file_stream_information(fsi, fcb, &length); + Status = fill_in_file_stream_information(fsi, fileref, &length); break; } @@ -3669,7 +3853,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec TRACE("FileHardLinkInformation\n"); - Status = fill_in_hard_link_information(fli, fcb, &length); + Status = fill_in_hard_link_information(fli, fileref, &length); break; } @@ -3685,7 +3869,6 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec break; } #endif - #if (NTDDI_VERSION >= NTDDI_WIN7) case FileStandardLinkInformation: @@ -3704,6 +3887,28 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec Status = STATUS_INVALID_PARAMETER; goto exit; #endif + +#if (NTDDI_VERSION >= NTDDI_WIN10) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + case FileIdInformation: + { + FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; + + if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) { + WARN("overflow\n"); + Status = STATUS_BUFFER_OVERFLOW; + goto exit; + } + + TRACE("FileIdInformation\n"); + + Status = fill_in_file_id_information(fii, fcb, &length); + + break; + } +#pragma GCC diagnostic pop +#endif default: WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass); @@ -3735,13 +3940,18 @@ NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP I top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + Irp->IoStatus.Information = 0; TRACE("query information\n"); IrpSp = IoGetCurrentIrpStackLocation(Irp); - acquire_tree_lock(Vcb, FALSE); + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); fcb = IrpSp->FileObject->FsContext; TRACE("fcb = %p\n", fcb); @@ -3755,8 +3965,9 @@ NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP I IoCompleteRequest( Irp, IO_NO_INCREMENT ); - release_tree_lock(Vcb, FALSE); + ExReleaseResourceLite(&Vcb->tree_lock); +exit: if (top_level) IoSetTopLevelIrp(NULL); diff --git a/reactos/drivers/filesystems/btrfs/flushthread.c b/reactos/drivers/filesystems/btrfs/flushthread.c index 5688ee9c9ec..16795eb1a84 100644 --- a/reactos/drivers/filesystems/btrfs/flushthread.c +++ b/reactos/drivers/filesystems/btrfs/flushthread.c @@ -26,16 +26,16 @@ static void do_flush(device_extension* Vcb) { FsRtlEnterFileSystem(); - acquire_tree_lock(Vcb, TRUE); + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->write_trees > 0) + if (Vcb->need_write) do_write(Vcb, &rollback); free_trees(Vcb); clear_rollback(&rollback); - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); FsRtlExitFileSystem(); } @@ -44,28 +44,30 @@ void STDCALL flush_thread(void* context) { DEVICE_OBJECT* devobj = context; device_extension* Vcb = devobj->DeviceExtension; LARGE_INTEGER due_time; - KTIMER flush_thread_timer; ObReferenceObject(devobj); - KeInitializeTimer(&flush_thread_timer); + KeInitializeTimer(&Vcb->flush_thread_timer); due_time.QuadPart = -INTERVAL * 10000; - KeSetTimer(&flush_thread_timer, due_time, NULL); + KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL); while (TRUE) { - KeWaitForSingleObject(&flush_thread_timer, Executive, KernelMode, FALSE, NULL); + KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL); - if (!(devobj->Vpb->Flags & VPB_MOUNTED)) + if (!(devobj->Vpb->Flags & VPB_MOUNTED) || Vcb->removing) break; do_flush(Vcb); - KeSetTimer(&flush_thread_timer, due_time, NULL); + KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL); } ObDereferenceObject(devobj); - KeCancelTimer(&flush_thread_timer); + KeCancelTimer(&Vcb->flush_thread_timer); + + KeSetEvent(&Vcb->flush_thread_finished, 0, FALSE); + PsTerminateSystemThread(STATUS_SUCCESS); } diff --git a/reactos/drivers/filesystems/btrfs/free-space.c b/reactos/drivers/filesystems/btrfs/free-space.c index 30812e3a585..5f583c194a5 100644 --- a/reactos/drivers/filesystems/btrfs/free-space.c +++ b/reactos/drivers/filesystems/btrfs/free-space.c @@ -21,37 +21,34 @@ // this be a constant number of sectors, a constant 256 KB, or what? #define CACHE_INCREMENTS 64 -static NTSTATUS remove_free_space_inode(device_extension* Vcb, KEY* key, LIST_ENTRY* rollback) { +// #define DEBUG_SPACE_LISTS + +static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* rollback) { NTSTATUS Status; - traverse_ptr tp; - INODE_ITEM* ii; + fcb* fcb; - Status = find_item(Vcb, Vcb->root_root, &tp, key, FALSE); + Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb); if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); + ERR("open_fcb returned %08x\n", Status); return Status; } - if (keycmp(key, &tp.item->key)) { - ERR("could not find (%llx,%x,%llx) in root_root\n", key->obj_id, key->obj_type, key->offset); - return STATUS_NOT_FOUND; + 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); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + return Status; + } } - if (tp.item->size < offsetof(INODE_ITEM, st_blocks)) { - 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, offsetof(INODE_ITEM, st_blocks)); - return STATUS_INTERNAL_ERROR; - } + fcb->deleted = TRUE; - ii = (INODE_ITEM*)tp.item->data; - - Status = excise_extents_inode(Vcb, Vcb->root_root, key->obj_id, NULL, 0, ii->st_size, NULL, rollback); - if (!NT_SUCCESS(Status)) { - ERR("excise_extents returned %08x\n", Status); - return Status; - } - - delete_tree_item(Vcb, &tp, rollback); + flush_fcb(fcb, FALSE, rollback); + free_fcb(fcb); + return STATUS_SUCCESS; } @@ -87,12 +84,26 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) { if (fsi->key.obj_type != TYPE_INODE_ITEM) WARN("key (%llx,%x,%llx) does not point to an INODE_ITEM\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset); else { - Status = remove_free_space_inode(Vcb, &fsi->key, &rollback); + LIST_ENTRY* le; + + Status = remove_free_space_inode(Vcb, fsi->key.obj_id, &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); goto end; } + + le = Vcb->chunks.Flink; + while (le != &Vcb->chunks) { + chunk* c = CONTAINING_RECORD(le, chunk, list_entry); + + if (c->offset == tp.item->key.offset && c->cache) { + free_fcb(c->cache); + c->cache = NULL; + } + + le = le->Flink; + } } } else 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)); @@ -114,7 +125,7 @@ end: return Status; } -static NTSTATUS add_space_entry(chunk* c, UINT64 offset, UINT64 size) { +NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size) { space* s; s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); @@ -124,26 +135,53 @@ static NTSTATUS add_space_entry(chunk* c, UINT64 offset, UINT64 size) { return STATUS_INSUFFICIENT_RESOURCES; } - s->offset = offset; + s->address = offset; s->size = size; - s->type = SPACE_TYPE_FREE; - if (IsListEmpty(&c->space)) - InsertTailList(&c->space, &s->list_entry); + if (IsListEmpty(list)) + InsertTailList(list, &s->list_entry); else { - space* s2 = CONTAINING_RECORD(c->space.Blink, space, list_entry); + space* s2 = CONTAINING_RECORD(list->Blink, space, list_entry); - if (s2->offset < offset) - InsertTailList(&c->space, &s->list_entry); + if (s2->address < offset) + InsertTailList(list, &s->list_entry); else { LIST_ENTRY* le; - le = c->space.Flink; - while (le != &c->space) { + le = list->Flink; + while (le != list) { s2 = CONTAINING_RECORD(le, space, list_entry); - if (s2->offset > offset) { + if (s2->address > offset) { InsertTailList(le, &s->list_entry); + goto size; + } + + le = le->Flink; + } + } + } + +size: + if (!list_size) + return STATUS_SUCCESS; + + if (IsListEmpty(list_size)) + InsertTailList(list_size, &s->list_entry_size); + else { + space* s2 = CONTAINING_RECORD(list_size->Blink, space, list_entry_size); + + if (s2->size >= size) + InsertTailList(list_size, &s->list_entry_size); + else { + LIST_ENTRY* le; + + le = list_size->Flink; + while (le != list_size) { + s2 = CONTAINING_RECORD(le, space, list_entry_size); + + if (s2->size <= size) { + InsertHeadList(le->Blink, &s->list_entry_size); return STATUS_SUCCESS; } @@ -176,27 +214,51 @@ static void load_free_space_bitmap(device_extension* Vcb, chunk* c, UINT64 offse addr = offset + (index * Vcb->superblock.sector_size); length = Vcb->superblock.sector_size * runlength; - add_space_entry(c, addr, length); + add_space_entry(&c->space, &c->space_size, addr, length); index += runlength; runlength = RtlFindNextForwardRunClear(&bmph, index, &index); } } +static void order_space_entry(space* s, LIST_ENTRY* list_size) { + LIST_ENTRY* le; + + if (IsListEmpty(list_size)) { + InsertHeadList(list_size, &s->list_entry_size); + return; + } + + le = list_size->Flink; + + while (le != list_size) { + space* s2 = CONTAINING_RECORD(le, space, list_entry_size); + + if (s2->size <= s->size) { + InsertHeadList(le->Blink, &s->list_entry_size); + return; + } + + le = le->Flink; + } + + InsertTailList(list_size, &s->list_entry_size); +} + static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { KEY searchkey; - traverse_ptr tp, tp2; + traverse_ptr tp; FREE_SPACE_ITEM* fsi; UINT64 inode, num_sectors, num_valid_sectors, i, *generation; - INODE_ITEM* ii; UINT8* data; NTSTATUS Status; UINT32 *checksums, crc32; FREE_SPACE_ENTRY* fse; UINT64 size, num_entries, num_bitmaps, extent_length, bmpnum, off; - LIST_ENTRY* le; + LIST_ENTRY *le, rollback; // FIXME - does this break if Vcb->superblock.sector_size is not 4096? + // FIXME - remove INODE_ITEM etc. if cache invalid for whatever reason TRACE("(%p, %llx)\n", Vcb, c->offset); @@ -228,65 +290,58 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { } inode = fsi->key.obj_id; - - searchkey = fsi->key; - num_entries = fsi->num_entries; num_bitmaps = fsi->num_bitmaps; - Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE); + Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache); if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); + ERR("open_fcb returned %08x\n", Status); return Status; } - if (keycmp(&tp2.item->key, &searchkey)) { - WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + if (c->cache->inode_item.st_size == 0) { + WARN("cache had zero length\n"); + free_fcb(c->cache); + c->cache = NULL; return STATUS_NOT_FOUND; } - if (tp2.item->size < sizeof(INODE_ITEM)) { - WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, sizeof(INODE_ITEM)); - return STATUS_NOT_FOUND; - } + c->cache->inode_item.flags |= BTRFS_INODE_NODATACOW; - ii = (INODE_ITEM*)tp2.item->data; - - if (ii->st_size == 0) { - ERR("inode %llx had a length of 0\n", inode); - return STATUS_NOT_FOUND; - } - - c->cache_size = ii->st_size; - c->cache_inode = fsi->key.obj_id; - - size = sector_align(ii->st_size, Vcb->superblock.sector_size); + size = sector_align(c->cache->inode_item.st_size, Vcb->superblock.sector_size); data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); if (!data) { ERR("out of memory\n"); + free_fcb(c->cache); + c->cache = NULL; return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL); + Status = read_file(c->cache, data, 0, c->cache->inode_item.st_size, NULL, NULL); if (!NT_SUCCESS(Status)) { ERR("read_file returned %08x\n", Status); ExFreePool(data); + + c->cache->deleted = TRUE; + mark_fcb_dirty(c->cache); + + free_fcb(c->cache); + c->cache = NULL; return Status; } - if (size > ii->st_size) - RtlZeroMemory(&data[ii->st_size], size - ii->st_size); + if (size > c->cache->inode_item.st_size) + RtlZeroMemory(&data[c->cache->inode_item.st_size], size - c->cache->inode_item.st_size); num_sectors = size / Vcb->superblock.sector_size; generation = (UINT64*)(data + (num_sectors * sizeof(UINT32))); if (*generation != fsi->generation) { - WARN("free space cache generation for %llx was %llx, expected %llx\n", c->offset, generation, fsi->generation); - ExFreePool(data); - return STATUS_NOT_FOUND; + WARN("free space cache generation for %llx was %llx, expected %llx\n", c->offset, *generation, fsi->generation); + goto clearcache; } extent_length = (num_sectors * sizeof(UINT32)) + sizeof(UINT64) + (num_entries * sizeof(FREE_SPACE_ENTRY)); @@ -295,8 +350,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { if (num_valid_sectors > num_sectors) { ERR("free space cache for %llx was %llx sectors, expected at least %llx\n", c->offset, num_sectors, num_valid_sectors); - ExFreePool(data); - return STATUS_NOT_FOUND; + goto clearcache; } checksums = (UINT32*)data; @@ -311,8 +365,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { if (crc32 != checksums[i]) { WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]); - ExFreePool(data); - return STATUS_NOT_FOUND; + goto clearcache; } } @@ -326,7 +379,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { fse = (FREE_SPACE_ENTRY*)&data[off]; if (fse->type == FREE_SPACE_EXTENT) { - Status = add_space_entry(c, fse->offset, fse->size); + Status = add_space_entry(&c->space, &c->space_size, fse->offset, fse->size); if (!NT_SUCCESS(Status)) { ERR("add_space_entry returned %08x\n", Status); ExFreePool(data); @@ -367,12 +420,16 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { if (le2 != &c->space) { space* s2 = CONTAINING_RECORD(le2, space, list_entry); - if (s2->offset == s->offset + s->size) { + if (s2->address == s->address + s->size) { s->size += s2->size; RemoveEntryList(&s2->list_entry); + RemoveEntryList(&s2->list_entry_size); ExFreePool(s2); + RemoveEntryList(&s->list_entry_size); + order_space_entry(s, &c->space_size); + le2 = le; } } @@ -383,6 +440,27 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) { ExFreePool(data); return STATUS_SUCCESS; + +clearcache: + ExFreePool(data); + + InitializeListHead(&rollback); + + Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("excise_extents returned %08x\n", Status); + do_rollback(Vcb, &rollback); + return Status; + } + + clear_rollback(&rollback); + + c->cache->deleted = TRUE; + mark_fcb_dirty(c->cache); + + free_fcb(c->cache); + c->cache = NULL; + return STATUS_NOT_FOUND; } NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { @@ -390,9 +468,9 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { KEY searchkey; UINT64 lastaddr; BOOL b; - space *s, *s2; - LIST_ENTRY* le; + space* s; NTSTATUS Status; +// LIST_ENTRY* le; if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) { Status = load_stored_free_space_cache(Vcb, c); @@ -432,12 +510,13 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { return STATUS_INSUFFICIENT_RESOURCES; } - s->offset = lastaddr; + s->address = lastaddr; s->size = tp.item->key.obj_id - lastaddr; - s->type = SPACE_TYPE_FREE; InsertTailList(&c->space, &s->list_entry); - TRACE("(%llx,%llx)\n", s->offset, s->size); + order_space_entry(s, &c->space_size); + + TRACE("(%llx,%llx)\n", s->address, s->size); } if (tp.item->key.obj_type == TYPE_METADATA_ITEM) @@ -459,95 +538,72 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) { return STATUS_INSUFFICIENT_RESOURCES; } - s->offset = lastaddr; + s->address = lastaddr; s->size = c->offset + c->chunk_item->size - lastaddr; - s->type = SPACE_TYPE_FREE; InsertTailList(&c->space, &s->list_entry); - TRACE("(%llx,%llx)\n", s->offset, s->size); + order_space_entry(s, &c->space_size); + + TRACE("(%llx,%llx)\n", s->address, s->size); } } - // add allocated space - - lastaddr = c->offset; - - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - - if (s->offset > lastaddr) { - s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); - - if (!s2) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - s2->offset = lastaddr; - s2->size = s->offset - lastaddr; - s2->type = SPACE_TYPE_USED; - - InsertTailList(&s->list_entry, &s2->list_entry); - } - - lastaddr = s->offset + s->size; - - le = le->Flink; - } - - if (lastaddr < c->offset + c->chunk_item->size) { - s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); - - if (!s) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - s->offset = lastaddr; - s->size = c->offset + c->chunk_item->size - lastaddr; - s->type = SPACE_TYPE_USED; - InsertTailList(&c->space, &s->list_entry); - } - - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - - TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type); - - le = le->Flink; - } - +// le = c->space_size.Flink; +// while (le != &c->space_size) { +// space* s = CONTAINING_RECORD(le, space, list_entry_size); +// +// ERR("(%llx, %llx)\n", s->address, s->size); +// +// le = le->Flink; +// } +// ERR("---\n"); + return STATUS_SUCCESS; } -static NTSTATUS insert_cache_extent(device_extension* Vcb, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { - LIST_ENTRY* le = Vcb->chunks.Flink; +static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { + LIST_ENTRY* le = fcb->Vcb->chunks.Flink; chunk* c; UINT64 flags; - // FIXME - how do we know which RAID level to put this to? - flags = BLOCK_FLAG_DATA; // SINGLE + flags = fcb->Vcb->data_flags; - while (le != &Vcb->chunks) { + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + while (le != &fcb->Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk_inode(Vcb, Vcb->root_root, inode, NULL, c, start, length, FALSE, NULL, NULL, rollback)) + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) { + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); return STATUS_SUCCESS; + } } + ExReleaseResourceLite(&c->nonpaged->lock); + le = le->Flink; } - if ((c = alloc_chunk(Vcb, flags, rollback))) { + if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) { + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk_inode(Vcb, Vcb->root_root, inode, NULL, c, start, length, FALSE, NULL, NULL, rollback)) + if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) { + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); return STATUS_SUCCESS; + } } + + ExReleaseResourceLite(&c->nonpaged->lock); } + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); + WARN("couldn't find any data chunks with %llx bytes free\n", length); return STATUS_DISK_FULL; @@ -557,7 +613,6 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan LIST_ENTRY* le; NTSTATUS Status; UINT64 num_entries, new_cache_size, i; - UINT64 lastused = c->offset; UINT32 num_sectors; // FIXME - also do bitmaps @@ -567,25 +622,25 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan num_entries = 0; - le = c->space.Flink; - while (le != &c->space) { - space* s = CONTAINING_RECORD(le, space, list_entry); + // num_entries is the number of entries in c->space and c->deleting - it might + // be slightly higher then what we end up writing, but doing it this way is much + // quicker and simpler. + if (!IsListEmpty(&c->space)) { + le = c->space.Flink; + while (le != &c->space) { + num_entries++; - if (s->type == SPACE_TYPE_USED || s->type == SPACE_TYPE_WRITING) { - if (s->offset > lastused) { -// TRACE("free: (%llx,%llx)\n", lastused, s->offset - lastused); - num_entries++; - } - - lastused = s->offset + s->size; + le = le->Flink; } - - le = le->Flink; } - if (c->offset + c->chunk_item->size > lastused) { -// TRACE("free: (%llx,%llx)\n", lastused, c->offset + c->chunk_item->size - lastused); - num_entries++; + if (!IsListEmpty(&c->deleting)) { + le = c->deleting.Flink; + while (le != &c->deleting) { + num_entries++; + + le = le->Flink; + } } new_cache_size = sizeof(UINT64) + (num_entries * sizeof(FREE_SPACE_ENTRY)); @@ -605,157 +660,109 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan new_cache_size = sector_align(new_cache_size, CACHE_INCREMENTS * Vcb->superblock.sector_size); - TRACE("chunk %llx: cache_size = %llx, new_cache_size = %llx\n", c->offset, c->cache_size, new_cache_size); + TRACE("chunk %llx: cache_size = %llx, new_cache_size = %llx\n", c->offset, c->cache->inode_item.st_size, new_cache_size); - if (new_cache_size > c->cache_size) { - if (c->cache_size == 0) { - INODE_ITEM* ii; - FREE_SPACE_ITEM* fsi; - UINT64 inode; - - // create new inode - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlZeroMemory(ii, sizeof(INODE_ITEM)); - ii->st_size = new_cache_size; - ii->st_blocks = new_cache_size; - ii->st_nlink = 1; - ii->st_mode = S_IRUSR | S_IWUSR | __S_IFREG; - ii->flags = BTRFS_INODE_NODATASUM | BTRFS_INODE_NODATACOW | BTRFS_INODE_NOCOMPRESS | BTRFS_INODE_PREALLOC; - - if (Vcb->root_root->lastinode == 0) - get_last_inode(Vcb, Vcb->root_root); - - inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101; - - if (!insert_tree_item(Vcb, Vcb->root_root, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - return STATUS_INTERNAL_ERROR; - } - - // create new free space entry - - fsi = ExAllocatePoolWithTag(PagedPool, sizeof(FREE_SPACE_ITEM), ALLOC_TAG); - if (!fsi) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - fsi->key.obj_id = inode; - 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)) { - ERR("insert_tree_item failed\n"); - return STATUS_INTERNAL_ERROR; - } - - // allocate space - - Status = insert_cache_extent(Vcb, inode, 0, new_cache_size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("insert_cache_extent returned %08x\n", Status); - return Status; - } - - Vcb->root_root->lastinode = inode; - c->cache_inode = inode; - } else { - KEY searchkey; - traverse_ptr tp; - INODE_ITEM* ii; - - ERR("extending existing inode\n"); - - // FIXME - try to extend existing extent first of all - // Or ditch all existing extents and replace with one new one? - - // add INODE_ITEM to tree cache - - searchkey.obj_id = c->cache_inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - 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 (keycmp(&searchkey, &tp.item->key)) { - ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); - return STATUS_INTERNAL_ERROR; - } - - 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)); - return STATUS_INTERNAL_ERROR; - } - - ii = (INODE_ITEM*)tp.item->data; - - if (!tp.tree->write) { - tp.tree->write = TRUE; - Vcb->write_trees++; - } - - // add free_space entry to tree cache - - searchkey.obj_id = FREE_SPACE_CACHE_ID; - searchkey.obj_type = 0; - searchkey.offset = c->offset; - - 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 (keycmp(&searchkey, &tp.item->key)) { - ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); - return STATUS_INTERNAL_ERROR; - } - - if (tp.item->size < sizeof(FREE_SPACE_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(FREE_SPACE_ITEM)); - return STATUS_INTERNAL_ERROR; - } - - if (!tp.tree->write) { - tp.tree->write = TRUE; - Vcb->write_trees++; - } - - // add new extent - - Status = insert_cache_extent(Vcb, c->cache_inode, c->cache_size, new_cache_size - c->cache_size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("insert_cache_extent returned %08x\n", Status); - return Status; - } - - // modify INODE_ITEM - - ii->st_size = new_cache_size; - ii->st_blocks = new_cache_size; - } - - c->cache_size = new_cache_size; - *changed = TRUE; - } else { + if (!c->cache) { + FREE_SPACE_ITEM* fsi; KEY searchkey; traverse_ptr tp; - // add INODE_ITEM and free_space entry to tree cache, for writing later + // create new inode - searchkey.obj_id = c->cache_inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; + c->cache = create_fcb(); + if (!c->cache) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + c->cache->Vcb = Vcb; + + c->cache->inode_item.st_size = new_cache_size; + c->cache->inode_item.st_blocks = new_cache_size; + c->cache->inode_item.st_nlink = 1; + c->cache->inode_item.st_mode = S_IRUSR | S_IWUSR | __S_IFREG; + c->cache->inode_item.flags = BTRFS_INODE_NODATASUM | BTRFS_INODE_NODATACOW | BTRFS_INODE_NOCOMPRESS | BTRFS_INODE_PREALLOC; + + c->cache->Header.IsFastIoPossible = fast_io_possible(c->cache); + c->cache->Header.AllocationSize.QuadPart = 0; + c->cache->Header.FileSize.QuadPart = 0; + c->cache->Header.ValidDataLength.QuadPart = 0; + + c->cache->subvol = Vcb->root_root; + + if (Vcb->root_root->lastinode == 0) + get_last_inode(Vcb, Vcb->root_root); + + c->cache->inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101; + + c->cache->type = BTRFS_TYPE_FILE; + c->cache->created = TRUE; + + // create new free space entry + + fsi = ExAllocatePoolWithTag(PagedPool, sizeof(FREE_SPACE_ITEM), ALLOC_TAG); + if (!fsi) { + ERR("out of memory\n"); + free_fcb(c->cache); + c->cache = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + searchkey.obj_id = FREE_SPACE_CACHE_ID; + searchkey.obj_type = 0; + searchkey.offset = c->offset; + + 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 (!keycmp(&searchkey, &tp.item->key)) + delete_tree_item(Vcb, &tp, rollback); + + fsi->key.obj_id = c->cache->inode; + 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)) { + ERR("insert_tree_item failed\n"); + free_fcb(c->cache); + c->cache = NULL; + return STATUS_INTERNAL_ERROR; + } + + // allocate space + + Status = insert_cache_extent(c->cache, 0, new_cache_size, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_cache_extent returned %08x\n", Status); + free_fcb(c->cache); + c->cache = NULL; + return Status; + } + + c->cache->extents_changed = TRUE; + + Vcb->root_root->lastinode = c->cache->inode; + + flush_fcb(c->cache, TRUE, rollback); + + *changed = TRUE; + } else if (new_cache_size > c->cache->inode_item.st_size) { + KEY searchkey; + traverse_ptr tp; + + ERR("extending existing inode\n"); + + // FIXME - try to extend existing extent first of all + // Or ditch all existing extents and replace with one new one? + + // add free_space entry to tree cache + + searchkey.obj_id = FREE_SPACE_CACHE_ID; + searchkey.obj_type = 0; + searchkey.offset = c->offset; Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { @@ -768,14 +775,62 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan return STATUS_INTERNAL_ERROR; } - 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)); + if (tp.item->size < sizeof(FREE_SPACE_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(FREE_SPACE_ITEM)); return STATUS_INTERNAL_ERROR; } - if (!tp.tree->write) { + tp.tree->write = TRUE; + + // add new extent + + Status = insert_cache_extent(c->cache, c->cache->inode_item.st_size, new_cache_size - c->cache->inode_item.st_size, rollback); + if (!NT_SUCCESS(Status)) { + ERR("insert_cache_extent returned %08x\n", Status); + return Status; + } + + // modify INODE_ITEM + + c->cache->inode_item.st_size = new_cache_size; + c->cache->inode_item.st_blocks = new_cache_size; + + flush_fcb(c->cache, TRUE, rollback); + + *changed = TRUE; + } else { + KEY searchkey; + traverse_ptr tp; + + // add INODE_ITEM and free_space entry to tree cache, for writing later + + searchkey.obj_id = c->cache->inode; + searchkey.obj_type = TYPE_INODE_ITEM; + searchkey.offset = 0; + + 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 (keycmp(&searchkey, &tp.item->key)) { + INODE_ITEM* ii; + + 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)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + } 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)); + return STATUS_INTERNAL_ERROR; + } + tp.tree->write = TRUE; - Vcb->write_trees++; } searchkey.obj_id = FREE_SPACE_CACHE_ID; @@ -798,10 +853,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan return STATUS_INTERNAL_ERROR; } - if (!tp.tree->write) { - tp.tree->write = TRUE; - Vcb->write_trees++; - } + tp.tree->write = TRUE; } // FIXME - reduce inode allocation if cache is shrinking. Make sure to avoid infinite write loops @@ -810,27 +862,25 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan } NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) { - LIST_ENTRY* le = Vcb->chunks.Flink; + LIST_ENTRY* le = Vcb->chunks_changed.Flink; NTSTATUS Status; - chunk* c; *changed = FALSE; - while (le != &Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); + while (le != &Vcb->chunks_changed) { + 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); - if (c->space_changed) { - BOOL b; - - Status = allocate_cache_chunk(Vcb, c, &b, rollback); - - if (b) - *changed = TRUE; - - if (!NT_SUCCESS(Status)) { - ERR("allocate_cache_chunk(%llx) returned %08x\n", c->offset, Status); - return Status; - } + if (b) + *changed = TRUE; + + if (!NT_SUCCESS(Status)) { + ERR("allocate_cache_chunk(%llx) returned %08x\n", c->offset, Status); + return Status; } le = le->Flink; @@ -839,98 +889,277 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba return STATUS_SUCCESS; } -static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) { - NTSTATUS Status; - KEY searchkey; - traverse_ptr tp; - FREE_SPACE_ITEM* fsi; - INODE_ITEM* ii; - void* data; - FREE_SPACE_ENTRY* fse; - UINT64 num_entries, num_sectors, lastused, *cachegen, i, off; - UINT32* checksums; +void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { LIST_ENTRY* le; - BOOL b; + space *s, *s2; - data = ExAllocatePoolWithTag(NonPagedPool, c->cache_size, ALLOC_TAG); - if (!data) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; +#ifdef DEBUG_SPACE_LISTS + _debug_message(func, "called space_list_add (%p, %llx, %llx, %p)\n", list, address, length, rollback); +#endif + + if (IsListEmpty(list)) { + s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); + + if (!s) { + ERR("out of memory\n"); + return; + } + + s->address = address; + s->size = length; + InsertTailList(list, &s->list_entry); + + if (list_size) + InsertTailList(list_size, &s->list_entry_size); + + // FIXME - insert rollback entry + + return; } - RtlZeroMemory(data, c->cache_size); - - num_entries = 0; - num_sectors = c->cache_size / Vcb->superblock.sector_size; - off = (sizeof(UINT32) * num_sectors) + sizeof(UINT64); - - lastused = c->offset; - - le = c->space.Flink; - while (le != &c->space) { - space* s = CONTAINING_RECORD(le, space, list_entry); - - if (s->type == SPACE_TYPE_USED || s->type == SPACE_TYPE_WRITING) { - if (s->offset > lastused) { - if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size) - off = sector_align(off, Vcb->superblock.sector_size); + le = list->Flink; + while (le != list) { + s2 = CONTAINING_RECORD(le, space, list_entry); + + // old entry envelops new one completely + if (s2->address <= address && s2->address + s2->size >= address + length) + return; + + // new entry envelops old one completely + if (address <= s2->address && address + length >= s2->address + s2->size) { + if (address < s2->address) { + s2->size += s2->address - address; + s2->address = address; + // FIXME - insert rollback - fse = (FREE_SPACE_ENTRY*)((UINT8*)data + off); - - fse->offset = lastused; - fse->size = s->offset - lastused; - fse->type = FREE_SPACE_EXTENT; - num_entries++; - - off += sizeof(FREE_SPACE_ENTRY); + while (s2->list_entry.Blink != list) { + space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry); + + if (s3->address + s3->size == s2->address) { + s2->address = s3->address; + s2->size += s3->size; + + RemoveEntryList(&s3->list_entry); + + if (list_size) + RemoveEntryList(&s3->list_entry_size); + + ExFreePool(s3); + } else + break; + } } - lastused = s->offset + s->size; + if (length > s2->size) { + s2->size = length; + // FIXME - insert rollback + + while (s2->list_entry.Flink != list) { + space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry); + + if (s3->address <= s2->address + s2->size) { + s2->size = max(s2->size, s3->address + s3->size - s2->address); + + RemoveEntryList(&s3->list_entry); + + if (list_size) + RemoveEntryList(&s3->list_entry_size); + + ExFreePool(s3); + } else + break; + } + } + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + } + + return; + } + + // new entry overlaps start of old one + if (address < s2->address && address + length >= s2->address) { + 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); + + if (s3->address + s3->size == s2->address) { + s2->address = s3->address; + s2->size += s3->size; + + RemoveEntryList(&s3->list_entry); + + if (list_size) + RemoveEntryList(&s3->list_entry_size); + + ExFreePool(s3); + } else + break; + } + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + } + + return; + } + + // new entry overlaps end of old one + if (address <= s2->address + s2->size && address + length > s2->address + s2->size) { + 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); + + if (s3->address <= s2->address + s2->size) { + s2->size = max(s2->size, s3->address + s3->size - s2->address); + + RemoveEntryList(&s3->list_entry); + + if (list_size) + RemoveEntryList(&s3->list_entry_size); + + ExFreePool(s3); + } else + break; + } + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + } + + return; + } + + // add completely separate entry + if (s2->address > address + length) { + s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); + + if (!s) { + ERR("out of memory\n"); + return; + } + + // FIXME - insert rollback + s->address = address; + s->size = length; + InsertHeadList(s2->list_entry.Blink, &s->list_entry); + + if (list_size) + order_space_entry(s, list_size); + + return; } le = le->Flink; } - if (c->offset + c->chunk_item->size > lastused) { + // 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); + order_space_entry(s2, list_size); + } + + return; + } + + // otherwise, insert at end + s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); + + if (!s) { + ERR("out of memory\n"); + return; + } + + s->address = address; + s->size = length; + InsertTailList(list, &s->list_entry); + + if (list_size) + order_space_entry(s, list_size); + + // FIXME - insert rollback +} + +static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) { + LIST_ENTRY* le; + + if (!IsListEmpty(deleting)) { + le = deleting->Flink; + while (le != deleting) { + space* s = CONTAINING_RECORD(le, space, list_entry); + + space_list_add2(spacelist, spacelist_size, s->address, s->size, NULL); + + le = le->Flink; + } + } +} + +static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) { + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; + FREE_SPACE_ITEM* fsi; + void* data; + FREE_SPACE_ENTRY* fse; + UINT64 num_entries, num_sectors, *cachegen, i, off; + UINT32* checksums; + LIST_ENTRY* le; + + space_list_merge(&c->space, &c->space_size, &c->deleting); + + data = ExAllocatePoolWithTag(NonPagedPool, c->cache->inode_item.st_size, ALLOC_TAG); + if (!data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(data, c->cache->inode_item.st_size); + + num_entries = 0; + num_sectors = c->cache->inode_item.st_size / Vcb->superblock.sector_size; + off = (sizeof(UINT32) * num_sectors) + sizeof(UINT64); + + le = c->space.Flink; + while (le != &c->space) { + space* s = CONTAINING_RECORD(le, space, list_entry); + if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size) off = sector_align(off, Vcb->superblock.sector_size); fse = (FREE_SPACE_ENTRY*)((UINT8*)data + off); - fse->offset = lastused; - fse->size = c->offset + c->chunk_item->size - lastused; + fse->offset = s->address; + fse->size = s->size; fse->type = FREE_SPACE_EXTENT; num_entries++; + + off += sizeof(FREE_SPACE_ENTRY); + + le = le->Flink; } // update INODE_ITEM - searchkey.obj_id = c->cache_inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; + c->cache->inode_item.generation = Vcb->superblock.generation; + c->cache->inode_item.transid = Vcb->superblock.generation; + c->cache->inode_item.sequence++; + c->cache->inode_item.st_ctime = *now; - 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 (keycmp(&searchkey, &tp.item->key)) { - ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); - return STATUS_INTERNAL_ERROR; - } - - 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)); - return STATUS_INTERNAL_ERROR; - } - - ii = (INODE_ITEM*)tp.item->data; - - ii->generation = Vcb->superblock.generation; - ii->transid = Vcb->superblock.generation; - ii->sequence++; - ii->st_ctime = *now; + flush_fcb(c->cache, TRUE, rollback); // update free_space item @@ -982,85 +1211,19 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* // write cache - searchkey.obj_id = c->cache_inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = 0; - - Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE); + Status = do_nocow_write(Vcb, c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback); if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); + ERR("do_nocow_write returned %08x\n", Status); return Status; } - - do { - traverse_ptr next_tp; - - if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->key.offset < c->cache_size) { - EXTENT_DATA* ed; - EXTENT_DATA2* eds; - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } - - ed = (EXTENT_DATA*)tp.item->data; - - if (ed->type != EXTENT_TYPE_REGULAR) { - ERR("cache EXTENT_DATA type not regular\n"); - return STATUS_INTERNAL_ERROR; - } - if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - eds = (EXTENT_DATA2*)&ed->data[0]; - - if (ed->compression != BTRFS_COMPRESSION_NONE) { - ERR("not writing compressed cache\n"); - return STATUS_INTERNAL_ERROR; - } - - if (ed->encryption != BTRFS_ENCRYPTION_NONE) { - WARN("encryption not supported\n"); - return STATUS_INTERNAL_ERROR; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("other encodings not supported\n"); - return STATUS_INTERNAL_ERROR; - } - - if (eds->address == 0) { - ERR("not writing cache to sparse extent\n"); - return STATUS_INTERNAL_ERROR; - } - - Status = write_data(Vcb, eds->address + eds->offset, (UINT8*)data + tp.item->key.offset, min(c->cache_size - tp.item->key.offset, eds->num_bytes)); - if (!NT_SUCCESS(Status)) { - ERR("write_data returned %08x\n", Status); - return Status; - } - } - - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - 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)) - break; - } - } while (b); - ExFreePool(data); return STATUS_SUCCESS; } NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) { - LIST_ENTRY* le = Vcb->chunks.Flink; + LIST_ENTRY* le = Vcb->chunks_changed.Flink; NTSTATUS Status; chunk* c; LARGE_INTEGER time; @@ -1069,17 +1232,16 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) { KeQuerySystemTime(&time); win_time_to_unix(time, &now); - while (le != &Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); + while (le != &Vcb->chunks_changed) { + c = CONTAINING_RECORD(le, chunk, list_entry_changed); - if (c->space_changed) { - Status = update_chunk_cache(Vcb, c, &now, rollback); + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + Status = update_chunk_cache(Vcb, c, &now, rollback); + ExReleaseResourceLite(&c->nonpaged->lock); - if (!NT_SUCCESS(Status)) { - ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status); - return Status; - } - + if (!NT_SUCCESS(Status)) { + ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status); + return Status; } le = le->Flink; @@ -1087,3 +1249,103 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } + +void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { + LIST_ENTRY* list; + + TRACE("(%p, %p, %u, %llx, %llx, %p)\n", Vcb, c, deleting, address, length, rollback); + + list = deleting ? &c->deleting : &c->space; + + 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); +} + +void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { + LIST_ENTRY *le, *le2; + space *s, *s2; + +#ifdef DEBUG_SPACE_LISTS + _debug_message(func, "called space_list_subtract (%p, %llx, %llx, %p)\n", list, address, length, rollback); +#endif + + if (IsListEmpty(list)) + return; + + le = list->Flink; + while (le != list) { + s2 = CONTAINING_RECORD(le, space, list_entry); + le2 = le->Flink; + + if (s2->address >= address + length) + return; + + if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely + // FIXME - insert rollback + RemoveEntryList(&s2->list_entry); + + if (list_size) + RemoveEntryList(&s2->list_entry_size); + + ExFreePool(s2); + } else if (address + length > s2->address && address + length < s2->address + s2->size) { + if (address > s2->address) { // cut out hole + // FIXME - insert rollback + + s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); + + if (!s) { + ERR("out of memory\n"); + return; + } + + s->address = s2->address; + s->size = address - s2->address; + InsertHeadList(s2->list_entry.Blink, &s->list_entry); + + s2->size = s2->address + s2->size - address - length; + s2->address = address + length; + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + order_space_entry(s, list_size); + } + + return; + } else { // remove start of entry + s2->size -= address + length - s2->address; + s2->address = address + length; + // FIXME - insert rollback + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + } + } + } else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry + // FIXME - insert rollback + s2->size = address - s2->address; + + if (list_size) { + RemoveEntryList(&s2->list_entry_size); + order_space_entry(s2, list_size); + } + } + + le = le2; + } +} + +void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) { + LIST_ENTRY* list; + + list = deleting ? &c->deleting : &c->space; + + 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); +} diff --git a/reactos/drivers/filesystems/btrfs/fsctl.c b/reactos/drivers/filesystems/btrfs/fsctl.c index 13903180027..27c1644235e 100644 --- a/reactos/drivers/filesystems/btrfs/fsctl.c +++ b/reactos/drivers/filesystems/btrfs/fsctl.c @@ -27,6 +27,9 @@ #define DOTDOT ".." +extern LIST_ENTRY VcbList; +extern ERESOURCE global_loading_lock; + static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) { btrfs_get_file_ids* bgfi; fcb* fcb; @@ -68,7 +71,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) { UINT8* buf; NTSTATUS Status; - write_tree_context* wtc; + write_data_context* wtc; LIST_ENTRY* le; tree t; tree_header* th; @@ -80,16 +83,16 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub return STATUS_INSUFFICIENT_RESOURCES; } - wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG); + wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG); if (!wtc) { ERR("out of memory\n"); ExFreePool(buf); return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_tree(Vcb, addr, buf); + Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL); if (!NT_SUCCESS(Status)) { - ERR("read_tree returned %08x\n", Status); + ERR("read_data returned %08x\n", Status); goto end; } @@ -140,7 +143,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, 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, rollback); if (!NT_SUCCESS(Status)) { ERR("increase_extent_refcount_data returned %08x\n", Status); @@ -172,10 +175,12 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE); InitializeListHead(&wtc->stripes); + wtc->tree = TRUE; + wtc->stripes_left = 0; - Status = write_tree(Vcb, t.new_address, buf, wtc); + Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL); if (!NT_SUCCESS(Status)) { - ERR("write_tree returned %08x\n", Status); + ERR("write_data returned %08x\n", Status); goto end; } @@ -183,9 +188,10 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub // launch writes and wait le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); - IoCallDriver(stripe->device->devobj, stripe->Irp); + if (stripe->status != WriteDataStatus_Ignore) + IoCallDriver(stripe->device->devobj, stripe->Irp); le = le->Flink; } @@ -194,9 +200,9 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); - if (!NT_SUCCESS(stripe->iosb.Status)) { + if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { Status = stripe->iosb.Status; break; } @@ -204,7 +210,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub le = le->Flink; } - free_write_tree_stripes(wtc); + free_write_data_stripes(wtc); buf = NULL; } @@ -220,7 +226,7 @@ end: return Status; } -static void flush_subvol_fcbs(root* subvol) { +static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) { LIST_ENTRY* le = subvol->fcbs.Flink; if (IsListEmpty(&subvol->fcbs)) @@ -237,7 +243,7 @@ static void flush_subvol_fcbs(root* subvol) { } } -static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8) { +static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8, PUNICODE_STRING name) { LIST_ENTRY rollback; UINT64 id; NTSTATUS Status; @@ -248,22 +254,33 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f LARGE_INTEGER time; BTRFS_TIME now; fcb* fcb = parent->FsContext; - ULONG disize, rrsize; - DIR_ITEM *di, *di2; - ROOT_REF *rr, *rr2; - INODE_ITEM* ii; + ccb* ccb = parent->FsContext2; + LIST_ENTRY* le; + file_ref *fileref, *fr; + + if (!ccb) { + ERR("error - ccb was NULL\n"); + return STATUS_INTERNAL_ERROR; + } + + if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + fileref = ccb->fileref; InitializeListHead(&rollback); - acquire_tree_lock(Vcb, TRUE); + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); // flush open files on this subvol - flush_subvol_fcbs(subvol); + flush_subvol_fcbs(subvol, &rollback); // flush metadata - if (Vcb->write_trees > 0) + if (Vcb->need_write) do_write(Vcb, &rollback); free_trees(Vcb); @@ -394,140 +411,110 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f goto end; } - if (!subvol->treeholder.tree->write) { - subvol->treeholder.tree->write = TRUE; - Vcb->write_trees++; - } + subvol->treeholder.tree->write = TRUE; - // add DIR_ITEM + // create fileref for entry in other subvolume - dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode); - if (dirpos == 0) { - ERR("find_next_dir_index failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - disize = sizeof(DIR_ITEM) - 1 + utf8->Length; - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { + fr = create_fileref(); + if (!fr) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di2) { + fr->utf8.Length = fr->utf8.MaximumLength = utf8->Length; + fr->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, fr->utf8.MaximumLength, ALLOC_TAG); + if (!fr->utf8.Buffer) { ERR("out of memory\n"); + free_fileref(fr); Status = STATUS_INSUFFICIENT_RESOURCES; - ExFreePool(di); goto end; } - di->key.obj_id = id; - di->key.obj_type = TYPE_ROOT_ITEM; - di->key.offset = 0xffffffffffffffff; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8->Length; - di->type = BTRFS_TYPE_DIRECTORY; - RtlCopyMemory(di->name, utf8->Buffer, utf8->Length); + RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length); - RtlCopyMemory(di2, di, disize); - - Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback); + Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb); if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); + ERR("open_fcb returned %08x\n", Status); + free_fileref(fr); goto end; } - // add DIR_INDEX - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; + Status = fcb_get_last_dir_index(fcb, &dirpos); + if (!NT_SUCCESS(Status)) { + ERR("fcb_get_last_dir_index returned %08x\n", Status); + free_fileref(fr); goto end; } - // add ROOT_REF + fr->index = dirpos; - rrsize = sizeof(ROOT_REF) - 1 + utf8->Length; - rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); - if (!rr) { + fr->filepart.MaximumLength = fr->filepart.Length = name->Length; + + fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG); + if (!fr->filepart.Buffer) { ERR("out of memory\n"); + free_fileref(fr); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - rr->dir = fcb->inode; - rr->index = dirpos; - rr->n = utf8->Length; - RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length); + RtlCopyMemory(fr->filepart.Buffer, name->Buffer, name->Length); - if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; + Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + free_fileref(fr); goto end; } - // add ROOT_BACKREF + fr->parent = fileref; - rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); - if (!rr2) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } + insert_fileref_child(fileref, fr, TRUE); + increase_fileref_refcount(fileref); - RtlCopyMemory(rr2, rr, rrsize); + fr->created = TRUE; + mark_fileref_dirty(fr); - if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } + if (fr->fcb->type == BTRFS_TYPE_DIRECTORY) + fr->fcb->fileref = fr; + free_fileref(fr); + // change fcb's INODE_ITEM - // unlike when we create a file normally, the seq of the parent doesn't appear to change fcb->inode_item.transid = Vcb->superblock.generation; + fcb->inode_item.sequence++; fcb->inode_item.st_size += utf8->Length * 2; fcb->inode_item.st_ctime = now; fcb->inode_item.st_mtime = now; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - if (keycmp(&searchkey, &tp.item->key)) { - ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - delete_tree_item(Vcb, &tp, &rollback); - - insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback); + mark_fcb_dirty(fcb); fcb->subvol->root_item.ctime = now; fcb->subvol->root_item.ctransid = Vcb->superblock.generation; - if (Vcb->write_trees > 0) - do_write(Vcb, &rollback); + send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED); + send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + + le = subvol->fcbs.Flink; + while (le != &subvol->fcbs) { + struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry); + LIST_ENTRY* le2 = fcb2->extents.Flink; + + while (le2 != &fcb2->extents) { + extent* ext = CONTAINING_RECORD(le2, extent, list_entry); + + if (!ext->ignore) + ext->unique = FALSE; + + le2 = le2->Flink; + } + + le = le->Flink; + } + + do_write(Vcb, &rollback); free_trees(Vcb); @@ -539,7 +526,7 @@ end: else do_rollback(Vcb, &rollback); - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); return Status; } @@ -577,6 +564,11 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, fileref = ccb->fileref; + if (!fileref) { + ERR("fileref was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) { WARN("insufficient privileges\n"); return STATUS_ACCESS_DENIED; @@ -617,10 +609,15 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length); - if (find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) { + 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; + } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ERR("find_file_in_dir_with_crc32 returned %08x\n", Status); + goto end2; } Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL); @@ -654,39 +651,19 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, goto end; } - Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8); + Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8, &nameus); if (NT_SUCCESS(Status)) { - UNICODE_STRING ffn; - - ffn.Length = fileref->full_filename.Length + bcs->namelen; - if (fcb != fcb->Vcb->root_fileref->fcb) - ffn.Length += sizeof(WCHAR); - - ffn.MaximumLength = ffn.Length; - ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG); - - if (ffn.Buffer) { - ULONG i; - - RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length); - i = fileref->full_filename.Length; - - if (fcb != fcb->Vcb->root_fileref->fcb) { - ffn.Buffer[i / sizeof(WCHAR)] = '\\'; - i += sizeof(WCHAR); - } - - RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], bcs->name, bcs->namelen); - - TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer); - - FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL, - FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL); - - ExFreePool(ffn.Buffer); - } else - ERR("out of memory\n"); + file_ref* fr; + + Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL); + if (!NT_SUCCESS(Status)) { + ERR("open_fileref returned %08x\n", Status); + Status = STATUS_SUCCESS; + } else { + send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED); + free_fileref(fr); + } } end: @@ -699,7 +676,7 @@ end2: } static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) { - fcb* fcb; + fcb *fcb, *rootfcb; ccb* ccb; file_ref* fileref; NTSTATUS Status; @@ -708,22 +685,19 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC root* r; LARGE_INTEGER time; BTRFS_TIME now; - ULONG len, disize, rrsize, irsize; + ULONG len, irsize; UNICODE_STRING nameus; ANSI_STRING utf8; UINT64 dirpos; - DIR_ITEM *di, *di2; UINT32 crc32; - ROOT_REF *rr, *rr2; - INODE_ITEM* ii; INODE_REF* ir; KEY searchkey; traverse_ptr tp; - SECURITY_DESCRIPTOR* sd = NULL; SECURITY_SUBJECT_CONTEXT subjcont; PSID owner; BOOLEAN defaulted; UINT64* root_num; + file_ref* fr; fcb = FileObject->FsContext; if (!fcb) { @@ -744,6 +718,11 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC return STATUS_NOT_A_DIRECTORY; } + if (!fileref) { + ERR("fileref was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + if (fileref->deleted || fcb->deleted) { ERR("parent has been deleted\n"); return STATUS_FILE_DELETED; @@ -787,7 +766,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC goto end2; } - acquire_tree_lock(Vcb, TRUE); + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); KeQuerySystemTime(&time); win_time_to_unix(time, &now); @@ -796,10 +775,15 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length); - if (find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) { + Status = find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL); + + if (NT_SUCCESS(Status)) { WARN("file already exists\n"); Status = STATUS_OBJECT_NAME_COLLISION; goto end; + } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ERR("find_file_in_dir_with_crc32 returned %08x\n", Status); + goto end; } if (Vcb->root_root->lastinode == 0) @@ -875,59 +859,62 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC // add .. inode to new subvol - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { + rootfcb = create_fcb(); + if (!rootfcb) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - RtlZeroMemory(ii, sizeof(INODE_ITEM)); - ii->generation = Vcb->superblock.generation; - ii->transid = Vcb->superblock.generation; - ii->st_nlink = 1; - ii->st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755 - ii->st_atime = ii->st_ctime = ii->st_mtime = ii->otime = now; - ii->st_gid = GID_NOBODY; // FIXME? - + rootfcb->Vcb = Vcb; + + rootfcb->subvol = r; + rootfcb->inode = SUBVOL_ROOT_INODE; + rootfcb->type = BTRFS_TYPE_DIRECTORY; + + rootfcb->inode_item.generation = Vcb->superblock.generation; + rootfcb->inode_item.transid = Vcb->superblock.generation; + rootfcb->inode_item.st_nlink = 1; + rootfcb->inode_item.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755 + 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); + SeCaptureSubjectContext(&subjcont); - Status = SeAssignSecurity(fcb->sd, NULL, (void**)&sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); + Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); if (!NT_SUCCESS(Status)) { ERR("SeAssignSecurity returned %08x\n", Status); goto end; } - if (!sd) { + if (!rootfcb->sd) { ERR("SeAssignSecurity returned NULL security descriptor\n"); Status = STATUS_INTERNAL_ERROR; goto end; } - Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted); + Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted); if (!NT_SUCCESS(Status)) { ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); - ii->st_uid = UID_NOBODY; + rootfcb->inode_item.st_uid = UID_NOBODY; } else { - ii->st_uid = sid_to_uid(&owner); + rootfcb->inode_item.st_uid = sid_to_uid(&owner); } + + rootfcb->sd_dirty = TRUE; - if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } + InsertTailList(&r->fcbs, &rootfcb->list_entry); + InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all); - // add security.NTACL xattr + rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb); + rootfcb->Header.AllocationSize.QuadPart = 0; + rootfcb->Header.FileSize.QuadPart = 0; + rootfcb->Header.ValidDataLength.QuadPart = 0; - Status = set_xattr(Vcb, r, r->root_item.objid, EA_NTACL, EA_NTACL_HASH, (UINT8*)sd, RtlLengthSecurityDescriptor(fcb->sd), &rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - goto end; - } - - ExFreePool(sd); + rootfcb->created = TRUE; // add INODE_REF @@ -949,93 +936,58 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC goto end; } - // add DIR_ITEM + // create fileref for entry in other subvolume - dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode); - if (dirpos == 0) { - ERR("find_next_dir_index failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - disize = sizeof(DIR_ITEM) - 1 + utf8.Length; - di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di) { + fr = create_fileref(); + if (!fr) { ERR("out of memory\n"); + free_fcb(rootfcb); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); - if (!di2) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - ExFreePool(di); - goto end; - } + fr->fcb = rootfcb; - di->key.obj_id = id; - di->key.obj_type = TYPE_ROOT_ITEM; - di->key.offset = 0; - di->transid = Vcb->superblock.generation; - di->m = 0; - di->n = utf8.Length; - di->type = BTRFS_TYPE_DIRECTORY; - RtlCopyMemory(di->name, utf8.Buffer, utf8.Length); + mark_fcb_dirty(rootfcb); - RtlCopyMemory(di2, di, disize); - - Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback); + Status = fcb_get_last_dir_index(fcb, &dirpos); if (!NT_SUCCESS(Status)) { - ERR("add_dir_item returned %08x\n", Status); + ERR("fcb_get_last_dir_index returned %08x\n", Status); + free_fileref(fr); goto end; } - // add DIR_INDEX + fr->index = dirpos; + fr->utf8 = utf8; - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - // add ROOT_REF - - rrsize = sizeof(ROOT_REF) - 1 + utf8.Length; - rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); - if (!rr) { + fr->filepart.MaximumLength = fr->filepart.Length = nameus.Length; + fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG); + if (!fr->filepart.Buffer) { ERR("out of memory\n"); + free_fileref(fr); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } - rr->dir = fcb->inode; - rr->index = dirpos; - rr->n = utf8.Length; - RtlCopyMemory(rr->name, utf8.Buffer, utf8.Length); + RtlCopyMemory(fr->filepart.Buffer, nameus.Buffer, nameus.Length); - if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; + Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE); + if (!NT_SUCCESS(Status)) { + ERR("RtlUpcaseUnicodeString returned %08x\n", Status); + free_fileref(fr); goto end; } - // add ROOT_BACKREF + fr->parent = fileref; - rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); - if (!rr2) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } + insert_fileref_child(fileref, fr, TRUE); + increase_fileref_refcount(fileref); - RtlCopyMemory(rr2, rr, rrsize); + if (fr->fcb->type == BTRFS_TYPE_DIRECTORY) + fr->fcb->fileref = fr; - if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) { - ERR("insert_tree_item failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } + fr->created = TRUE; + mark_fileref_dirty(fr); // change fcb->subvol's ROOT_ITEM @@ -1044,89 +996,34 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC // change fcb's INODE_ITEM - // unlike when we create a file normally, the times and seq of the parent don't appear to change fcb->inode_item.transid = Vcb->superblock.generation; fcb->inode_item.st_size += utf8.Length * 2; + fcb->inode_item.sequence++; + fcb->inode_item.st_ctime = now; + fcb->inode_item.st_mtime = now; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - if (keycmp(&searchkey, &tp.item->key)) { - ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - delete_tree_item(Vcb, &tp, &rollback); - - insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback); + mark_fcb_dirty(fcb); Vcb->root_root->lastinode = id; Status = STATUS_SUCCESS; end: - if (NT_SUCCESS(Status)) - Status = consider_write(Vcb); - if (!NT_SUCCESS(Status)) do_rollback(Vcb, &rollback); else clear_rollback(&rollback); - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); if (NT_SUCCESS(Status)) { - UNICODE_STRING ffn; - - ffn.Length = fileref->full_filename.Length + length; - if (fcb != fcb->Vcb->root_fileref->fcb) - ffn.Length += sizeof(WCHAR); - - ffn.MaximumLength = ffn.Length; - ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG); - - if (ffn.Buffer) { - ULONG i; - - RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length); - i = fileref->full_filename.Length; - - if (fcb != fcb->Vcb->root_fileref->fcb) { - ffn.Buffer[i / sizeof(WCHAR)] = '\\'; - i += sizeof(WCHAR); - } - - RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], name, length); - - TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer); - - FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL, - FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL); - - ExFreePool(ffn.Buffer); - } else - ERR("out of memory\n"); + send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED); + send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); } end2: - if (utf8.Buffer) - ExFreePool(utf8.Buffer); + if (fr) + free_fileref(fr); return Status; } @@ -1187,6 +1084,818 @@ 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) { + FILE_SET_SPARSE_BUFFER* fssb = data; + NTSTATUS Status; + BOOL set; + fcb* fcb; + ccb* ccb = FileObject->FsContext2; + file_ref* fileref = ccb ? ccb->fileref : NULL; + + if (data && length < sizeof(FILE_SET_SPARSE_BUFFER)) + return STATUS_INVALID_PARAMETER; + + if (!FileObject) { + ERR("FileObject was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + fcb = FileObject->FsContext; + + if (!fcb) { + ERR("FCB was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!ccb) { + ERR("CCB was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) { + WARN("insufficient privileges\n"); + return STATUS_ACCESS_DENIED; + } + + if (!fileref) { + ERR("no fileref\n"); + return STATUS_INVALID_PARAMETER; + } + + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + if (fcb->type != BTRFS_TYPE_FILE) { + WARN("FileObject did not point to a file\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (fssb) + set = fssb->SetSparse; + else + set = TRUE; + + if (set) { + fcb->atts |= FILE_ATTRIBUTE_SPARSE_FILE; + fcb->atts_changed = TRUE; + } else { + ULONG defda; + + fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE; + 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); + + fcb->atts_deleted = defda == fcb->atts; + } + + mark_fcb_dirty(fcb); + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED); + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&Vcb->tree_lock); + + return Status; +} + +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); + + 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; + + 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; + } + + le = le2; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { + FILE_ZERO_DATA_INFORMATION* fzdi = data; + NTSTATUS Status; + fcb* fcb; + ccb* ccb; + file_ref* fileref; + LIST_ENTRY rollback, changed_sector_list, *le; + LARGE_INTEGER time; + BTRFS_TIME now; + UINT64 start, end; + extent* ext; + BOOL nocsum; + IO_STATUS_BLOCK iosb; + + // FIXME - check permissions + + if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION)) + return STATUS_INVALID_PARAMETER; + + if (!FileObject) { + ERR("FileObject was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) { + WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart); + return STATUS_INVALID_PARAMETER; + } + + fcb = FileObject->FsContext; + + if (!fcb) { + ERR("FCB was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + ccb = FileObject->FsContext2; + fileref = ccb ? ccb->fileref : NULL; + + if (!fileref) { + ERR("fileref was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + InitializeListHead(&rollback); + + ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb); + + if (fcb->type != BTRFS_TYPE_FILE) { + WARN("FileObject did not point to a file\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (fcb->ads) { + ERR("FileObject is stream\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) { + Status = STATUS_SUCCESS; + goto end; + } + + ext = NULL; + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* ext2 = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext2->ignore) { + ext = ext2; + break; + } + + le = le->Flink; + } + + if (!ext) { + Status = STATUS_SUCCESS; + goto end; + } + + nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM; + + if (!nocsum) + InitializeListHead(&changed_sector_list); + + if (ext->datalen >= sizeof(EXTENT_DATA) && ext->data->type == EXTENT_TYPE_INLINE) { + Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, nocsum ? NULL : &changed_sector_list, Irp, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("zero_data returned %08x\n", Status); + goto end; + } + } else { + start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size); + + if (fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size) + end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size); + else + end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size; + + if (end <= start) { + Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, nocsum ? NULL : &changed_sector_list, Irp, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("zero_data returned %08x\n", Status); + goto end; + } + } else { + if (start > fzdi->FileOffset.QuadPart) { + Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, nocsum ? NULL : &changed_sector_list, Irp, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("zero_data returned %08x\n", Status); + goto end; + } + } + + if (end < fzdi->BeyondFinalZero.QuadPart) { + Status = zero_data(Vcb, fcb, fzdi->BeyondFinalZero.QuadPart, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback); + if (!NT_SUCCESS(Status)) { + ERR("zero_data returned %08x\n", Status); + goto end; + } + } + + if (end > start) { + Status = excise_extents(Vcb, fcb, start, end, &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; + } + } + } + } + + CcPurgeCacheSection(&fcb->nonpaged->segment_object, &fzdi->FileOffset, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, FALSE); + + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + fcb->inode_item.transid = Vcb->superblock.generation; + fcb->inode_item.sequence++; + fcb->inode_item.st_ctime = now; + fcb->inode_item.st_mtime = now; + + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED); + + fcb->subvol->root_item.ctransid = Vcb->superblock.generation; + fcb->subvol->root_item.ctime = now; + + Status = STATUS_SUCCESS; + + if (!nocsum) { + ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); + commit_checksum_changes(Vcb, &changed_sector_list); + ExReleaseResourceLite(&Vcb->checksum_lock); + } + +end: + if (!NT_SUCCESS(Status)) + do_rollback(Vcb, &rollback); + else + clear_rollback(&rollback); + + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&Vcb->tree_lock); + + return Status; +} + +static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, DWORD* retlen) { + NTSTATUS Status; + fcb* fcb; + LIST_ENTRY* le; + FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf; + ULONG i = 0; + BOOL sparse, finish_off; + UINT64 nonsparse_run_start; + + TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n"); + + if (!FileObject) { + ERR("FileObject was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf) + return STATUS_INVALID_PARAMETER; + + fcb = FileObject->FsContext; + + if (!fcb) { + ERR("FCB was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); + + // If file is not marked as sparse, claim the whole thing as an allocated range + + if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) { + if (fcb->inode_item.st_size == 0) + Status = STATUS_SUCCESS; + else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER)) + Status = STATUS_BUFFER_TOO_SMALL; + else { + ranges[i].FileOffset.QuadPart = 0; + ranges[i].Length.QuadPart = fcb->inode_item.st_size; + i++; + Status = STATUS_SUCCESS; + } + + goto end; + + } + + le = fcb->extents.Flink; + + sparse = FALSE; + nonsparse_run_start = 0xffffffffffffffff; + finish_off = TRUE; + + 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; + + 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 > inbuf->FileOffset.QuadPart + inbuf->Length.QuadPart) { + finish_off = FALSE; + break; + } + + sparse = TRUE; + } + } else { // not sparse + if (sparse) { + sparse = FALSE; + nonsparse_run_start = ext->offset; + } + } + } + + le = le->Flink; + } + + if (finish_off && nonsparse_run_start < fcb->inode_item.st_size) { + 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; + i++; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + goto end; + } + } + + Status = STATUS_SUCCESS; + +end: + *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER); + + ExReleaseResourceLite(fcb->Header.Resource); + + return Status; +} + +static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, DWORD* retlen) { + fcb* fcb; + + TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen); + + if (!FileObject) { + ERR("FileObject was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER)) + return STATUS_INVALID_PARAMETER; + + fcb = FileObject->FsContext; + + if (!fcb) { + ERR("FCB was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); + + RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(UINT64)); + RtlCopyMemory(&buf->ObjectId[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64)); + + ExReleaseResourceLite(fcb->Header.Resource); + + RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo)); + + *retlen = sizeof(FILE_OBJECTID_BUFFER); + + return STATUS_SUCCESS; +} + +static void flush_fcb_caches(device_extension* Vcb) { + LIST_ENTRY* le; + + le = Vcb->all_fcbs.Flink; + while (le != &Vcb->all_fcbs) { + struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all); + IO_STATUS_BLOCK iosb; + + if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted) + CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb); + + le = le->Flink; + } +} + +static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS Status; + LIST_ENTRY rollback; + KIRQL irql; + + TRACE("FSCTL_LOCK_VOLUME\n"); + + TRACE("locking volume\n"); + + FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK); + + if (Vcb->locked) + return STATUS_SUCCESS; + + ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE); + + if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->fcb->open_count > 0 || has_open_children(Vcb->root_fileref))) { + Status = STATUS_ACCESS_DENIED; + ExReleaseResourceLite(&Vcb->fcb_lock); + goto end; + } + + Vcb->locked = TRUE; + + ExReleaseResourceLite(&Vcb->fcb_lock); + + InitializeListHead(&rollback); + + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); + + flush_fcb_caches(Vcb); + + if (Vcb->need_write) + do_write(Vcb, &rollback); + + free_trees(Vcb); + + clear_rollback(&rollback); + + ExReleaseResourceLite(&Vcb->tree_lock); + + IoAcquireVpbSpinLock(&irql); + + if (!(Vcb->Vpb->Flags & VPB_LOCKED)) { + Vcb->Vpb->Flags |= VPB_LOCKED; + Vcb->locked_fileobj = IrpSp->FileObject; + } else { + Status = STATUS_ACCESS_DENIED; + IoReleaseVpbSpinLock(irql); + goto end; + } + + IoReleaseVpbSpinLock(irql); + + Status = STATUS_SUCCESS; + +end: + if (!NT_SUCCESS(Status)) + FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED); + + return Status; +} + +void do_unlock_volume(device_extension* Vcb) { + KIRQL irql; + + IoAcquireVpbSpinLock(&irql); + + Vcb->locked = FALSE; + Vcb->Vpb->Flags &= ~VPB_LOCKED; + Vcb->locked_fileobj = NULL; + + IoReleaseVpbSpinLock(irql); +} + +static NTSTATUS unlock_volume(device_extension* Vcb, PIRP Irp) { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + TRACE("FSCTL_UNLOCK_VOLUME\n"); + + if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj) + return STATUS_NOT_LOCKED; + + TRACE("unlocking volume\n"); + + do_unlock_volume(Vcb); + + FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK); + + return STATUS_SUCCESS; +} + +static NTSTATUS invalidate_volumes(PIRP Irp) { + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; + NTSTATUS Status; + HANDLE h; + PFILE_OBJECT fileobj; + PDEVICE_OBJECT devobj; + LIST_ENTRY* le; + + TRACE("FSCTL_INVALIDATE_VOLUMES\n"); + + if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) + return STATUS_PRIVILEGE_NOT_HELD; + +#if defined(_WIN64) + if (IoIs32bitProcess(Irp)) { + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) + return STATUS_INVALID_PARAMETER; + + h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer)); + } else { +#endif + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) + return STATUS_INVALID_PARAMETER; + + h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; +#if defined(_WIN64) + } +#endif + + Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, KernelMode, (void**)&fileobj, NULL); + + if (!NT_SUCCESS(Status)) { + ERR("ObReferenceObjectByHandle returned %08x\n", Status); + return Status; + } + + devobj = fileobj->DeviceObject; + ObDereferenceObject(fileobj); + + ExAcquireResourceSharedLite(&global_loading_lock, TRUE); + + le = VcbList.Flink; + + while (le != &VcbList) { + device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); + + if (Vcb->Vpb->RealDevice == devobj) { + if (Vcb->Vpb == devobj->Vpb) { + KIRQL irql; + PVPB newvpb; + BOOL free_newvpb = FALSE; + LIST_ENTRY rollback; + + newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG); + if (!newvpb) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlZeroMemory(newvpb, sizeof(VPB)); + + IoAcquireVpbSpinLock(&irql); + devobj->Vpb->Flags &= ~VPB_MOUNTED; + IoReleaseVpbSpinLock(irql); + + InitializeListHead(&rollback); + + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); + + Vcb->removing = TRUE; + + ExReleaseResourceLite(&Vcb->tree_lock); + + CcWaitForCurrentLazyWriterActivity(); + + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); + + flush_fcb_caches(Vcb); + + if (Vcb->need_write) + do_write(Vcb, &rollback); + + free_trees(Vcb); + + clear_rollback(&rollback); + + flush_fcb_caches(Vcb); + + ExReleaseResourceLite(&Vcb->tree_lock); + + IoAcquireVpbSpinLock(&irql); + + if (devobj->Vpb->Flags & VPB_MOUNTED) { + newvpb->Type = IO_TYPE_VPB; + newvpb->Size = sizeof(VPB); + newvpb->RealDevice = devobj; + newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING; + + devobj->Vpb = newvpb; + } else + free_newvpb = TRUE; + + IoReleaseVpbSpinLock(irql); + + if (free_newvpb) + ExFreePool(newvpb); + + uninit(Vcb, FALSE); + } + + break; + } + + le = le->Flink; + } + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(&global_loading_lock); + + return Status; +} + NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS Status; @@ -1223,13 +1932,11 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_LOCK_VOLUME: - WARN("STUB: FSCTL_LOCK_VOLUME\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = lock_volume(DeviceObject->DeviceExtension, Irp); break; case FSCTL_UNLOCK_VOLUME: - WARN("STUB: FSCTL_UNLOCK_VOLUME\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = unlock_volume(DeviceObject->DeviceExtension, Irp); break; case FSCTL_DISMOUNT_VOLUME: @@ -1277,8 +1984,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_INVALIDATE_VOLUMES: - WARN("STUB: FSCTL_INVALIDATE_VOLUMES\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = invalidate_volumes(Irp); break; case FSCTL_QUERY_FAT_BPB: @@ -1293,7 +1999,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL case FSCTL_FILESYSTEM_GET_STATISTICS: Status = fs_get_statistics(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, - IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information); + IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); break; case FSCTL_GET_NTFS_VOLUME_DATA: @@ -1342,8 +2048,8 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_GET_OBJECT_ID: - WARN("STUB: FSCTL_GET_OBJECT_ID\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, + IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); break; case FSCTL_DELETE_OBJECT_ID: @@ -1357,7 +2063,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL case FSCTL_GET_REPARSE_POINT: Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, - IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information); + IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); break; case FSCTL_DELETE_REPARSE_POINT: @@ -1385,23 +2091,24 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; case FSCTL_CREATE_OR_GET_OBJECT_ID: - WARN("STUB: FSCTL_CREATE_OR_GET_OBJECT_ID\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, + IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); break; case FSCTL_SET_SPARSE: - WARN("STUB: FSCTL_SET_SPARSE\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, + IrpSp->Parameters.FileSystemControl.InputBufferLength); break; case FSCTL_SET_ZERO_DATA: - WARN("STUB: FSCTL_SET_ZERO_DATA\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = set_zero_data(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, + IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); break; case FSCTL_QUERY_ALLOCATED_RANGES: - WARN("STUB: FSCTL_QUERY_ALLOCATED_RANGES\n"); - Status = STATUS_NOT_IMPLEMENTED; + Status = query_ranges(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer, + IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer, + IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); break; case FSCTL_ENABLE_UPGRADE: @@ -1661,15 +2368,15 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL break; #endif case FSCTL_BTRFS_GET_FILE_IDS: - Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength); + Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); break; case FSCTL_BTRFS_CREATE_SUBVOL: - Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength); + Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength); break; case FSCTL_BTRFS_CREATE_SNAPSHOT: - Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength); + Status = create_snapshot(DeviceObject->DeviceExtension, 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 e3f84f8078b..c63f097c096 100644 --- a/reactos/drivers/filesystems/btrfs/pnp.c +++ b/reactos/drivers/filesystems/btrfs/pnp.c @@ -174,14 +174,14 @@ static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) { InitializeListHead(&rollback); - acquire_tree_lock(Vcb, TRUE); + ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); - if (Vcb->write_trees > 0) + if (Vcb->need_write) do_write(Vcb, &rollback); clear_rollback(&rollback); - release_tree_lock(Vcb, TRUE); + ExReleaseResourceLite(&Vcb->tree_lock); Status = STATUS_SUCCESS; end: @@ -200,8 +200,13 @@ static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { + Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT); + if (!NT_SUCCESS(Status)) { + WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status); + } + uninit(Vcb, FALSE); - DeviceObject->Vpb->Flags &= ~VPB_MOUNTED; + Vcb->Vpb->Flags &= ~VPB_MOUNTED; } return STATUS_SUCCESS; @@ -229,6 +234,11 @@ NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto end; + } + Status = STATUS_NOT_IMPLEMENTED; switch (IrpSp->MinorFunction) { diff --git a/reactos/drivers/filesystems/btrfs/read.c b/reactos/drivers/filesystems/btrfs/read.c index 9cbe2043a47..3d50d0a5443 100644 --- a/reactos/drivers/filesystems/btrfs/read.c +++ b/reactos/drivers/filesystems/btrfs/read.c @@ -15,6 +15,7 @@ struct read_data_context; typedef struct { struct read_data_context* context; UINT8* buf; + UINT16 stripenum; PIRP Irp; IO_STATUS_BLOCK iosb; enum read_data_status status; @@ -26,7 +27,12 @@ typedef struct { chunk* c; UINT32 buflen; UINT64 num_stripes; + LONG stripes_left; UINT64 type; + UINT32 sector_size; + UINT16 firstoff, startoffstripe, sectors_per_stripe; + UINT32* csum; + BOOL tree; read_data_stripe* stripes; } read_data_context; @@ -34,7 +40,8 @@ static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP I read_data_stripe* stripe = conptr; read_data_context* context = (read_data_context*)stripe->context; UINT64 i; - BOOL complete; + + // FIXME - we definitely need a per-stripe lock here if (stripe->status == ReadDataStatus_Cancelling) { stripe->status = ReadDataStatus_Cancelled; @@ -44,14 +51,89 @@ static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP I stripe->iosb = Irp->IoStatus; if (NT_SUCCESS(Irp->IoStatus.Status)) { - // FIXME - calculate and compare checksum - - stripe->status = ReadDataStatus_Success; + if (context->type == BLOCK_FLAG_DUPLICATE) { + if (context->tree) { + tree_header* th = (tree_header*)stripe->buf; + UINT32 crc32; + + crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum)); + + if (crc32 != *((UINT32*)th->csum)) + stripe->status = ReadDataStatus_CRCError; + } else if (context->csum) { + for (i = 0; i < Irp->IoStatus.Information / context->sector_size; i++) { + UINT32 crc32 = ~calc_crc32c(0xffffffff, stripe->buf + (i * context->sector_size), context->sector_size); + + if (crc32 != context->csum[i]) { + stripe->status = ReadDataStatus_CRCError; + goto end; + } + } + } - for (i = 0; i < context->num_stripes; i++) { - if (context->stripes[i].status == ReadDataStatus_Pending) { - context->stripes[i].status = ReadDataStatus_Cancelling; - IoCancelIrp(context->stripes[i].Irp); + stripe->status = ReadDataStatus_Success; + + for (i = 0; i < context->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_Pending) { + context->stripes[i].status = ReadDataStatus_Cancelling; + IoCancelIrp(context->stripes[i].Irp); + } + } + } else if (context->type == BLOCK_FLAG_RAID0) { + // no point checking the checksum here, as there's nothing we can do + stripe->status = ReadDataStatus_Success; + } else if (context->type == BLOCK_FLAG_RAID10) { + if (context->csum) { + UINT16 start, left; + UINT32 j; + + if (context->startoffstripe == stripe->stripenum) { + start = 0; + left = context->sectors_per_stripe - context->firstoff; + } else { + UINT16 ns; + + if (context->startoffstripe > stripe->stripenum) { + ns = stripe->stripenum + (context->num_stripes / 2) - context->startoffstripe; + } else { + ns = stripe->stripenum - context->startoffstripe; + } + + if (context->firstoff == 0) + start = context->sectors_per_stripe * ns; + else + start = (context->sectors_per_stripe - context->firstoff) + (context->sectors_per_stripe * (ns - 1)); + + left = context->sectors_per_stripe; + } + + j = start; + for (i = 0; i < Irp->IoStatus.Information / context->sector_size; i++) { + UINT32 crc32 = ~calc_crc32c(0xffffffff, stripe->buf + (i * context->sector_size), context->sector_size); + + if (crc32 != context->csum[j]) { + int3; + stripe->status = ReadDataStatus_CRCError; + goto end; + } + + j++; + left--; + + if (left == 0) { + j += context->sectors_per_stripe; + left = context->sectors_per_stripe; + } + } + } + + stripe->status = ReadDataStatus_Success; + + for (i = 0; i < context->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_Pending && context->stripes[i].stripenum == stripe->stripenum) { + context->stripes[i].status = ReadDataStatus_Cancelling; + IoCancelIrp(context->stripes[i].Irp); + } } } @@ -61,30 +143,21 @@ static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP I } end: - complete = TRUE; - - for (i = 0; i < context->num_stripes; i++) { - if (context->stripes[i].status == ReadDataStatus_Pending || context->stripes[i].status == ReadDataStatus_Cancelling) { - complete = FALSE; - break; - } - } - - if (complete) + if (InterlockedDecrement(&context->stripes_left) == 0) KeSetEvent(&context->Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } -static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT8* buf) { +NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk** pc, PIRP Irp) { CHUNK_ITEM* ci; CHUNK_ITEM_STRIPE* cis; read_data_context* context; - UINT64 i/*, type*/, offset; + UINT64 i, type, offset; NTSTATUS Status; device** devices; - - // FIXME - make this work with RAID + UINT64 *stripestart = NULL, *stripeend = NULL; + UINT16 startoffstripe; if (Vcb->log_to_phys_loaded) { chunk* c = get_chunk_from_address(Vcb, addr); @@ -97,28 +170,68 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len ci = c->chunk_item; offset = c->offset; devices = c->devices; + + if (pc) + *pc = c; + } else { + LIST_ENTRY* le = Vcb->sys_chunks.Flink; + + ci = NULL; + + while (le != &Vcb->sys_chunks) { + sys_chunk* sc = CONTAINING_RECORD(le, sys_chunk, list_entry); + + if (sc->key.obj_id == 0x100 && sc->key.obj_type == TYPE_CHUNK_ITEM && sc->key.offset <= addr) { + CHUNK_ITEM* chunk_item = sc->data; + + if ((addr - sc->key.offset) < chunk_item->size && chunk_item->num_stripes > 0) { + ci = chunk_item; + offset = sc->key.offset; + cis = (CHUNK_ITEM_STRIPE*)&chunk_item[1]; + + devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * ci->num_stripes, ALLOC_TAG); + if (!devices) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for (i = 0; i < ci->num_stripes; i++) { + devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid); + } + + break; + } + } + + le = le->Flink; + } + + if (!ci) { + ERR("could not find chunk for %llx in bootstrap\n", addr); + return STATUS_INTERNAL_ERROR; + } + + if (pc) + *pc = NULL; } -// if (ci->type & BLOCK_FLAG_DUPLICATE) { -// type = BLOCK_FLAG_DUPLICATE; -// } else if (ci->type & BLOCK_FLAG_RAID0) { -// FIXME("RAID0 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID1) { -// FIXME("RAID1 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID10) { -// FIXME("RAID10 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID5) { -// FIXME("RAID5 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID6) { -// FIXME("RAID6 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else { // SINGLE -// type = 0; -// } + if (ci->type & BLOCK_FLAG_DUPLICATE) { + type = BLOCK_FLAG_DUPLICATE; + } else if (ci->type & BLOCK_FLAG_RAID0) { + type = BLOCK_FLAG_RAID0; + } else if (ci->type & BLOCK_FLAG_RAID1) { + type = BLOCK_FLAG_DUPLICATE; + } else if (ci->type & BLOCK_FLAG_RAID10) { + type = BLOCK_FLAG_RAID10; + } else if (ci->type & BLOCK_FLAG_RAID5) { + FIXME("RAID5 not yet supported\n"); + return STATUS_NOT_IMPLEMENTED; + } else if (ci->type & BLOCK_FLAG_RAID6) { + FIXME("RAID6 not yet supported\n"); + return STATUS_NOT_IMPLEMENTED; + } else { // SINGLE + type = BLOCK_FLAG_DUPLICATE; + } cis = (CHUNK_ITEM_STRIPE*)&ci[1]; @@ -134,6 +247,7 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG); if (!context->stripes) { ERR("out of memory\n"); + ExFreePool(context); return STATUS_INSUFFICIENT_RESOURCES; } @@ -141,32 +255,139 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len context->buflen = length; context->num_stripes = ci->num_stripes; -// context->type = type; + context->stripes_left = context->num_stripes; + context->sector_size = Vcb->superblock.sector_size; + context->csum = csum; + context->tree = is_tree; + context->type = type; + + stripestart = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * ci->num_stripes, ALLOC_TAG); + if (!stripestart) { + ERR("out of memory\n"); + ExFreePool(context); + return STATUS_INSUFFICIENT_RESOURCES; + } + + stripeend = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * ci->num_stripes, ALLOC_TAG); + if (!stripeend) { + ERR("out of memory\n"); + ExFreePool(stripestart); + ExFreePool(context); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (type == BLOCK_FLAG_RAID0) { + UINT64 startoff, endoff; + UINT16 endoffstripe; + + get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes, &startoff, &startoffstripe); + get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes, &endoff, &endoffstripe); + + for (i = 0; i < ci->num_stripes; i++) { + if (startoffstripe > i) { + stripestart[i] = startoff - (startoff % ci->stripe_length) + ci->stripe_length; + } else if (startoffstripe == i) { + stripestart[i] = startoff; + } else { + stripestart[i] = startoff - (startoff % ci->stripe_length); + } + + if (endoffstripe > i) { + stripeend[i] = endoff - (endoff % ci->stripe_length) + ci->stripe_length; + } else if (endoffstripe == i) { + stripeend[i] = endoff + 1; + } else { + stripeend[i] = endoff - (endoff % ci->stripe_length); + } + } + } else if (type == BLOCK_FLAG_RAID10) { + UINT64 startoff, endoff; + UINT16 endoffstripe, j; + + get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes / ci->sub_stripes, &startoff, &startoffstripe); + get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes / ci->sub_stripes, &endoff, &endoffstripe); + + if ((ci->num_stripes % ci->sub_stripes) != 0) { + ERR("chunk %llx: num_stripes %x was not a multiple of sub_stripes %x!\n", offset, ci->num_stripes, ci->sub_stripes); + Status = STATUS_INTERNAL_ERROR; + goto exit; + } + + context->firstoff = (startoff % ci->stripe_length) / Vcb->superblock.sector_size; + context->startoffstripe = startoffstripe; + context->sectors_per_stripe = ci->stripe_length / Vcb->superblock.sector_size; + + startoffstripe *= ci->sub_stripes; + endoffstripe *= ci->sub_stripes; + + for (i = 0; i < ci->num_stripes; i += ci->sub_stripes) { + if (startoffstripe > i) { + stripestart[i] = startoff - (startoff % ci->stripe_length) + ci->stripe_length; + } else if (startoffstripe == i) { + stripestart[i] = startoff; + } else { + stripestart[i] = startoff - (startoff % ci->stripe_length); + } + + if (endoffstripe > i) { + stripeend[i] = endoff - (endoff % ci->stripe_length) + ci->stripe_length; + } else if (endoffstripe == i) { + stripeend[i] = endoff + 1; + } else { + stripeend[i] = endoff - (endoff % ci->stripe_length); + } + + for (j = 1; j < ci->sub_stripes; j++) { + stripestart[i+j] = stripestart[i]; + stripeend[i+j] = stripeend[i]; + } + } + } else if (type == BLOCK_FLAG_DUPLICATE) { + for (i = 0; i < ci->num_stripes; i++) { + stripestart[i] = addr - offset; + stripeend[i] = stripestart[i] + length; + } + } // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request for (i = 0; i < ci->num_stripes; i++) { PIO_STACK_LOCATION IrpSp; - if (!devices[i]) { + if (!devices[i] || stripestart[i] == stripeend[i]) { context->stripes[i].status = ReadDataStatus_MissingDevice; context->stripes[i].buf = NULL; + context->stripes_left--; } else { context->stripes[i].context = (struct read_data_context*)context; - context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); + context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG); if (!context->stripes[i].buf) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } - - context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE); - if (!context->stripes[i].Irp) { - ERR("IoAllocateIrp failed\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; + if (type == BLOCK_FLAG_RAID10) { + context->stripes[i].stripenum = i / ci->sub_stripes; + } + + if (!Irp) { + context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE); + + if (!context->stripes[i].Irp) { + ERR("IoAllocateIrp failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + } else { + context->stripes[i].Irp = IoMakeAssociatedIrp(Irp, devices[i]->devobj->StackSize); + + if (!context->stripes[i].Irp) { + ERR("IoMakeAssociatedIrp failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } } IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp); @@ -175,7 +396,7 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len if (devices[i]->devobj->Flags & DO_BUFFERED_IO) { FIXME("FIXME - buffered IO\n"); } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) { - context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, length, FALSE, FALSE, NULL); + context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, stripeend[i] - stripestart[i], FALSE, FALSE, NULL); if (!context->stripes[i].Irp->MdlAddress) { ERR("IoAllocateMdl failed\n"); Status = STATUS_INSUFFICIENT_RESOURCES; @@ -187,8 +408,8 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len context->stripes[i].Irp->UserBuffer = context->stripes[i].buf; } - IrpSp->Parameters.Read.Length = length; - IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset; + IrpSp->Parameters.Read.Length = stripeend[i] - stripestart[i]; + IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[i] + cis[i].offset; context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb; @@ -208,41 +429,223 @@ static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 len // FIXME - if checksum error, write good data over bad - // check if any of the stripes succeeded + // check if any of the devices return a "user-induced" error for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadDataStatus_Success) { - RtlCopyMemory(buf, context->stripes[i].buf, length); - Status = STATUS_SUCCESS; - goto exit; - } - } - - // if not, see if we got a checksum error - -// for (i = 0; i < ci->num_stripes; i++) { -// if (context->stripes[i].status == ReadDataStatus_CRCError) { -// WARN("stripe %llu had a checksum error\n", i); -// -// Status = STATUS_IMAGE_CHECKSUM_MISMATCH; -// goto exit; -// } -// } - - // failing that, return the first error we encountered - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadDataStatus_Error) { + if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) { + IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj); + Status = context->stripes[i].iosb.Status; goto exit; } } - // if we somehow get here, return STATUS_INTERNAL_ERROR - - Status = STATUS_INTERNAL_ERROR; + if (type == BLOCK_FLAG_RAID0) { + UINT32 pos, *stripeoff; + UINT8 stripe; + + for (i = 0; i < ci->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_Error) { + WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status); + Status = context->stripes[i].iosb.Status; + goto exit; + } + } + + pos = 0; + stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * ci->num_stripes, ALLOC_TAG); + if (!stripeoff) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes); + + stripe = startoffstripe; + while (pos < length) { + if (pos == 0) { + UINT32 readlen = min(stripeend[stripe] - stripestart[stripe], ci->stripe_length - (stripestart[stripe] % ci->stripe_length)); + + RtlCopyMemory(buf, context->stripes[stripe].buf, readlen); + stripeoff[stripe] += readlen; + pos += readlen; + } else if (length - pos < ci->stripe_length) { + RtlCopyMemory(buf + pos, &context->stripes[stripe].buf[stripeoff[stripe]], length - pos); + pos = length; + } else { + RtlCopyMemory(buf + pos, &context->stripes[stripe].buf[stripeoff[stripe]], ci->stripe_length); + stripeoff[stripe] += ci->stripe_length; + pos += ci->stripe_length; + } + + stripe = (stripe + 1) % ci->num_stripes; + } + + ExFreePool(stripeoff); + + // FIXME - handle the case where one of the stripes doesn't read everything, i.e. Irp->IoStatus.Information is short + + if (is_tree) { // shouldn't happen, as trees shouldn't cross stripe boundaries + tree_header* th = (tree_header*)buf; + UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); + + if (crc32 != *((UINT32*)th->csum)) { + WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum)); + Status = STATUS_CRC_ERROR; + goto exit; + } + } else if (csum) { + for (i = 0; i < length / Vcb->superblock.sector_size; i++) { + UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size); + + if (crc32 != csum[i]) { + WARN("checksum error (%08x != %08x)\n", crc32, csum[i]); + Status = STATUS_CRC_ERROR; + goto exit; + } + } + } + + Status = STATUS_SUCCESS; + } else if (type == BLOCK_FLAG_RAID10) { + UINT32 pos, *stripeoff; + UINT8 stripe; + read_data_stripe** stripes; + + stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG); + if (!stripes) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + RtlZeroMemory(stripes, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes); + + for (i = 0; i < ci->num_stripes; i += ci->sub_stripes) { + UINT16 j; + + for (j = 0; j < ci->sub_stripes; j++) { + if (context->stripes[i+j].status == ReadDataStatus_Success) { + stripes[i / ci->sub_stripes] = &context->stripes[i+j]; + break; + } + } + + if (!stripes[i / ci->sub_stripes]) { + for (j = 0; j < ci->sub_stripes; j++) { + if (context->stripes[i+j].status == ReadDataStatus_CRCError) { + WARN("stripe %llu had a checksum error\n", i+j); + Status = STATUS_CRC_ERROR; + goto exit; + } + } + + for (j = 0; j < ci->sub_stripes; j++) { + if (context->stripes[i+j].status == ReadDataStatus_Error) { + WARN("stripe %llu returned error %08x\n", i+j, context->stripes[i+j].iosb.Status); + Status = context->stripes[i].iosb.Status; + goto exit; + } + } + } + } + + pos = 0; + stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG); + if (!stripeoff) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes); + + stripe = startoffstripe / ci->sub_stripes; + while (pos < length) { + if (pos == 0) { + UINT32 readlen = min(stripeend[stripe * ci->sub_stripes] - stripestart[stripe * ci->sub_stripes], ci->stripe_length - (stripestart[stripe * ci->sub_stripes] % ci->stripe_length)); + + RtlCopyMemory(buf, stripes[stripe]->buf, readlen); + stripeoff[stripe] += readlen; + pos += readlen; + } else if (length - pos < ci->stripe_length) { + RtlCopyMemory(buf + pos, &stripes[stripe]->buf[stripeoff[stripe]], length - pos); + pos = length; + } else { + RtlCopyMemory(buf + pos, &stripes[stripe]->buf[stripeoff[stripe]], ci->stripe_length); + stripeoff[stripe] += ci->stripe_length; + pos += ci->stripe_length; + } + + stripe = (stripe + 1) % (ci->num_stripes / ci->sub_stripes); + } + + ExFreePool(stripes); + ExFreePool(stripeoff); + + // FIXME - handle the case where one of the stripes doesn't read everything, i.e. Irp->IoStatus.Information is short + + if (is_tree) { + tree_header* th = (tree_header*)buf; + UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); + + if (crc32 != *((UINT32*)th->csum)) { + WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum)); + Status = STATUS_CRC_ERROR; + goto exit; + } + } + + Status = STATUS_SUCCESS; + } else if (type == BLOCK_FLAG_DUPLICATE) { + // check if any of the stripes succeeded + + for (i = 0; i < ci->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_Success) { + RtlCopyMemory(buf, context->stripes[i].buf, length); + Status = STATUS_SUCCESS; + goto exit; + } + } + + // if not, see if we got a checksum error + + for (i = 0; i < ci->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_CRCError) { +#ifdef _DEBUG + WARN("stripe %llu had a checksum error\n", i); + + if (context->tree) { + tree_header* th = (tree_header*)context->stripes[i].buf; + UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum)); + + WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum)); + } +#endif + + Status = STATUS_CRC_ERROR; + goto exit; + } + } + + // failing that, return the first error we encountered + + for (i = 0; i < ci->num_stripes; i++) { + if (context->stripes[i].status == ReadDataStatus_Error) { + Status = context->stripes[i].iosb.Status; + goto exit; + } + } + + // if we somehow get here, return STATUS_INTERNAL_ERROR + + Status = STATUS_INTERNAL_ERROR; + } exit: + if (stripestart) ExFreePool(stripestart); + if (stripeend) ExFreePool(stripeend); for (i = 0; i < ci->num_stripes; i++) { if (context->stripes[i].Irp) { @@ -259,13 +662,14 @@ exit: ExFreePool(context->stripes); ExFreePool(context); + + if (!Vcb->log_to_phys_loaded) + ExFreePool(devices); return Status; } static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG length, ULONG* pbr) { - UINT8* xattrdata; - UINT16 xattrlen; ULONG readlen; NTSTATUS Status; @@ -273,239 +677,404 @@ static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG l if (pbr) *pbr = 0; - if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &xattrdata, &xattrlen)) { - ERR("get_xattr failed\n"); - return STATUS_OBJECT_NAME_NOT_FOUND; - } - - if (start >= xattrlen) { + if (start >= fcb->adsdata.Length) { TRACE("tried to read beyond end of stream\n"); - Status = STATUS_END_OF_FILE; - goto end; + return STATUS_END_OF_FILE; } if (length == 0) { WARN("tried to read zero bytes\n"); - Status = STATUS_SUCCESS; - goto end; + return STATUS_SUCCESS; } - if (start + length < xattrlen) + if (start + length < fcb->adsdata.Length) readlen = length; else - readlen = (ULONG)xattrlen - (ULONG)start; + readlen = fcb->adsdata.Length - (ULONG)start; - RtlCopyMemory(data + start, xattrdata, readlen); + if (readlen > 0) + RtlCopyMemory(data + start, fcb->adsdata.Buffer, readlen); if (pbr) *pbr = readlen; Status = STATUS_SUCCESS; - -end: - ExFreePool(xattrdata); - + return Status; } -NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr) { - KEY searchkey; +static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length) { NTSTATUS Status; + KEY searchkey; traverse_ptr tp, next_tp; + UINT64 i, j; + BOOL b; + + searchkey.obj_id = EXTENT_CSUM_ID; + searchkey.obj_type = TYPE_EXTENT_CSUM; + searchkey.offset = start; + + Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + i = 0; + do { + if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { + ULONG readlen; + + if (start < tp.item->key.offset) + j = 0; + else + j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i; + + if (j * sizeof(UINT32) > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) { + ERR("checksum not found for %llx\n", start + (i * Vcb->superblock.sector_size)); + return STATUS_INTERNAL_ERROR; + } + + readlen = min((tp.item->size / sizeof(UINT32)) - j, length - i); + RtlCopyMemory(&csum[i], tp.item->data + (j * sizeof(UINT32)), readlen * sizeof(UINT32)); + i += readlen; + + if (i == length) + break; + } + + b = find_next_item(Vcb, &tp, &next_tp, FALSE); + + if (b) + tp = next_tp; + } while (b); + + if (i < length) { + ERR("could not read checksums: offset %llx, length %llx sectors\n", start, length); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum) { + UINT32* csum = NULL; + NTSTATUS Status; + UINT64 end; + RTL_BITMAP bmp; + ULONG *bmpbuf = NULL, bmpbuflen, index, runlength; + LIST_ENTRY* le; + + if (length == 0) { + *pcsum = NULL; + return STATUS_SUCCESS; + } + + bmpbuflen = sector_align(length, sizeof(ULONG) * 8) / 8; + bmpbuf = ExAllocatePoolWithTag(PagedPool, bmpbuflen, ALLOC_TAG); + if (!bmpbuf) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + RtlInitializeBitMap(&bmp, bmpbuf, length); + RtlClearAllBits(&bmp); + + csum = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * length, ALLOC_TAG); + if (!csum) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + ExAcquireResourceSharedLite(&Vcb->checksum_lock, TRUE); + + end = start + (length * Vcb->superblock.sector_size); + + le = Vcb->sector_checksums.Flink; + while (le != &Vcb->sector_checksums) { + changed_sector* cs = (changed_sector*)le; + UINT64 cs_end = cs->ol.key + (cs->length * Vcb->superblock.sector_size); + + if (cs->ol.key <= start && cs_end >= end) { // outer + if (cs->deleted) { + RtlClearAllBits(&bmp); + } else { + RtlSetAllBits(&bmp); + RtlCopyMemory(csum, &cs->checksums[(start - cs->ol.key) / Vcb->superblock.sector_size], sizeof(UINT32) * length); + } + } else if (cs->ol.key >= start && cs->ol.key <= end) { // right or inner + if (cs->deleted) { + RtlClearBits(&bmp, (cs->ol.key - start) / Vcb->superblock.sector_size, (min(end, cs_end) - cs->ol.key) / Vcb->superblock.sector_size); + } else { + RtlSetBits(&bmp, (cs->ol.key - start) / Vcb->superblock.sector_size, (min(end, cs_end) - cs->ol.key) / Vcb->superblock.sector_size); + RtlCopyMemory(&csum[(cs->ol.key - start) / Vcb->superblock.sector_size], cs->checksums, (min(end, cs_end) - cs->ol.key) * sizeof(UINT32) / Vcb->superblock.sector_size); + } + } else if (cs_end >= start && cs_end <= end) { // left + if (cs->deleted) { + RtlClearBits(&bmp, 0, (cs_end - start) / Vcb->superblock.sector_size); + } else { + RtlSetBits(&bmp, 0, (cs_end - start) / Vcb->superblock.sector_size); + RtlCopyMemory(csum, &cs->checksums[(start - cs->ol.key) / Vcb->superblock.sector_size], (cs_end - start) * sizeof(UINT32) / Vcb->superblock.sector_size); + } + } + + le = le->Flink; + } + + ExReleaseResourceLite(&Vcb->checksum_lock); + + runlength = RtlFindFirstRunClear(&bmp, &index); + + while (runlength != 0) { + Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength); + if (!NT_SUCCESS(Status)) { + ERR("load_csum_from_disk returned %08x\n", Status); + goto end; + } + + runlength = RtlFindNextForwardRunClear(&bmp, index + runlength, &index); + } + + Status = STATUS_SUCCESS; + +end: + if (bmpbuf) + ExFreePool(bmpbuf); + + if (NT_SUCCESS(Status)) + *pcsum = csum; + else if (csum) + ExFreePool(csum); + + return Status; +} + +NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp) { + NTSTATUS Status; EXTENT_DATA* ed; UINT64 bytes_read = 0; + LIST_ENTRY* le; - TRACE("(%p, %llx, %llx, %p, %llx, %llx, %p)\n", Vcb, subvol->id, inode, data, start, length, pbr); + TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr); if (pbr) *pbr = 0; - - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start; - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto exit; - } - - if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) { - if (find_next_item(Vcb, &tp, &next_tp, FALSE)) { - 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); - } - } + le = fcb->extents.Flink; - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - if (tp.item->key.offset > start) { - ERR("first EXTENT_DATA was after offset\n"); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - -// while (TRUE) { -// BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE); -// -// if (!foundnext || next_tp.item->key.obj_id != inode || -// next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset > start) { -// if (foundnext) -// free_traverse_ptr(&next_tp); -// -// break; -// } -// -// free_traverse_ptr(&tp); -// tp = next_tp; -// } - - do { - UINT64 len; - EXTENT_DATA2* ed2; + while (le != &fcb->extents) { + extent* ext = CONTAINING_RECORD(le, extent, list_entry); - ed = (EXTENT_DATA*)tp.item->data; - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - Status = STATUS_INTERNAL_ERROR; - goto exit; - } - - ed2 = (EXTENT_DATA2*)ed->data; - - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - if (tp.item->key.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 (ed->encryption != BTRFS_ENCRYPTION_NONE) { - WARN("Encryption not supported\n"); - Status = STATUS_NOT_IMPLEMENTED; - goto exit; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("Other encodings not supported\n"); - Status = STATUS_NOT_IMPLEMENTED; - goto exit; - } - - switch (ed->type) { - case EXTENT_TYPE_INLINE: - { - UINT64 off = start + bytes_read - tp.item->key.offset; - UINT64 read = len - off; - - if (read > length) read = length; - - RtlCopyMemory(data + bytes_read, &ed->data[off], read); - - bytes_read += read; - length -= read; + if (!ext->ignore) { + if (ext->offset == start) break; - } - - case EXTENT_TYPE_REGULAR: - { - UINT64 off = start + bytes_read - tp.item->key.offset; - UINT32 to_read, read; - UINT8* buf; + else if (ext->offset > start) { + LIST_ENTRY* le2 = le->Blink; - read = len - off; - if (read > length) read = length; + ext = NULL; - if (ed2->address == 0) { - RtlZeroMemory(data + bytes_read, read); - } else { - to_read = sector_align(read, Vcb->superblock.sector_size); + while (le2 != &fcb->extents) { + extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry); - buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG); - - if (!buf) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; + if (!ext2->ignore) { + le = le2; + ext = ext2; + break; } - // FIXME - load checksums - - Status = read_data(Vcb, ed2->address + ed2->offset + off, to_read, buf); - if (!NT_SUCCESS(Status)) { - ERR("read_data returned %08x\n", Status); - ExFreePool(buf); - goto exit; - } - - RtlCopyMemory(data + bytes_read, buf, read); - - ExFreePool(buf); + le2 = le2->Blink; } - bytes_read += read; - length -= read; - - break; + if (!ext) { + ERR("first extent was after offset\n"); + Status = STATUS_INTERNAL_ERROR; + goto exit; + } else + break; } - - case EXTENT_TYPE_PREALLOC: - { - UINT64 off = start + bytes_read - tp.item->key.offset; - UINT32 read = len - off; - - if (read > length) read = length; - - RtlZeroMemory(data + bytes_read, read); - - bytes_read += read; - length -= read; - - break; - } - - default: - WARN("Unsupported extent data type %u\n", ed->type); - Status = STATUS_NOT_IMPLEMENTED; - goto exit; } - if (length > 0) { - BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, FALSE); + 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 (!foundnext) + if (!ext2->ignore) { + le = le2; + ext = ext2; break; - else if (next_tp.item->key.obj_id != inode || - next_tp.item->key.obj_type != TYPE_EXTENT_DATA || - next_tp.item->key.offset != tp.item->key.offset + len - ) { - break; - } else { - TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset); - - tp = next_tp; } - } else - break; - } while (TRUE); + + le2 = le2->Blink; + } + + if (!ext) { + ERR("could not find extent\n"); + Status = STATUS_INTERNAL_ERROR; + goto exit; + } + } + + while (le != &fcb->extents) { + UINT64 len; + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + EXTENT_DATA2* ed2; + + 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; + } + + 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; + } + + 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 (ed->encryption != BTRFS_ENCRYPTION_NONE) { + WARN("Encryption not supported\n"); + Status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + + if (ed->encoding != BTRFS_ENCODING_NONE) { + WARN("Other encodings not supported\n"); + Status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + + switch (ed->type) { + case EXTENT_TYPE_INLINE: + { + UINT64 off = start + bytes_read - ext->offset; + UINT64 read = len - off; + + if (read > length) read = length; + + RtlCopyMemory(data + bytes_read, &ed->data[off], read); + + bytes_read += read; + length -= read; + break; + } + + case EXTENT_TYPE_REGULAR: + { + UINT64 off = start + bytes_read - ext->offset; + UINT32 to_read, read; + UINT8* buf; + + read = len - off; + if (read > length) read = length; + + if (ed2->address == 0) { + RtlZeroMemory(data + bytes_read, read); + } else { + UINT32 *csum, bumpoff = 0; + UINT64 addr; + + addr = ed2->address + ed2->offset + off; + to_read = sector_align(read, fcb->Vcb->superblock.sector_size); + + if (addr % fcb->Vcb->superblock.sector_size > 0) { + bumpoff = addr % fcb->Vcb->superblock.sector_size; + addr -= bumpoff; + to_read = sector_align(read + bumpoff, 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); + + 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; + } + + RtlCopyMemory(data + bytes_read, buf + bumpoff, read); + + ExFreePool(buf); + + if (csum) + ExFreePool(csum); + } + + bytes_read += read; + length -= read; + + break; + } + + case EXTENT_TYPE_PREALLOC: + { + UINT64 off = start + bytes_read - ext->offset; + UINT32 read = len - off; + + if (read > length) read = length; + + RtlZeroMemory(data + bytes_read, read); + + bytes_read += read; + length -= read; + + break; + } + + default: + WARN("Unsupported extent data type %u\n", ed->type); + Status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + + if (length == 0) + break; + } + + le = le->Flink; + } Status = STATUS_SUCCESS; if (pbr) @@ -515,68 +1084,39 @@ exit: return Status; } -NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { +NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - UINT8* data; PFILE_OBJECT FileObject = IrpSp->FileObject; fcb* fcb = FileObject->FsContext; - UINT64 start; - ULONG length, bytes_read; - NTSTATUS Status; - BOOL top_level; - - FsRtlEnterFileSystem(); - - top_level = is_top_level(Irp); - - TRACE("read\n"); - - Irp->IoStatus.Information = 0; - - if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { - CcMdlReadComplete(IrpSp->FileObject, Irp->MdlAddress); - - Irp->MdlAddress = NULL; - Status = STATUS_SUCCESS; - bytes_read = 0; - - goto exit; - } - - start = IrpSp->Parameters.Read.ByteOffset.QuadPart; + UINT8* data; + ULONG length, addon = 0; + UINT64 start = IrpSp->Parameters.Read.ByteOffset.QuadPart; length = IrpSp->Parameters.Read.Length; - bytes_read = 0; + *bytes_read = 0; - if (!fcb || !fcb->Vcb || !fcb->subvol) { - Status = STATUS_INTERNAL_ERROR; // FIXME - invalid param error? - goto exit; - } + if (!fcb || !fcb->Vcb || !fcb->subvol) + return STATUS_INTERNAL_ERROR; TRACE("file = %S (fcb = %p)\n", file_desc(FileObject), fcb); TRACE("offset = %llx, length = %x\n", start, length); TRACE("paging_io = %s, no cache = %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE", Irp->Flags & IRP_NOCACHE ? "TRUE" : "FALSE"); - if (fcb->type == BTRFS_TYPE_DIRECTORY) { - Status = STATUS_INVALID_DEVICE_REQUEST; - goto exit; - } + if (fcb->type == BTRFS_TYPE_DIRECTORY) + return STATUS_INVALID_DEVICE_REQUEST; if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) { WARN("tried to read locked region\n"); - Status = STATUS_FILE_LOCK_CONFLICT; - goto exit; + return STATUS_FILE_LOCK_CONFLICT; } if (length == 0) { - WARN("tried to read zero bytes\n"); - Status = STATUS_SUCCESS; - goto exit; + TRACE("tried to read zero bytes\n"); + return STATUS_SUCCESS; } if (start >= fcb->Header.FileSize.QuadPart) { TRACE("tried to read with offset after file end (%llx >= %llx)\n", start, fcb->Header.FileSize.QuadPart); - Status = STATUS_END_OF_FILE; - goto exit; + return STATUS_END_OF_FILE; } TRACE("FileObject %p fcb %p FileSize = %llx st_size = %llx (%p)\n", FileObject, fcb, fcb->Header.FileSize.QuadPart, fcb->inode_item.st_size, &fcb->inode_item.st_size); @@ -587,20 +1127,25 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (Irp->MdlAddress && !data) { ERR("MmGetSystemAddressForMdlSafe returned NULL\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (start >= fcb->Header.ValidDataLength.QuadPart) { + length = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart; + RtlZeroMemory(data, length); + Irp->IoStatus.Information = *bytes_read = length; + return STATUS_SUCCESS; } if (length + start > fcb->Header.ValidDataLength.QuadPart) { - RtlZeroMemory(data + (fcb->Header.ValidDataLength.QuadPart - start), length - (fcb->Header.ValidDataLength.QuadPart - start)); + addon = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart; + RtlZeroMemory(data + (fcb->Header.ValidDataLength.QuadPart - start), addon); length = fcb->Header.ValidDataLength.QuadPart - start; } } if (!(Irp->Flags & IRP_NOCACHE)) { - BOOL wait; - - Status = STATUS_SUCCESS; + NTSTATUS Status = STATUS_SUCCESS; _SEH2_TRY { if (!FileObject->PrivateCacheMap) { @@ -617,21 +1162,16 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY); } - // FIXME - uncomment this when async is working - // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE; - wait = TRUE; - if (IrpSp->MinorFunction & IRP_MN_MDL) { CcMdlRead(FileObject,&IrpSp->Parameters.Read.ByteOffset, length, &Irp->MdlAddress, &Irp->IoStatus); } else { TRACE("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus); TRACE("sizes = %llx, %llx, %llx\n", fcb->Header.AllocationSize, fcb->Header.FileSize, fcb->Header.ValidDataLength); if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) { - TRACE("CcCopyRead failed\n"); + TRACE("CcCopyRead could not wait\n"); IoMarkIrpPending(Irp); - Status = STATUS_PENDING; - goto exit; + return STATUS_PENDING; } TRACE("CcCopyRead finished\n"); } @@ -641,10 +1181,20 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (NT_SUCCESS(Status)) { Status = Irp->IoStatus.Status; - bytes_read = Irp->IoStatus.Information; + Irp->IoStatus.Information += addon; + *bytes_read = Irp->IoStatus.Information; } else ERR("EXCEPTION - %08x\n", Status); + + return Status; } else { + NTSTATUS Status; + + if (!wait) { + IoMarkIrpPending(Irp); + return STATUS_PENDING; + } + if (!(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer->DataSectionObject) { IO_STATUS_BLOCK iosb; @@ -652,30 +1202,115 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (!NT_SUCCESS(iosb.Status)) { ERR("CcFlushCache returned %08x\n", iosb.Status); - Status = iosb.Status; - goto exit; + return iosb.Status; } } - acquire_tree_lock(fcb->Vcb, FALSE); + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); if (fcb->ads) - Status = read_stream(fcb, data, start, length, &bytes_read); + Status = read_stream(fcb, data, start, length, bytes_read); else - Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, start, length, &bytes_read); + Status = read_file(fcb, data, start, length, bytes_read, Irp); - release_tree_lock(fcb->Vcb, FALSE); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); - TRACE("read %u bytes\n", bytes_read); + *bytes_read += addon; + TRACE("read %u bytes\n", *bytes_read); - Irp->IoStatus.Information = bytes_read; + Irp->IoStatus.Information = *bytes_read; + + return Status; + } +} + +NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { + device_extension* Vcb = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + ULONG bytes_read; + NTSTATUS Status; + BOOL top_level; + fcb* fcb; + ccb* ccb; + BOOL tree_lock = FALSE, fcb_lock = FALSE; + + FsRtlEnterFileSystem(); + + top_level = is_top_level(Irp); + + TRACE("read\n"); + + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit2; } + Irp->IoStatus.Information = 0; + + if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { + CcMdlReadComplete(IrpSp->FileObject, Irp->MdlAddress); + + Irp->MdlAddress = NULL; + Status = STATUS_SUCCESS; + bytes_read = 0; + + goto exit; + } + + fcb = FileObject->FsContext; + + if (!fcb) { + ERR("fcb was NULL\n"); + Status = STATUS_INVALID_PARAMETER; + goto exit; + } + + ccb = FileObject->FsContext2; + + if (!ccb) { + ERR("ccb was NULL\n"); + Status = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_READ_DATA)) { + WARN("insufficient privileges\n"); + Status = STATUS_ACCESS_DENIED; + goto exit; + } + + if (Irp->Flags & IRP_NOCACHE) { + if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) { + Status = STATUS_PENDING; + IoMarkIrpPending(Irp); + goto exit; + } + + tree_lock = TRUE; + + if (!ExAcquireResourceSharedLite(fcb->Header.Resource, IoIsOperationSynchronous(Irp))) { + Status = STATUS_PENDING; + IoMarkIrpPending(Irp); + goto exit; + } + + fcb_lock = TRUE; + } + + Status = do_read(Irp, IoIsOperationSynchronous(Irp), &bytes_read); + exit: + if (fcb_lock) + ExReleaseResourceLite(fcb->Header.Resource); + + if (tree_lock) + ExReleaseResourceLite(&Vcb->tree_lock); + Irp->IoStatus.Status = Status; if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO)) - FileObject->CurrentByteOffset.QuadPart = start + (NT_SUCCESS(Status) ? bytes_read : 0); + FileObject->CurrentByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart + (NT_SUCCESS(Status) ? bytes_read : 0); // fastfat doesn't do this, but the Wine ntdll file test seems to think we ought to if (Irp->UserIosb) @@ -687,7 +1322,12 @@ exit: if (Status != STATUS_PENDING) IoCompleteRequest(Irp, IO_NO_INCREMENT); + else { + if (!add_thread_job(Vcb, Irp)) + do_read_job(Irp); + } +exit2: if (top_level) IoSetTopLevelIrp(NULL); diff --git a/reactos/drivers/filesystems/btrfs/registry.c b/reactos/drivers/filesystems/btrfs/registry.c new file mode 100644 index 00000000000..a959c70ea4d --- /dev/null +++ b/reactos/drivers/filesystems/btrfs/registry.c @@ -0,0 +1,667 @@ +#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; + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + ULONG i, j, kvfilen, index, retlen; + KEY_VALUE_FULL_INFORMATION* kvfi = NULL; + HANDLE h; + + path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); + path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); + + if (!path.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); + i = registry_path.Length / sizeof(WCHAR); + + path.Buffer[i] = '\\'; + i++; + + for (j = 0; j < 16; j++) { + path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); + path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); + + i += 2; + + if (j == 3 || j == 5 || j == 7 || j == 9) { + path.Buffer[i] = '-'; + i++; + } + } + + kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)); + kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); + if (!kvfi) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa); + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + Status = STATUS_SUCCESS; + goto end; + } else if (!NT_SUCCESS(Status)) { + ERR("ZwOpenKey returned %08x\n", Status); + goto end; + } + + index = 0; + + ignoreus.Buffer = option_ignore; + ignoreus.Length = ignoreus.MaximumLength = wcslen(option_ignore) * sizeof(WCHAR); + + do { + Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen); + + index++; + + if (NT_SUCCESS(Status)) { + UNICODE_STRING us; + + us.Length = us.MaximumLength = kvfi->NameLength; + us.Buffer = kvfi->Name; + + if (FsRtlAreNamesEqual(&ignoreus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) { + DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset); + + options->ignore = *val != 0 ? TRUE : FALSE; + } + } else if (Status != STATUS_NO_MORE_ENTRIES) { + ERR("ZwEnumerateValueKey returned %08x\n", Status); + goto end2; + } + } while (NT_SUCCESS(Status)); + + Status = STATUS_SUCCESS; + +end2: + ZwClose(h); + +end: + ExFreePool(path.Buffer); + + if (kvfi) + ExFreePool(kvfi); + + return Status; +} + +NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid) { + UNICODE_STRING path, mountedus; + ULONG i, j; + NTSTATUS Status; + OBJECT_ATTRIBUTES oa; + HANDLE h; + DWORD data; + + path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); + path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); + + if (!path.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); + i = registry_path.Length / sizeof(WCHAR); + + path.Buffer[i] = '\\'; + i++; + + for (j = 0; j < 16; j++) { + path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); + path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); + + i += 2; + + if (j == 3 || j == 5 || j == 7 || j == 9) { + path.Buffer[i] = '-'; + i++; + } + } + + InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = ZwCreateKey(&h, KEY_SET_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, NULL); + if (!NT_SUCCESS(Status)) { + ERR("ZwCreateKey returned %08x\n", Status); + goto end; + } + + mountedus.Buffer = option_mounted; + mountedus.Length = mountedus.MaximumLength = wcslen(option_mounted) * sizeof(WCHAR); + + data = 1; + + Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD)); + if (!NT_SUCCESS(Status)) { + ERR("ZwSetValueKey returned %08x\n", Status); + goto end2; + } + + Status = STATUS_SUCCESS; + +end2: + ZwClose(h); + +end: + ExFreePool(path.Buffer); + + return Status; +} + +static NTSTATUS registry_mark_volume_unmounted_path(PUNICODE_STRING path) { + HANDLE h; + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + ULONG index, kvbilen = sizeof(KEY_VALUE_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen; + KEY_VALUE_BASIC_INFORMATION* kvbi; + BOOL has_options = FALSE; + UNICODE_STRING mountedus; + + // If a volume key has any options in it, we set Mounted to 0 and return. Otherwise, + // we delete the whole thing. + + kvbi = ExAllocatePoolWithTag(PagedPool, kvbilen, ALLOC_TAG); + if (!kvbi) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + InitializeObjectAttributes(&oa, path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = ZwOpenKey(&h, KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, &oa); + if (!NT_SUCCESS(Status)) { + ERR("ZwOpenKey returned %08x\n", Status); + goto end; + } + + index = 0; + + mountedus.Buffer = option_mounted; + mountedus.Length = mountedus.MaximumLength = wcslen(option_mounted) * sizeof(WCHAR); + + do { + Status = ZwEnumerateValueKey(h, index, KeyValueBasicInformation, kvbi, kvbilen, &retlen); + + index++; + + if (NT_SUCCESS(Status)) { + UNICODE_STRING us; + + us.Length = us.MaximumLength = kvbi->NameLength; + us.Buffer = kvbi->Name; + + if (!FsRtlAreNamesEqual(&mountedus, &us, TRUE, NULL)) { + has_options = TRUE; + break; + } + } else if (Status != STATUS_NO_MORE_ENTRIES) { + ERR("ZwEnumerateValueKey returned %08x\n", Status); + goto end2; + } + } while (NT_SUCCESS(Status)); + + if (has_options) { + DWORD data = 0; + + Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD)); + if (!NT_SUCCESS(Status)) { + ERR("ZwSetValueKey returned %08x\n", Status); + goto end2; + } + } else { + Status = ZwDeleteKey(h); + if (!NT_SUCCESS(Status)) { + ERR("ZwDeleteKey returned %08x\n", Status); + goto end2; + } + } + + Status = STATUS_SUCCESS; + +end2: + ZwClose(h); + +end: + ExFreePool(kvbi); + + return Status; +} + +NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid) { + UNICODE_STRING path; + NTSTATUS Status; + ULONG i, j; + + path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR)); + path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); + + if (!path.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length); + i = registry_path.Length / sizeof(WCHAR); + + path.Buffer[i] = '\\'; + i++; + + for (j = 0; j < 16; j++) { + path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4); + path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF); + + i += 2; + + if (j == 3 || j == 5 || j == 7 || j == 9) { + path.Buffer[i] = '-'; + i++; + } + } + + Status = registry_mark_volume_unmounted_path(&path); + if (!NT_SUCCESS(Status)) { + ERR("registry_mark_volume_unmounted_path returned %08x\n", Status); + goto end; + } + + Status = STATUS_SUCCESS; + +end: + ExFreePool(path.Buffer); + + return Status; +} + +#define is_hex(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) + +static BOOL is_uuid(ULONG namelen, WCHAR* name) { + ULONG i; + + if (namelen != 36 * sizeof(WCHAR)) + return FALSE; + + for (i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (name[i] != '-') + return FALSE; + } else if (!is_hex(name[i])) + return FALSE; + } + + return TRUE; +} + +typedef struct { + UNICODE_STRING name; + LIST_ENTRY list_entry; +} key_name; + +static void reset_subkeys(HANDLE h, PUNICODE_STRING reg_path) { + NTSTATUS Status; + KEY_BASIC_INFORMATION* kbi; + ULONG kbilen = sizeof(KEY_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen, index = 0; + LIST_ENTRY key_names, *le; + + InitializeListHead(&key_names); + + kbi = ExAllocatePoolWithTag(PagedPool, kbilen, ALLOC_TAG); + if (!kbi) { + ERR("out of memory\n"); + return; + } + + do { + Status = ZwEnumerateKey(h, index, KeyBasicInformation, kbi, kbilen, &retlen); + + index++; + + if (NT_SUCCESS(Status)) { + key_name* kn; + + ERR("key: %.*S\n", kbi->NameLength / sizeof(WCHAR), kbi->Name); + + if (is_uuid(kbi->NameLength, kbi->Name)) { + kn = ExAllocatePoolWithTag(PagedPool, sizeof(key_name), ALLOC_TAG); + if (!kn) { + ERR("out of memory\n"); + goto end; + } + + kn->name.Length = kn->name.MaximumLength = kbi->NameLength; + kn->name.Buffer = ExAllocatePoolWithTag(PagedPool, kn->name.Length, ALLOC_TAG); + + if (!kn->name.Buffer) { + ERR("out of memory\n"); + ExFreePool(kn); + goto end; + } + + RtlCopyMemory(kn->name.Buffer, kbi->Name, kbi->NameLength); + + InsertTailList(&key_names, &kn->list_entry); + } + } else if (Status != STATUS_NO_MORE_ENTRIES) + ERR("ZwEnumerateKey returned %08x\n", Status); + } while (NT_SUCCESS(Status)); + + le = key_names.Flink; + while (le != &key_names) { + key_name* kn = CONTAINING_RECORD(le, key_name, list_entry); + UNICODE_STRING path; + + path.Length = path.MaximumLength = reg_path->Length + sizeof(WCHAR) + kn->name.Length; + path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG); + + if (!path.Buffer) { + ERR("out of memory\n"); + goto end; + } + + RtlCopyMemory(path.Buffer, reg_path->Buffer, reg_path->Length); + path.Buffer[reg_path->Length / sizeof(WCHAR)] = '\\'; + RtlCopyMemory(&path.Buffer[(reg_path->Length / sizeof(WCHAR)) + 1], kn->name.Buffer, kn->name.Length); + + Status = registry_mark_volume_unmounted_path(&path); + if (!NT_SUCCESS(Status)) + WARN("registry_mark_volume_unmounted_path returned %08x\n", Status); + + ExFreePool(path.Buffer); + + le = le->Flink; + } + +end: + while (!IsListEmpty(&key_names)) { + key_name* kn; + + le = RemoveHeadList(&key_names); + kn = CONTAINING_RECORD(le, key_name, list_entry); + + if (kn->name.Buffer) + ExFreePool(kn->name.Buffer); + + ExFreePool(kn); + } + + ExFreePool(kbi); +} + +static void read_mappings(PUNICODE_STRING regpath) { + WCHAR* path; + UNICODE_STRING us; + HANDLE h; + OBJECT_ATTRIBUTES oa; + ULONG dispos; + NTSTATUS Status; + ULONG kvfilen, retlen, i; + KEY_VALUE_FULL_INFORMATION* kvfi; + + const WCHAR mappings[] = L"\\Mappings"; + + path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG); + if (!path) { + ERR("out of memory\n"); + return; + } + + RtlCopyMemory(path, regpath->Buffer, regpath->Length); + RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR)); + + us.Buffer = path; + us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR)); + + InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + // FIXME - keep open and do notify for changes + Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); + + if (!NT_SUCCESS(Status)) { + ERR("ZwCreateKey returned %08x\n", Status); + ExFreePool(path); + return; + } + + if (dispos == REG_OPENED_EXISTING_KEY) { + kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256; + kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); + + if (!kvfi) { + ERR("out of memory\n"); + ExFreePool(path); + ZwClose(h); + return; + } + + i = 0; + do { + Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen); + + if (NT_SUCCESS(Status) && kvfi->DataLength > 0) { + UINT32 val = 0; + + RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32))); + + TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val); + + add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val); + } + + i = i + 1; + } while (Status != STATUS_NO_MORE_ENTRIES); + } + + ZwClose(h); + + 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__ + 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); + +#ifdef _DEBUG + RtlInitUnicodeString(&us, L"DebugLogLevel"); + + kvfi = NULL; + kvfilen = 0; + Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); + + if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { + kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); + + if (!kvfi) { + ERR("out of memory\n"); + ZwClose(h); + return; + } + + 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)); + } 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)); + if (!NT_SUCCESS(Status)) { + ERR("ZwSetValueKey reutrned %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)); + + if (!NT_SUCCESS(Status)) { + ERR("ZwSetValueKey reutrned %08x\n", Status); + } + } else { + ERR("ZwQueryValueKey returned %08x\n", Status); + } + + RtlInitUnicodeString(&us, L"LogDevice"); + + kvfi = NULL; + kvfilen = 0; + Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); + + if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { + kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); + + if (!kvfi) { + ERR("out of memory\n"); + ZwClose(h); + return; + } + + Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); + + if (NT_SUCCESS(Status)) { + if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { + log_device.Length = log_device.MaximumLength = kvfi->DataLength; + log_device.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG); + + if (!log_device.Buffer) { + ERR("out of memory\n"); + ExFreePool(kvfi); + ZwClose(h); + return; + } + + RtlCopyMemory(log_device.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength); + + if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0) + log_device.Length -= sizeof(WCHAR); + } else { + ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength); + + Status = ZwDeleteValueKey(h, &us); + if (!NT_SUCCESS(Status)) { + ERR("ZwDeleteValueKey returned %08x\n", Status); + } + } + } + + ExFreePool(kvfi); + } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ERR("ZwQueryValueKey returned %08x\n", Status); + } + + RtlInitUnicodeString(&us, L"LogFile"); + + kvfi = NULL; + kvfilen = 0; + Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); + + if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { + kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); + + if (!kvfi) { + ERR("out of memory\n"); + ZwClose(h); + return; + } + + Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); + + if (NT_SUCCESS(Status)) { + if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { + log_file.Length = log_file.MaximumLength = kvfi->DataLength; + log_file.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG); + + if (!log_file.Buffer) { + ERR("out of memory\n"); + ExFreePool(kvfi); + ZwClose(h); + return; + } + + RtlCopyMemory(log_file.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength); + + if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0) + log_file.Length -= sizeof(WCHAR); + } else { + ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength); + + Status = ZwDeleteValueKey(h, &us); + if (!NT_SUCCESS(Status)) { + ERR("ZwDeleteValueKey returned %08x\n", Status); + } + } + } + + ExFreePool(kvfi); + } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file, (wcslen(def_log_file) + 1) * sizeof(WCHAR)); + + if (!NT_SUCCESS(Status)) { + ERR("ZwSetValueKey returned %08x\n", Status); + } + } else { + ERR("ZwQueryValueKey returned %08x\n", Status); + } + + if (log_file.Length == 0) { + log_file.Length = log_file.MaximumLength = wcslen(def_log_file) * sizeof(WCHAR); + log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG); + + if (!log_file.Buffer) { + ERR("out of memory\n"); + ZwClose(h); + return; + } + + RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length); + } +#endif + + ZwClose(h); +} diff --git a/reactos/drivers/filesystems/btrfs/reparse.c b/reactos/drivers/filesystems/btrfs/reparse.c index 15724b0cc9e..e281dc3f61f 100644 --- a/reactos/drivers/filesystems/btrfs/reparse.c +++ b/reactos/drivers/filesystems/btrfs/reparse.c @@ -30,7 +30,8 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen); - acquire_tree_lock(fcb->Vcb, FALSE); + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); if (fcb->type == BTRFS_TYPE_SYMLINK) { data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG); @@ -41,7 +42,7 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, } TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size); - Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL); + Status = read_file(fcb, (UINT8*)data, 0, fcb->inode_item.st_size, NULL, NULL); if (!NT_SUCCESS(Status)) { ERR("read_file returned %08x\n", Status); @@ -101,38 +102,22 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, Status = STATUS_SUCCESS; } else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) { if (fcb->type == BTRFS_TYPE_FILE) { - Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, buffer, 0, buflen, retlen); + Status = read_file(fcb, buffer, 0, buflen, retlen, NULL); if (!NT_SUCCESS(Status)) { ERR("read_file returned %08x\n", Status); } } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { - UINT8* data; - UINT16 datalen; - - if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen)) { - Status = STATUS_NOT_A_REPARSE_POINT; - goto end; - } - - if (!data) { - Status = STATUS_NOT_A_REPARSE_POINT; - goto end; - } - - if (datalen < sizeof(ULONG)) { - ExFreePool(data); + if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) { Status = STATUS_NOT_A_REPARSE_POINT; goto end; } if (buflen > 0) { - *retlen = min(buflen, datalen); - RtlCopyMemory(buffer, data, *retlen); + *retlen = min(buflen, fcb->reparse_xattr.Length); + RtlCopyMemory(buffer, fcb->reparse_xattr.Buffer, *retlen); } else *retlen = 0; - - ExFreePool(data); } else Status = STATUS_NOT_A_REPARSE_POINT; } else { @@ -140,115 +125,19 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, } end: - release_tree_lock(fcb->Vcb, FALSE); + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); return Status; } -static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type, LIST_ENTRY* rollback) { - KEY searchkey; - UINT32 crc32; - traverse_ptr tp; - NTSTATUS Status; - - crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length); - - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_ITEM; - searchkey.offset = crc32; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&tp.item->key, &searchkey)) { - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - } else { - DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2; - BOOL found = FALSE; - ULONG len = tp.item->size; - - if (!di) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(di, tp.item->data, tp.item->size); - - di2 = di; - do { - if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di2->m + di2->n) { - ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) { - di2->type = type; - found = TRUE; - break; - } - - if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) { - len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n; - di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n]; - } else - break; - } while (len > 0); - - if (found) { - delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL, rollback); - } else - ExFreePool(di); - } - } else { - WARN("search for DIR_ITEM by crc32 failed\n"); - } - - searchkey.obj_id = parinode; - searchkey.obj_type = TYPE_DIR_INDEX; - searchkey.offset = index; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (!keycmp(&tp.item->key, &searchkey)) { - if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); - } else { - DIR_ITEM* di = (DIR_ITEM*)tp.item->data; - DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - if (!di2) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(di2, di, tp.item->size); - di2->type = type; - - delete_tree_item(Vcb, &tp, rollback); - insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL, rollback); - } - } - - return STATUS_SUCCESS; -} - -static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) { +static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) { NTSTATUS Status; ULONG minlen; UNICODE_STRING subname; ANSI_STRING target; - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b; - LARGE_INTEGER offset; + LARGE_INTEGER offset, time; + BTRFS_TIME now; USHORT i; minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR); @@ -260,125 +149,13 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)]; subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength; - ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer); + TRACE("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer); - fcb->type = BTRFS_TYPE_SYMLINK; + fileref->fcb->type = BTRFS_TYPE_SYMLINK; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_REF; - searchkey.offset = 0; + fileref->fcb->inode_item.st_mode |= __S_IFLNK; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) { - if (tp.item->size < sizeof(INODE_REF)) { - WARN("(%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(INODE_REF)); - } else { - INODE_REF* ir; - ULONG size = tp.item->size; - ANSI_STRING utf8; - - ir = (INODE_REF*)tp.item->data; - - do { - if (size < sizeof(INODE_REF) || size < sizeof(INODE_REF) - 1 + ir->n) { - WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - utf8.Buffer = ir->name; - utf8.Length = utf8.MaximumLength = ir->n; - - Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - change_file_type returned %08x\n", Status); - return Status; - } - - if (size > sizeof(INODE_REF) - 1 + ir->n) { - size -= sizeof(INODE_REF) - 1 + ir->n; - - ir = (INODE_REF*)&ir->name[ir->n]; - } else - break; - } while (TRUE); - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF; - } - } while (b); - - if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) { - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_EXTREF; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) { - if (tp.item->size < sizeof(INODE_EXTREF)) { - WARN("(%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(INODE_EXTREF)); - } else { - INODE_EXTREF* ier; - ULONG size = tp.item->size; - ANSI_STRING utf8; - - ier = (INODE_EXTREF*)tp.item->data; - - do { - if (size < sizeof(INODE_EXTREF) || size < sizeof(INODE_EXTREF) - 1 + ier->n) { - WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - utf8.Buffer = ier->name; - utf8.Length = utf8.MaximumLength = ier->n; - - Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - change_file_type returned %08x\n", Status); - return Status; - } - - if (size > sizeof(INODE_EXTREF) - 1 + ier->n) { - size -= sizeof(INODE_EXTREF) - 1 + ier->n; - - ier = (INODE_EXTREF*)&ier->name[ier->n]; - } else - break; - } while (TRUE); - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF; - } - } while (b); - } - - fcb->inode_item.st_mode |= __S_IFLNK; - - Status = truncate_file(fcb, 0, rollback); + Status = truncate_file(fileref->fcb, 0, rollback); if (!NT_SUCCESS(Status)) { ERR("truncate_file returned %08x\n", Status); return Status; @@ -386,7 +163,7 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length); if (!NT_SUCCESS(Status)) { - ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status); + ERR("RtlUnicodeToUTF8N 1 failed with error %08x\n", Status); return Status; } @@ -399,7 +176,7 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length); if (!NT_SUCCESS(Status)) { - ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status); + ERR("RtlUnicodeToUTF8N 2 failed with error %08x\n", Status); ExFreePool(target.Buffer); return Status; } @@ -410,159 +187,23 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG } offset.QuadPart = 0; - Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, rollback); - + Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, FALSE, TRUE, + TRUE, FALSE, rollback); ExFreePool(target.Buffer); - return Status; -} - -static NTSTATUS delete_symlink(fcb* fcb, LIST_ENTRY* rollback) { - NTSTATUS Status; - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b; - LARGE_INTEGER time; - BTRFS_TIME now; - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_REF; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) { - if (tp.item->size < sizeof(INODE_REF)) { - WARN("(%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(INODE_REF)); - } else { - INODE_REF* ir; - ULONG size = tp.item->size; - ANSI_STRING utf8; - - ir = (INODE_REF*)tp.item->data; - - do { - if (size < sizeof(INODE_REF) || size < sizeof(INODE_REF) - 1 + ir->n) { - WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - utf8.Buffer = ir->name; - utf8.Length = utf8.MaximumLength = ir->n; - - Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_FILE, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - change_file_type returned %08x\n", Status); - return Status; - } - - if (size > sizeof(INODE_REF) - 1 + ir->n) { - size -= sizeof(INODE_REF) - 1 + ir->n; - - ir = (INODE_REF*)&ir->name[ir->n]; - } else - break; - } while (TRUE); - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF; - } - } while (b); - - if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) { - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_EXTREF; - searchkey.offset = 0; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - do { - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) { - if (tp.item->size < sizeof(INODE_EXTREF)) { - WARN("(%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(INODE_EXTREF)); - } else { - INODE_EXTREF* ier; - ULONG size = tp.item->size; - ANSI_STRING utf8; - - ier = (INODE_EXTREF*)tp.item->data; - - do { - if (size < sizeof(INODE_EXTREF) || size < sizeof(INODE_EXTREF) - 1 + ier->n) { - WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - break; - } - - utf8.Buffer = ier->name; - utf8.Length = utf8.MaximumLength = ier->n; - - Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_FILE, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - change_file_type returned %08x\n", Status); - return Status; - } - - if (size > sizeof(INODE_EXTREF) - 1 + ier->n) { - size -= sizeof(INODE_EXTREF) - 1 + ier->n; - - ier = (INODE_EXTREF*)&ier->name[ier->n]; - } else - break; - } while (TRUE); - } - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF; - } - } while (b); - } - - Status = truncate_file(fcb, 0, rollback); - if (!NT_SUCCESS(Status)) { - ERR("truncate_file returned %08x\n", Status); - return Status; - } - KeQuerySystemTime(&time); - win_time_to_unix(time, &now); + + fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->inode_item.sequence++; + fileref->fcb->inode_item.st_ctime = now; + fileref->fcb->inode_item.st_mtime = now; - fcb->type = BTRFS_TYPE_FILE; - fcb->inode_item.st_mode &= ~__S_IFLNK; - fcb->inode_item.st_mode |= __S_IFREG; - fcb->inode_item.transid = fcb->Vcb->superblock.generation; - fcb->inode_item.sequence++; - fcb->inode_item.st_ctime = now; - fcb->inode_item.st_mtime = now; - - Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_inode_item returned %08x\n", Status); - return Status; - } - - fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; - fcb->subvol->root_item.ctime = now; + fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->subvol->root_item.ctime = now; + + mark_fcb_dirty(fileref->fcb); + mark_fileref_dirty(fileref); return Status; } @@ -575,6 +216,8 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; NTSTATUS Status = STATUS_SUCCESS; fcb* fcb; + ccb* ccb; + file_ref* fileref; ULONG tag; LIST_ENTRY rollback; @@ -591,10 +234,24 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } fcb = FileObject->FsContext; + ccb = FileObject->FsContext2; + + if (!ccb) { + ERR("ccb was NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + fileref = ccb->fileref; + + if (!fileref) { + ERR("fileref was NULL\n"); + return STATUS_INVALID_PARAMETER; + } TRACE("%S\n", file_desc(FileObject)); - acquire_tree_lock(fcb->Vcb, TRUE); + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); if (fcb->type == BTRFS_TYPE_SYMLINK) { WARN("tried to set a reparse point on an existing symlink\n"); @@ -616,17 +273,32 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { RtlCopyMemory(&tag, buffer, sizeof(ULONG)); if (fcb->type == BTRFS_TYPE_FILE && tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) { - Status = set_symlink(Irp, fcb, rdb, buflen, &rollback); + Status = set_symlink(Irp, fileref, rdb, buflen, &rollback); + fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; } else { - LARGE_INTEGER offset; - char val[64]; + LARGE_INTEGER offset, time; + BTRFS_TIME now; if (fcb->type == BTRFS_TYPE_DIRECTORY) { // for directories, store as xattr - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, buffer, buflen, &rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); + ANSI_STRING buf; + + buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG); + if (!buf.Buffer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } + buf.Length = buf.MaximumLength = buflen; + + if (fcb->reparse_xattr.Buffer) + ExFreePool(fcb->reparse_xattr.Buffer); + + fcb->reparse_xattr = buf; + RtlCopyMemory(fcb->reparse_xattr.Buffer, buffer, buflen); + + fcb->reparse_xattr_changed = TRUE; + + Status = STATUS_SUCCESS; } else { // otherwise, store as file data Status = truncate_file(fcb, 0, &rollback); if (!NT_SUCCESS(Status)) { @@ -636,26 +308,30 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { offset.QuadPart = 0; - Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback); + Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE, TRUE, TRUE, FALSE, &rollback); if (!NT_SUCCESS(Status)) { ERR("write_file2 returned %08x\n", Status); goto end; } } - - fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; - 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); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - goto end; - } + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + fcb->inode_item.transid = fcb->Vcb->superblock.generation; + fcb->inode_item.sequence++; + fcb->inode_item.st_ctime = now; + fcb->inode_item.st_mtime = now; + fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; + fcb->atts_changed = TRUE; + + fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; + fcb->subvol->root_item.ctime = now; + + mark_fcb_dirty(fcb); } - if (NT_SUCCESS(Status)) - Status = consider_write(fcb->Vcb); + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED); end: if (NT_SUCCESS(Status)) @@ -663,7 +339,8 @@ end: else do_rollback(fcb->Vcb, &rollback); - release_tree_lock(fcb->Vcb, TRUE); + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); return Status; } @@ -679,7 +356,6 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { file_ref* fileref; LIST_ENTRY rollback; - // FIXME - send notification if this succeeds? The attributes will have changed. // FIXME - check permissions TRACE("(%p, %p)\n", DeviceObject, Irp); @@ -695,25 +371,29 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { ccb = FileObject->FsContext2; fileref = ccb ? ccb->fileref : NULL; + ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE); + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + TRACE("%S\n", file_desc(FileObject)); if (!fileref) { ERR("fileref was NULL\n"); - return STATUS_INVALID_PARAMETER; + Status = STATUS_INVALID_PARAMETER; + goto end; } if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) { ERR("buffer was too short\n"); - return STATUS_INVALID_PARAMETER; + Status = STATUS_INVALID_PARAMETER; + goto end; } if (rdb->ReparseDataLength > 0) { WARN("rdb->ReparseDataLength was not zero\n"); - return STATUS_INVALID_PARAMETER; + Status = STATUS_INVALID_PARAMETER; + goto end; } - acquire_tree_lock(fcb->Vcb, TRUE); - if (fcb->ads) { WARN("tried to delete reparse point on ADS\n"); Status = STATUS_INVALID_PARAMETER; @@ -721,21 +401,35 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } if (fcb->type == BTRFS_TYPE_SYMLINK) { + LARGE_INTEGER time; + BTRFS_TIME now; + if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) { WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n"); Status = STATUS_INVALID_PARAMETER; goto end; } - Status = delete_symlink(fcb, &rollback); - if (!NT_SUCCESS(Status)) { - ERR("delete_symlink returned %08x\n", Status); - goto end; - } + KeQuerySystemTime(&time); + win_time_to_unix(time, &now); + + fileref->fcb->type = BTRFS_TYPE_FILE; + fileref->fcb->inode_item.st_mode &= ~__S_IFLNK; + fileref->fcb->inode_item.st_mode |= __S_IFREG; + fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation; + fileref->fcb->inode_item.sequence++; + fileref->fcb->inode_item.st_ctime = now; + fileref->fcb->inode_item.st_mtime = now; + fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; + + mark_fileref_dirty(fileref); + mark_fcb_dirty(fileref->fcb); + + fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; + fileref->fcb->subvol->root_item.ctime = now; } else if (fcb->type == BTRFS_TYPE_FILE) { LARGE_INTEGER time; BTRFS_TIME now; - ULONG defda; // FIXME - do we need to check that the reparse tags match? @@ -746,24 +440,9 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; - - defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE); - - if (defda != fcb->atts) { - char val[64]; - - 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); - 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); + fcb->atts_changed = TRUE; KeQuerySystemTime(&time); - win_time_to_unix(time, &now); fcb->inode_item.transid = fcb->Vcb->superblock.generation; @@ -771,55 +450,35 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { fcb->inode_item.st_ctime = now; fcb->inode_item.st_mtime = now; - Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_inode_item returned %08x\n", Status); - goto end; - } + mark_fcb_dirty(fcb); fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; } else if (fcb->type == BTRFS_TYPE_DIRECTORY) { LARGE_INTEGER time; BTRFS_TIME now; - ULONG defda; // FIXME - do we need to check that the reparse tags match? fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT; + fcb->atts_changed = TRUE; - defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE); - defda |= FILE_ATTRIBUTE_DIRECTORY; + if (fcb->reparse_xattr.Buffer) { + ExFreePool(fcb->reparse_xattr.Buffer); + fcb->reparse_xattr.Buffer = NULL; + } - if (defda != fcb->atts) { - char val[64]; - - 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); - 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_REPARSE, EA_REPARSE_HASH, &rollback); + fcb->reparse_xattr_changed = TRUE; KeQuerySystemTime(&time); - win_time_to_unix(time, &now); - + fcb->inode_item.transid = fcb->Vcb->superblock.generation; fcb->inode_item.sequence++; fcb->inode_item.st_ctime = now; fcb->inode_item.st_mtime = now; - Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback); - if (!NT_SUCCESS(Status)) { - ERR("update_inode_item returned %08x\n", Status); - goto end; - } + mark_fcb_dirty(fcb); fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; @@ -831,16 +490,16 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Status = STATUS_SUCCESS; - if (NT_SUCCESS(Status)) - Status = consider_write(fcb->Vcb); + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED); end: if (NT_SUCCESS(Status)) clear_rollback(&rollback); else do_rollback(fcb->Vcb, &rollback); - - release_tree_lock(fcb->Vcb, TRUE); + + ExReleaseResourceLite(fcb->Header.Resource); + ExReleaseResourceLite(&fcb->Vcb->tree_lock); return Status; } diff --git a/reactos/drivers/filesystems/btrfs/search.c b/reactos/drivers/filesystems/btrfs/search.c index 4245e2e92c4..8a7a79b70aa 100644 --- a/reactos/drivers/filesystems/btrfs/search.c +++ b/reactos/drivers/filesystems/btrfs/search.c @@ -53,6 +53,63 @@ VOID WINAPI IopNotifyPlugPlayNotification( static const WCHAR devpath[] = {'\\','D','e','v','i','c','e',0}; +static NTSTATUS create_part0(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT DeviceObject, PUNICODE_STRING pardir, PUNICODE_STRING nameus, + BTRFS_UUID* uuid) { + PDEVICE_OBJECT newdevobj; + UNICODE_STRING name; + NTSTATUS Status; + part0_device_extension* p0de; + + static const WCHAR btrfs_partition[] = L"\\BtrfsPartition"; + + name.Length = name.MaximumLength = pardir->Length + (wcslen(btrfs_partition) * sizeof(WCHAR)); + name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG); + if (!name.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(name.Buffer, pardir->Buffer, pardir->Length); + RtlCopyMemory(&name.Buffer[pardir->Length / sizeof(WCHAR)], btrfs_partition, wcslen(btrfs_partition) * sizeof(WCHAR)); + + Status = IoCreateDevice(DriverObject, sizeof(part0_device_extension), &name, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &newdevobj); + if (!NT_SUCCESS(Status)) { + ERR("IoCreateDevice returned %08x\n", Status); + ExFreePool(name.Buffer); + return Status; + } + + p0de = newdevobj->DeviceExtension; + p0de->type = VCB_TYPE_PARTITION0; + p0de->devobj = DeviceObject; + RtlCopyMemory(&p0de->uuid, uuid, sizeof(BTRFS_UUID)); + + p0de->name.Length = name.Length; + p0de->name.MaximumLength = name.MaximumLength; + p0de->name.Buffer = ExAllocatePoolWithTag(PagedPool, p0de->name.MaximumLength, ALLOC_TAG); + + if (!p0de->name.Buffer) { + ERR("out of memory\b"); + ExFreePool(name.Buffer); + ExFreePool(p0de->name.Buffer); + IoDeleteDevice(newdevobj); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(p0de->name.Buffer, name.Buffer, name.Length); + + ObReferenceObject(DeviceObject); + + newdevobj->StackSize = DeviceObject->StackSize + 1; + + newdevobj->Flags |= DO_DIRECT_IO; + newdevobj->Flags &= ~DO_DEVICE_INITIALIZING; + + *nameus = name; + + return STATUS_SUCCESS; +} + static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) { ULONG tnsize; MOUNTMGR_TARGET_NAME* tn; @@ -81,7 +138,8 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) { mountmgr, tn, tnsize, NULL, 0, FALSE, &Event, &IoStatusBlock); if (!Irp) { - ERR("IoBuildDeviceIoControlRequest failed\n"); + ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", us->Length / sizeof(WCHAR), us->Buffer); + ExFreePool(tn); return; } @@ -91,8 +149,10 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) { Status = IoStatusBlock.Status; } - if (!NT_SUCCESS(Status)) - ERR("IoCallDriver (1) returned %08x\n", Status); + if (!NT_SUCCESS(Status)) { + ERR("%.*S: IoCallDriver 1 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status); + return; + } ExFreePool(tn); @@ -113,7 +173,7 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) { mountmgr, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock); if (!Irp) { - ERR("IoBuildDeviceIoControlRequest failed\n"); + ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", us->Length / sizeof(WCHAR), us->Buffer); return; } @@ -123,15 +183,15 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) { Status = IoStatusBlock.Status; } - if (!NT_SUCCESS(Status)) - ERR("IoCallDriver (2) returned %08x\n", Status); - else + if (!NT_SUCCESS(Status)) { + ERR("%.*S: IoCallDriver 2 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status); + } else TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter); ExFreePool(mmdlt); } -static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_ENTRY* volumes) { +static void STDCALL test_vol(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING pardir, PUNICODE_STRING us, BOOL part0, LIST_ENTRY* volumes) { KEVENT Event; PIRP Irp; IO_STATUS_BLOCK IoStatusBlock; @@ -143,10 +203,11 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E UINT8* data; UNICODE_STRING us2; BOOL added_entry = FALSE; + UINT32 sector_size; TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer); - us2.Length = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + us->Length; + us2.Length = pardir->Length + sizeof(WCHAR) + us->Length; us2.MaximumLength = us2.Length; us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG); if (!us2.Buffer) { @@ -154,9 +215,9 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E return; } - RtlCopyMemory(us2.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR)); - us2.Buffer[wcslen(devpath)] = '\\'; - RtlCopyMemory(&us2.Buffer[wcslen(devpath)+1], us->Buffer, us->Length); + RtlCopyMemory(us2.Buffer, pardir->Buffer, pardir->Length); + us2.Buffer[pardir->Length / sizeof(WCHAR)] = '\\'; + RtlCopyMemory(&us2.Buffer[(pardir->Length / sizeof(WCHAR))+1], us->Buffer, us->Length); TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer); @@ -165,12 +226,40 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E ERR("IoGetDeviceObjectPointer returned %08x\n", Status); goto exit; } + + sector_size = DeviceObject->SectorSize; + + if (sector_size == 0) { + DISK_GEOMETRY geometry; + IO_STATUS_BLOCK iosb; + + Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb); + + if (!NT_SUCCESS(Status)) { + ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08x\n", + us2.Length / sizeof(WCHAR), us2.Buffer, Status); + goto exit; + } + + if (iosb.Information < sizeof(DISK_GEOMETRY)) { + ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %u bytes, expected %u\n", + us2.Length / sizeof(WCHAR), us2.Buffer, iosb.Information, sizeof(DISK_GEOMETRY)); + } + + sector_size = geometry.BytesPerSector; + + if (sector_size == 0) { + ERR("%.*S had a sector size of 0\n", us2.Length / sizeof(WCHAR), us2.Buffer); + goto exit; + } + } KeInitializeEvent(&Event, NotificationEvent, FALSE); Offset.QuadPart = superblock_addrs[0]; - toread = sector_align(sizeof(superblock), DeviceObject->SectorSize); + toread = sector_align(sizeof(superblock), sector_size); data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); if (!data) { ERR("out of memory\n"); @@ -192,22 +281,85 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E } if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) { + int i; + GET_LENGTH_INFORMATION gli; superblock* sb = (superblock*)data; volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG); + if (!v) { ERR("out of memory\n"); goto deref; } - v->devobj = DeviceObject; + Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, + &gli, sizeof(gli), TRUE, NULL); + if (!NT_SUCCESS(Status)) { + ERR("error reading length information: %08x\n", Status); + goto deref; + } + + if (part0) { + UNICODE_STRING us3; + + Status = create_part0(DriverObject, DeviceObject, pardir, &us3, &sb->dev_item.device_uuid); + + if (!NT_SUCCESS(Status)) { + ERR("create_part0 returned %08x\n", Status); + goto deref; + } + + ExFreePool(us2.Buffer); + us2 = us3; + } + RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID)); RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)); v->devnum = sb->dev_item.dev_id; v->devpath = us2; v->processed = FALSE; + v->length = gli.Length.QuadPart; + v->gen1 = sb->generation; + v->gen2 = 0; InsertTailList(volumes, &v->list_entry); + i = 1; + while (superblock_addrs[i] != 0 && superblock_addrs[i] + toread <= v->length) { + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + Offset.QuadPart = superblock_addrs[i]; + + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock); + + if (!Irp) { + ERR("IoBuildSynchronousFsdRequest failed\n"); + goto deref; + } + + Status = IoCallDriver(DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (NT_SUCCESS(Status)) { + UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); + + if (crc32 != *((UINT32*)sb->checksum)) + WARN("superblock %u CRC error\n", i); + else if (sb->generation > v->gen1) { + v->gen2 = v->gen1; + v->gen1 = sb->generation; + } + } else { + ERR("got error %08x while reading superblock %u\n", Status, i); + } + + i++; + } + TRACE("volume found\n"); + TRACE("gen1 = %llx, gen2 = %llx\n", v->gen1, v->gen2); TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7], v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]); @@ -228,7 +380,192 @@ exit: ExFreePool(us2.Buffer); } -void STDCALL look_for_vols(LIST_ENTRY* volumes) { +static NTSTATUS look_in_harddisk_dir(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING name, LIST_ENTRY* volumes) { + UNICODE_STRING path; + OBJECT_ATTRIBUTES attr; + NTSTATUS Status; + HANDLE h; + OBJECT_DIRECTORY_INFORMATION* odi; + ULONG odisize, context; + BOOL restart, has_part0 = FALSE, has_parts = FALSE; + + static const WCHAR partition[] = L"Partition"; + static WCHAR partition0[] = L"Partition0"; + + path.Buffer = ExAllocatePoolWithTag(PagedPool, ((wcslen(devpath) + 1) * sizeof(WCHAR)) + name->Length, ALLOC_TAG); + if (!path.Buffer) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(path.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR)); + path.Buffer[wcslen(devpath)] = '\\'; + RtlCopyMemory(&path.Buffer[wcslen(devpath) + 1], name->Buffer, name->Length); + path.Length = path.MaximumLength = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + name->Length; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &path; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + Status = ZwOpenDirectoryObject(&h, DIRECTORY_TRAVERSE, &attr); + + if (!NT_SUCCESS(Status)) { + ERR("ZwOpenDirectoryObject returned %08x\n", Status); + goto end; + } + + odisize = sizeof(OBJECT_DIRECTORY_INFORMATION) * 16; + odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG); + if (!odi) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ZwClose(h); + goto end; + } + + restart = TRUE; + do { + Status = ZwQueryDirectoryObject(h, odi, odisize, FALSE, restart, &context, NULL/*&retlen*/); + restart = FALSE; + + if (!NT_SUCCESS(Status)) { + if (Status != STATUS_NO_MORE_ENTRIES) + ERR("ZwQueryDirectoryObject returned %08x\n", Status); + } else { + OBJECT_DIRECTORY_INFORMATION* odi2 = odi; + + while (odi2->Name.Buffer) { + TRACE("%.*S, %.*S\n", odi2->TypeName.Length / sizeof(WCHAR), odi2->TypeName.Buffer, odi2->Name.Length / sizeof(WCHAR), odi2->Name.Buffer); + + if (odi2->Name.Length > wcslen(partition) * sizeof(WCHAR) && + RtlCompareMemory(odi2->Name.Buffer, partition, wcslen(partition) * sizeof(WCHAR)) == wcslen(partition) * sizeof(WCHAR)) { + + if (odi2->Name.Length == (wcslen(partition) + 1) * sizeof(WCHAR) && odi2->Name.Buffer[(odi2->Name.Length / sizeof(WCHAR)) - 1] == '0') { + // Partition0 refers to the whole disk + has_part0 = TRUE; + } else { + has_parts = TRUE; + + test_vol(DriverObject, mountmgr, &path, &odi2->Name, FALSE, volumes); + } + } + + odi2 = &odi2[1]; + } + } + } while (NT_SUCCESS(Status)); + + // If disk had no partitions, test the whole disk + if (!has_parts && has_part0) { + UNICODE_STRING part0us; + + part0us.Buffer = partition0; + part0us.Length = part0us.MaximumLength = wcslen(partition0) * sizeof(WCHAR); + + test_vol(DriverObject, mountmgr, &path, &part0us, TRUE, volumes); + } + + ZwClose(h); + + ExFreePool(odi); + + Status = STATUS_SUCCESS; + +end: + ExFreePool(path.Buffer); + + return Status; +} + +static void remove_drive_letter(PDEVICE_OBJECT mountmgr, volume* v) { + NTSTATUS Status; + KEVENT Event; + PIRP Irp; + MOUNTMGR_MOUNT_POINT* mmp; + ULONG mmpsize; + MOUNTMGR_MOUNT_POINTS mmps1, *mmps2; + IO_STATUS_BLOCK IoStatusBlock; + + mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + v->devpath.Length; + + mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG); + if (!mmp) { + ERR("out of memory\n"); + return; + } + + RtlZeroMemory(mmp, mmpsize); + + mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); + mmp->DeviceNameLength = v->devpath.Length; + RtlCopyMemory(&mmp[1], v->devpath.Buffer, v->devpath.Length); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS, + mountmgr, mmp, mmpsize, + &mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), FALSE, &Event, &IoStatusBlock); + if (!Irp) { + ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer); + ExFreePool(mmp); + return; + } + + Status = IoCallDriver(mountmgr, Irp); + if (Status == STATUS_PENDING) { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { + ERR("%.*S: IoCallDriver 1 returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status); + ExFreePool(mmp); + return; + } + + if (Status != STATUS_BUFFER_OVERFLOW || mmps1.Size == 0) { + ExFreePool(mmp); + return; + } + + mmps2 = ExAllocatePoolWithTag(PagedPool, mmps1.Size, ALLOC_TAG); + if (!mmps2) { + ERR("out of memory\n"); + ExFreePool(mmp); + return; + } + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS, + mountmgr, mmp, mmpsize, + mmps2, mmps1.Size, FALSE, &Event, &IoStatusBlock); + if (!Irp) { + ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer); + ExFreePool(mmps2); + ExFreePool(mmp); + return; + } + + Status = IoCallDriver(mountmgr, Irp); + if (Status == STATUS_PENDING) { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS(Status)) { + ERR("%.*S: IoCallDriver 2 returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status); + ExFreePool(mmps2); + ExFreePool(mmp); + return; + } + + ExFreePool(mmps2); + ExFreePool(mmp); +} + +void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes) { PFILE_OBJECT FileObject; PDEVICE_OBJECT mountmgr; OBJECT_ATTRIBUTES attr; @@ -241,8 +578,8 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) { NTSTATUS Status; LIST_ENTRY* le; - static const WCHAR hdv[] = {'H','a','r','d','d','i','s','k','V','o','l','u','m','e',0}; - static const WCHAR device[] = {'D','e','v','i','c','e',0}; + static const WCHAR directory[] = L"Directory"; + static const WCHAR harddisk[] = L"Harddisk"; RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr); @@ -271,6 +608,7 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) { odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG); if (!odi) { ERR("out of memory\n"); + ZwClose(h); return; } @@ -286,21 +624,21 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) { OBJECT_DIRECTORY_INFORMATION* odi2 = odi; while (odi2->Name.Buffer) { - if (odi2->TypeName.Length == wcslen(device) * sizeof(WCHAR) && - RtlCompareMemory(odi2->TypeName.Buffer, device, wcslen(device) * sizeof(WCHAR)) == wcslen(device) * sizeof(WCHAR) && - odi2->Name.Length > wcslen(hdv) * sizeof(WCHAR) && - RtlCompareMemory(odi2->Name.Buffer, hdv, wcslen(hdv) * sizeof(WCHAR)) == wcslen(hdv) * sizeof(WCHAR)) { - test_vol(mountmgr, &odi2->Name, volumes); + if (odi2->TypeName.Length == wcslen(directory) * sizeof(WCHAR) && + RtlCompareMemory(odi2->TypeName.Buffer, directory, odi2->TypeName.Length) == odi2->TypeName.Length && + odi2->Name.Length > wcslen(harddisk) * sizeof(WCHAR) && + RtlCompareMemory(odi2->Name.Buffer, harddisk, wcslen(harddisk) * sizeof(WCHAR)) == wcslen(harddisk) * sizeof(WCHAR)) { + look_in_harddisk_dir(DriverObject, mountmgr, &odi2->Name, volumes); } + odi2 = &odi2[1]; } } } while (NT_SUCCESS(Status)); + ExFreePool(odi); ZwClose(h); - // FIXME - if Windows has already added the second device of a filesystem itself, delete it - le = volumes->Flink; while (le != volumes) { volume* v = CONTAINING_RECORD(le, volume, list_entry); @@ -315,8 +653,11 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) { if (RtlCompareMemory(&v2->fsuuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { v2->processed = TRUE; - if (v2->devnum < mountvol->devnum) + if (v2->devnum < mountvol->devnum) { + remove_drive_letter(mountmgr, mountvol); mountvol = v2; + } else if (v2->devnum > mountvol->devnum) + remove_drive_letter(mountmgr, v2); } le2 = le2->Flink; diff --git a/reactos/drivers/filesystems/btrfs/security.c b/reactos/drivers/filesystems/btrfs/security.c index 009670c479b..9b6b0cf06fd 100644 --- a/reactos/drivers/filesystems/btrfs/security.c +++ b/reactos/drivers/filesystems/btrfs/security.c @@ -17,6 +17,9 @@ #include "btrfs_drv.h" +#define SEF_DACL_AUTO_INHERIT 0x01 +#define SEF_SACL_AUTO_INHERIT 0x02 + typedef struct { UCHAR revision; UCHAR elements; @@ -382,103 +385,6 @@ static ACL* load_default_acl() { return acl; } -static ACL* inherit_acl(SECURITY_DESCRIPTOR* parsd, BOOL file) { - ULONG size; - NTSTATUS Status; - ACL *paracl, *acl; - BOOLEAN parhasdacl, pardefaulted; - ACE_HEADER *ah, *parah; - UINT32 i; - USHORT num_aces; - - // FIXME - replace this with SeAssignSecurity - - Status = RtlGetDaclSecurityDescriptor(parsd, &parhasdacl, ¶cl, &pardefaulted); - if (!NT_SUCCESS(Status)) { - ERR("RtlGetDaclSecurityDescriptor returned %08x\n", Status); - return NULL; - } - - // FIXME - handle parhasdacl == FALSE - -// OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE - num_aces = 0; - size = sizeof(ACL); - ah = (ACE_HEADER*)¶cl[1]; - for (i = 0; i < paracl->AceCount; i++) { - if (!file && ah->AceFlags & CONTAINER_INHERIT_ACE) { - num_aces++; - size += ah->AceSize; - - if (ah->AceFlags & INHERIT_ONLY_ACE) { - num_aces++; - size += ah->AceSize; - } - } - - if (ah->AceFlags & OBJECT_INHERIT_ACE && (file || !(ah->AceFlags & CONTAINER_INHERIT_ACE))) { - num_aces++; - size += ah->AceSize; - } - - ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize); - } - - acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); - if (!acl) { - ERR("out of memory\n"); - return NULL; - } - - acl->AclRevision = ACL_REVISION; - acl->Sbz1 = 0; - acl->AclSize = size; - acl->AceCount = num_aces; - acl->Sbz2 = 0; - - ah = (ACE_HEADER*)&acl[1]; - parah = (ACE_HEADER*)¶cl[1]; - for (i = 0; i < paracl->AceCount; i++) { - if (!file && parah->AceFlags & CONTAINER_INHERIT_ACE) { - if (parah->AceFlags & INHERIT_ONLY_ACE) { - RtlCopyMemory(ah, parah, parah->AceSize); - ah->AceFlags &= ~INHERIT_ONLY_ACE; - ah->AceFlags |= INHERITED_ACE; - ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE); - - ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize); - } - - RtlCopyMemory(ah, parah, parah->AceSize); - ah->AceFlags |= INHERITED_ACE; - - if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE) - ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE); - - ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize); - } - - if (parah->AceFlags & OBJECT_INHERIT_ACE && (file || !(parah->AceFlags & CONTAINER_INHERIT_ACE))) { - RtlCopyMemory(ah, parah, parah->AceSize); - ah->AceFlags |= INHERITED_ACE; - - if (file) - ah->AceFlags &= ~INHERIT_ONLY_ACE; - else - ah->AceFlags |= INHERIT_ONLY_ACE; - - if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE || file) - ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE); - - ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize); - } - - parah = (ACE_HEADER*)((UINT8*)parah + parah->AceSize); - } - - return acl; -} - // static void STDCALL sid_to_string(PSID sid, char* s) { // sid_header* sh = (sid_header*)sid; // LARGE_INTEGER authnum; @@ -532,7 +438,7 @@ static BOOL get_sd_from_xattr(fcb* fcb) { ERR("RtlSelfRelativeToAbsoluteSD 1 returned %08x\n", Status); } - ERR("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize); + TRACE("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize); newsd2 = sdsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, sdsize, ALLOC_TAG); dacl = daclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, daclsize, ALLOC_TAG); @@ -540,7 +446,7 @@ static BOOL get_sd_from_xattr(fcb* fcb) { owner = ownersize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, ownersize, ALLOC_TAG); group = groupsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, groupsize, ALLOC_TAG); - if ((sdsize > 0 && !newsd2) || (daclsize > 0 && !dacl) || (saclsize > 0 && !sacl) || (ownersize > 0 && !owner) || (groupsize > 0 || !group)) { + if ((sdsize > 0 && !newsd2) || (daclsize > 0 && !dacl) || (saclsize > 0 && !sacl) || (ownersize > 0 && !owner) || (groupsize > 0 && !group)) { ERR("out of memory\n"); if (newsd2) ExFreePool(newsd2); if (dacl) ExFreePool(dacl); @@ -646,16 +552,13 @@ static BOOL get_sd_from_xattr(fcb* fcb) { return TRUE; } -void fcb_get_sd(fcb* fcb, struct _fcb* parent) { +static void get_top_level_sd(fcb* fcb) { NTSTATUS Status; SECURITY_DESCRIPTOR sd; ULONG buflen; ACL* acl = NULL; PSID usersid = NULL, groupsid = NULL; - if (get_sd_from_xattr(fcb)) - goto end; - Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(Status)) { @@ -694,14 +597,10 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent) { } // } - if (!parent) - acl = load_default_acl(); - else - acl = inherit_acl(parent->sd, fcb->type != BTRFS_TYPE_DIRECTORY); + acl = load_default_acl(); if (!acl) { ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } @@ -755,6 +654,47 @@ end: ExFreePool(groupsid); } +void fcb_get_sd(fcb* fcb, struct _fcb* parent) { + NTSTATUS Status; + PSID usersid = NULL, groupsid = NULL; + SECURITY_SUBJECT_CONTEXT subjcont; + + if (get_sd_from_xattr(fcb)) + return; + + if (!parent) { + get_top_level_sd(fcb); + return; + } + + SeCaptureSubjectContext(&subjcont); + + Status = SeAssignSecurityEx(parent->sd, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, SEF_DACL_AUTO_INHERIT, + &subjcont, IoGetFileObjectGenericMapping(), PagedPool); + if (!NT_SUCCESS(Status)) { + ERR("SeAssignSecurityEx returned %08x\n", Status); + } + + uid_to_sid(fcb->inode_item.st_uid, &usersid); + if (!usersid) { + ERR("out of memory\n"); + return; + } + + RtlSetOwnerSecurityDescriptor(&fcb->sd, usersid, FALSE); + + gid_to_sid(fcb->inode_item.st_gid, &groupsid); + if (!groupsid) { + ERR("out of memory\n"); + return; + } + + RtlSetGroupSecurityDescriptor(&fcb->sd, groupsid, FALSE); + + ExFreePool(usersid); + ExFreePool(groupsid); +} + static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) { NTSTATUS Status; fcb* fcb = FileObject->FsContext; @@ -787,8 +727,11 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) NTSTATUS Status; SECURITY_DESCRIPTOR* sd; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + device_extension* Vcb = DeviceObject->DeviceExtension; ULONG buflen; BOOL top_level; + PFILE_OBJECT FileObject = IrpSp->FileObject; + ccb* ccb = FileObject ? FileObject->FsContext2 : NULL; TRACE("query security\n"); @@ -796,6 +739,23 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + + if (!ccb) { + ERR("no ccb\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (Irp->RequestorMode == UserMode && !(ccb->access & READ_CONTROL)) { + WARN("insufficient permissions\n"); + Status = STATUS_ACCESS_DENIED; + goto end; + } + Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; @@ -820,12 +780,13 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) if (Irp->MdlAddress && !sd) { ERR("MmGetSystemAddressForMdlSafe returned NULL\n"); - return STATUS_INSUFFICIENT_RESOURCES; + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; } buflen = IrpSp->Parameters.QuerySecurity.Length; - Status = get_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation); + Status = get_file_security(Vcb, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation); if (NT_SUCCESS(Status)) Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length; @@ -835,12 +796,14 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) } else Irp->IoStatus.Information = 0; +end: TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information); Irp->IoStatus.Status = Status; - IoCompleteRequest( Irp, IO_NO_INCREMENT ); + IoCompleteRequest(Irp, IO_NO_INCREMENT); +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -857,31 +820,26 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi ccb* ccb = FileObject->FsContext2; file_ref* fileref = ccb ? ccb->fileref : NULL; SECURITY_DESCRIPTOR* oldsd; - INODE_ITEM* ii; - KEY searchkey; - traverse_ptr tp; LARGE_INTEGER time; BTRFS_TIME now; - LIST_ENTRY rollback; TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, flags); - InitializeListHead(&rollback); - if (Vcb->readonly) return STATUS_MEDIA_WRITE_PROTECTED; - acquire_tree_lock(Vcb, TRUE); - if (fcb->ads) { if (fileref && fileref->parent) fcb = fileref->parent->fcb; else { ERR("could not find parent fcb for stream\n"); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + goto end; } } + ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) { Status = STATUS_ACCESS_DENIED; goto end; @@ -898,24 +856,6 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi ExFreePool(oldsd); - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; - - Status = find_item(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 INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - delete_tree_item(Vcb, &tp, &rollback); - KeQuerySystemTime(&time); win_time_to_unix(time, &now); @@ -937,48 +877,28 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi fcb->inode_item.st_uid = sid_to_uid(owner); } - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, &rollback)) { - ERR("error - failed to insert INODE_ITEM\n"); - Status = STATUS_INTERNAL_ERROR; - ExFreePool(ii); - goto end; - } - - Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), &rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - ExFreePool(ii); - goto end; - } + fcb->sd_dirty = TRUE; fcb->subvol->root_item.ctransid = Vcb->superblock.generation; fcb->subvol->root_item.ctime = now; - Status = consider_write(Vcb); + mark_fcb_dirty(fcb); + + send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED); end: - if (NT_SUCCESS(Status)) - clear_rollback(&rollback); - else - do_rollback(Vcb, &rollback); + ExReleaseResourceLite(fcb->Header.Resource); - release_tree_lock(Vcb, TRUE); - return Status; } NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + ccb* ccb = FileObject ? FileObject->FsContext2 : NULL; + device_extension* Vcb = DeviceObject->DeviceExtension; + ULONG access_req = 0; BOOL top_level; TRACE("set security\n"); @@ -987,34 +907,58 @@ NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { top_level = is_top_level(Irp); + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + + if (!ccb) { + ERR("no ccb\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; - if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) + if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) { TRACE("OWNER_SECURITY_INFORMATION\n"); + access_req |= WRITE_OWNER; + } - if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) + if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) { TRACE("GROUP_SECURITY_INFORMATION\n"); + access_req |= WRITE_OWNER; + } - if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION) + if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION) { TRACE("DACL_SECURITY_INFORMATION\n"); + access_req |= WRITE_DAC; + } - if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION) + if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION) { TRACE("SACL_SECURITY_INFORMATION\n"); + access_req |= ACCESS_SYSTEM_SECURITY; + } - Status = set_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor, + if ((ccb->access & access_req) != access_req) { + Status = STATUS_ACCESS_DENIED; + WARN("insufficient privileges\n"); + goto end; + } + + Status = set_file_security(DeviceObject->DeviceExtension, FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor, IrpSp->Parameters.SetSecurity.SecurityInformation); +end: Irp->IoStatus.Status = Status; - IoCompleteRequest( Irp, IO_NO_INCREMENT ); + IoCompleteRequest(Irp, IO_NO_INCREMENT); - if (top_level) - IoSetTopLevelIrp(NULL); - TRACE("returning %08x\n", Status); - + +exit: if (top_level) IoSetTopLevelIrp(NULL); @@ -1023,16 +967,16 @@ NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { return Status; } -NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as) { +NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) { NTSTATUS Status; PSID owner; BOOLEAN defaulted; - Status = SeAssignSecurity((fileref && fileref->parent) ? fileref->parent->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY, - &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool); - + Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, + SEF_SACL_AUTO_INHERIT, &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool); + if (!NT_SUCCESS(Status)) { - ERR("SeAssignSecurity returned %08x\n", Status); + ERR("SeAssignSecurityEx returned %08x\n", Status); return Status; } diff --git a/reactos/drivers/filesystems/btrfs/treefuncs.c b/reactos/drivers/filesystems/btrfs/treefuncs.c index 83148874389..d3a63df0fb5 100644 --- a/reactos/drivers/filesystems/btrfs/treefuncs.c +++ b/reactos/drivers/filesystems/btrfs/treefuncs.c @@ -19,355 +19,20 @@ // #define DEBUG_TREE_LOCKS -enum read_tree_status { - ReadTreeStatus_Pending, - ReadTreeStatus_Success, - ReadTreeStatus_Cancelling, - ReadTreeStatus_Cancelled, - ReadTreeStatus_Error, - ReadTreeStatus_CRCError, - ReadTreeStatus_MissingDevice -}; - -struct read_tree_context; - -typedef struct { - struct read_tree_context* context; - UINT8* buf; - PIRP Irp; - IO_STATUS_BLOCK iosb; - enum read_tree_status status; -} read_tree_stripe; - -typedef struct { - KEVENT Event; - NTSTATUS Status; - chunk* c; -// UINT8* buf; - UINT32 buflen; - UINT64 num_stripes; - LONG stripes_left; - UINT64 type; - read_tree_stripe* stripes; -} read_tree_context; - -enum rollback_type { - ROLLBACK_INSERT_ITEM, - ROLLBACK_DELETE_ITEM -}; - typedef struct { enum rollback_type type; void* ptr; LIST_ENTRY list_entry; } rollback_item; -static NTSTATUS STDCALL read_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { - read_tree_stripe* stripe = conptr; - read_tree_context* context = (read_tree_context*)stripe->context; - UINT64 i; - - if (stripe->status == ReadTreeStatus_Cancelling) { - stripe->status = ReadTreeStatus_Cancelled; - goto end; - } - - stripe->iosb = Irp->IoStatus; - - if (NT_SUCCESS(Irp->IoStatus.Status)) { - tree_header* th = (tree_header*)stripe->buf; - UINT32 crc32; - - crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum)); - - if (crc32 == *((UINT32*)th->csum)) { - stripe->status = ReadTreeStatus_Success; - - for (i = 0; i < context->num_stripes; i++) { - if (context->stripes[i].status == ReadTreeStatus_Pending) { - context->stripes[i].status = ReadTreeStatus_Cancelling; - IoCancelIrp(context->stripes[i].Irp); - } - } - - goto end; - } else - stripe->status = ReadTreeStatus_CRCError; - } else { - stripe->status = ReadTreeStatus_Error; - } - -end: - if (InterlockedDecrement(&context->stripes_left) == 0) - KeSetEvent(&context->Event, 0, FALSE); - -// return STATUS_SUCCESS; - return STATUS_MORE_PROCESSING_REQUIRED; -} - -NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) { - CHUNK_ITEM* ci; - CHUNK_ITEM_STRIPE* cis; - read_tree_context* context; - UINT64 i/*, type*/, offset; - NTSTATUS Status; - device** devices; - - // FIXME - make this work with RAID - - if (Vcb->log_to_phys_loaded) { - chunk* c = get_chunk_from_address(Vcb, addr); - - if (!c) { - ERR("get_chunk_from_address failed\n"); - return STATUS_INTERNAL_ERROR; - } - - ci = c->chunk_item; - offset = c->offset; - devices = c->devices; - } else { - LIST_ENTRY* le = Vcb->sys_chunks.Flink; - - ci = NULL; - - while (le != &Vcb->sys_chunks) { - sys_chunk* sc = CONTAINING_RECORD(le, sys_chunk, list_entry); - - if (sc->key.obj_id == 0x100 && sc->key.obj_type == TYPE_CHUNK_ITEM && sc->key.offset <= addr) { - CHUNK_ITEM* chunk_item = sc->data; - - if ((addr - sc->key.offset) < chunk_item->size && chunk_item->num_stripes > 0) { - ci = chunk_item; - offset = sc->key.offset; - cis = (CHUNK_ITEM_STRIPE*)&chunk_item[1]; - - devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * ci->num_stripes, ALLOC_TAG); - if (!devices) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - for (i = 0; i < ci->num_stripes; i++) { - devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid); - } - - break; - } - } - - le = le->Flink; - } - - if (!ci) { - ERR("could not find chunk for %llx in bootstrap\n", addr); - return STATUS_INTERNAL_ERROR; - } - } - -// if (ci->type & BLOCK_FLAG_DUPLICATE) { -// type = BLOCK_FLAG_DUPLICATE; -// } else if (ci->type & BLOCK_FLAG_RAID0) { -// FIXME("RAID0 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID1) { -// FIXME("RAID1 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID10) { -// FIXME("RAID10 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID5) { -// FIXME("RAID5 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else if (ci->type & BLOCK_FLAG_RAID6) { -// FIXME("RAID6 not yet supported\n"); -// return STATUS_NOT_IMPLEMENTED; -// } else { // SINGLE -// type = 0; -// } - - cis = (CHUNK_ITEM_STRIPE*)&ci[1]; - - context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_context), ALLOC_TAG); - if (!context) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlZeroMemory(context, sizeof(read_tree_context)); - KeInitializeEvent(&context->Event, NotificationEvent, FALSE); - - context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_stripe) * ci->num_stripes, ALLOC_TAG); - if (!context->stripes) { - ERR("out of memory\n"); - ExFreePool(context); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlZeroMemory(context->stripes, sizeof(read_tree_stripe) * ci->num_stripes); - - context->buflen = Vcb->superblock.node_size; - context->num_stripes = ci->num_stripes; - context->stripes_left = context->num_stripes; -// context->type = type; - - // FIXME - for RAID, check beforehand whether there's enough devices to satisfy request - - for (i = 0; i < ci->num_stripes; i++) { - PIO_STACK_LOCATION IrpSp; - - if (!devices[i]) { - context->stripes[i].status = ReadTreeStatus_MissingDevice; - context->stripes[i].buf = NULL; - context->stripes_left--; - } else { - context->stripes[i].context = (struct read_tree_context*)context; - context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG); - - if (!context->stripes[i].buf) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE); - - if (!context->stripes[i].Irp) { - ERR("IoAllocateIrp failed\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp); - IrpSp->MajorFunction = IRP_MJ_READ; - - if (devices[i]->devobj->Flags & DO_BUFFERED_IO) { - FIXME("FIXME - buffered IO\n"); - } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) { - context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, Vcb->superblock.node_size, FALSE, FALSE, NULL); - if (!context->stripes[i].Irp->MdlAddress) { - ERR("IoAllocateMdl failed\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess); - } else { - context->stripes[i].Irp->UserBuffer = context->stripes[i].buf; - } - - IrpSp->Parameters.Read.Length = Vcb->superblock.node_size; - IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset; - - context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb; - - IoSetCompletionRoutine(context->stripes[i].Irp, read_tree_completion, &context->stripes[i], TRUE, TRUE, TRUE); - - context->stripes[i].status = ReadTreeStatus_Pending; - } - } - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status != ReadTreeStatus_MissingDevice) { - IoCallDriver(devices[i]->devobj, context->stripes[i].Irp); - } - } - - KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL); - - // FIXME - if checksum error, write good data over bad - - // check if any of the devices return a "user-induced" error - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadTreeStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) { - IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj); - - Status = context->stripes[i].iosb.Status; - goto exit; - } - } - - // check if any of the stripes succeeded - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadTreeStatus_Success) { - RtlCopyMemory(buf, context->stripes[i].buf, Vcb->superblock.node_size); - Status = STATUS_SUCCESS; - goto exit; - } - } - - // if not, see if we got a checksum error - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadTreeStatus_CRCError) { -#ifdef _DEBUG - tree_header* th = (tree_header*)context->stripes[i].buf; - UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum)); -// UINT64 j; - - WARN("stripe %llu had a checksum error\n", i); - WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum)); -#endif - -// for (j = 0; j < ci->num_stripes; j++) { -// WARN("stripe %llu: device = %p, status = %u\n", j, c->devices[j], context->stripes[j].status); -// } -// int3; - - Status = STATUS_IMAGE_CHECKSUM_MISMATCH; - goto exit; - } - } - - // failing that, return the first error we encountered - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].status == ReadTreeStatus_Error) { - Status = context->stripes[i].iosb.Status; - goto exit; - } - } - - // if we somehow get here, return STATUS_INTERNAL_ERROR - - Status = STATUS_INTERNAL_ERROR; - -// for (i = 0; i < ci->num_stripes; i++) { -// ERR("%llx: status = %u, NTSTATUS = %08x\n", i, context->stripes[i].status, context->stripes[i].iosb.Status); -// } -exit: - - for (i = 0; i < ci->num_stripes; i++) { - if (context->stripes[i].Irp) { - if (devices[i]->devobj->Flags & DO_DIRECT_IO) { - MmUnlockPages(context->stripes[i].Irp->MdlAddress); - IoFreeMdl(context->stripes[i].Irp->MdlAddress); - } - IoFreeIrp(context->stripes[i].Irp); - } - - if (context->stripes[i].buf) - ExFreePool(context->stripes[i].buf); - } - - ExFreePool(context->stripes); - ExFreePool(context); - - if (!Vcb->log_to_phys_loaded) - ExFreePool(devices); - - return Status; -} - -NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, 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) { UINT8* buf; NTSTATUS Status; tree_header* th; tree* t; tree_data* td; chunk* c; + shared_data* sd; TRACE("(%p, %llx)\n", Vcb, addr); @@ -377,9 +42,9 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** return STATUS_INSUFFICIENT_RESOURCES; } - Status = read_tree(Vcb, addr, buf); + Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, NULL); if (!NT_SUCCESS(Status)) { - ERR("read_tree returned 0x%08x\n", Status); + ERR("read_data returned 0x%08x\n", Status); ExFreePool(buf); return Status; } @@ -407,8 +72,6 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** t->has_new_address = FALSE; t->write = FALSE; - c = get_chunk_from_address(Vcb, addr); - if (c) t->flags = c->chunk_item->type; else @@ -419,10 +82,31 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** // t->items = ExAllocatePoolWithTag(PagedPool, num_items * sizeof(tree_data), ALLOC_TAG); InitializeListHead(&t->itemlist); + if (t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) { + sd = ExAllocatePoolWithTag(NonPagedPool, sizeof(shared_data), ALLOC_TAG); + if (!sd) { + ERR("out of memory\n"); + ExFreePool(buf); + return STATUS_INSUFFICIENT_RESOURCES; + } + + sd->address = addr; + sd->parent = parent ? parent->header.address : addr; + InitializeListHead(&sd->entries); + + ExInterlockedInsertTailList(&Vcb->shared_extents, &sd->list_entry, &Vcb->shared_extents_lock); + } + if (t->header.level == 0) { // leaf node leaf_node* ln = (leaf_node*)(buf + sizeof(tree_header)); unsigned int i; + if ((t->header.num_items * sizeof(leaf_node)) + sizeof(tree_header) > Vcb->superblock.node_size) { + ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items); + ExFreePool(buf); + return STATUS_INSUFFICIENT_RESOURCES; + } + for (i = 0; i < t->header.num_items; i++) { td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG); if (!td) { @@ -446,6 +130,55 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** } else td->data = NULL; + if ((t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) && + ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA)) { + EXTENT_DATA* ed = (EXTENT_DATA*)td->data; + + if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { + EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; + + if (ed2->size != 0) { + LIST_ENTRY* le; + BOOL found = FALSE; + + TRACE("shared extent %llx,%llx\n", ed2->address, ed2->size); + + le = sd->entries.Flink; + while (le != &sd->entries) { + shared_data_entry* sde = CONTAINING_RECORD(le, shared_data_entry, list_entry); + + if (sde->address == ed2->address && sde->size == ed2->size && sde->edr.root == t->header.tree_id && + sde->edr.objid == ln[i].key.obj_id && sde->edr.offset == ln[i].key.offset - ed2->offset) { + sde->edr.count++; + found = TRUE; + break; + } + + le = le->Flink; + } + + if (!found) { + shared_data_entry* sde = ExAllocatePoolWithTag(PagedPool, sizeof(shared_data_entry), ALLOC_TAG); + + if (!sde) { + ERR("out of memory\n"); + ExFreePool(buf); + return STATUS_INSUFFICIENT_RESOURCES; + } + + sde->address = ed2->address; + sde->size = ed2->size; + sde->edr.root = t->header.tree_id; + sde->edr.objid = ln[i].key.obj_id; + sde->edr.offset = ln[i].key.offset - ed2->offset; + sde->edr.count = 1; + + InsertTailList(&sd->entries, &sde->list_entry); + } + } + } + } + td->size = ln[i].size; td->ignore = FALSE; td->inserted = FALSE; @@ -460,6 +193,12 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** internal_node* in = (internal_node*)(buf + sizeof(tree_header)); unsigned int i; + if ((t->header.num_items * sizeof(internal_node)) + sizeof(tree_header) > Vcb->superblock.node_size) { + ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items); + ExFreePool(buf); + return STATUS_INSUFFICIENT_RESOURCES; + } + for (i = 0; i < t->header.num_items; i++) { td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG); if (!td) { @@ -474,7 +213,6 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** td->treeholder.address = in[i].address; td->treeholder.generation = in[i].generation; td->treeholder.tree = NULL; - init_tree_holder(&td->treeholder); // td->treeholder.nonpaged->status = tree_holder_unloaded; td->ignore = FALSE; td->inserted = FALSE; @@ -587,7 +325,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, func, file, line); + Status = _load_tree(Vcb, th->address, r, &th->tree, t, func, file, line); if (!NT_SUCCESS(Status)) { ERR("load_tree returned %08x\n", Status); ExReleaseResourceLite(&r->nonpaged->load_tree_lock); @@ -964,17 +702,20 @@ void free_trees_root(device_extension* Vcb, root* r) { LIST_ENTRY* nextle = le->Flink; tree* t = CONTAINING_RECORD(le, tree, list_entry); - if (t->root == r && t->header.level == level) { - BOOL top = !t->paritem; - - empty = FALSE; - - free_tree2(t, funcname, __FILE__, __LINE__); - if (top && r->treeholder.tree == t) - r->treeholder.tree = NULL; - - if (IsListEmpty(&Vcb->trees)) - return; + if (t->root == r) { + if (t->header.level == level) { + BOOL top = !t->paritem; + + empty = FALSE; + + free_tree2(t, funcname, __FILE__, __LINE__); + if (top && r->treeholder.tree == t) + r->treeholder.tree = NULL; + + if (IsListEmpty(&Vcb->trees)) + return; + } else if (t->header.level > level) + empty = FALSE; } le = nextle; @@ -986,22 +727,59 @@ void free_trees_root(device_extension* Vcb, root* r) { } void STDCALL free_trees(device_extension* Vcb) { - tree* t; - root* r; - - while (!IsListEmpty(&Vcb->trees)) { - t = CONTAINING_RECORD(Vcb->trees.Flink, tree, list_entry); - r = t->root; + LIST_ENTRY* le; + UINT8 level; + + for (level = 0; level <= 255; level++) { + BOOL empty = TRUE; - ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE); + le = Vcb->trees.Flink; - free_trees_root(Vcb, r); + while (le != &Vcb->trees) { + LIST_ENTRY* nextle = le->Flink; + tree* t = CONTAINING_RECORD(le, tree, list_entry); + root* r = t->root; + + if (t->header.level == level) { + BOOL top = !t->paritem; + + empty = FALSE; + + free_tree2(t, funcname, __FILE__, __LINE__); + if (top && r->treeholder.tree == t) + r->treeholder.tree = NULL; + + if (IsListEmpty(&Vcb->trees)) + goto free_shared; + } else if (t->header.level > level) + empty = FALSE; + + le = nextle; + } - ExReleaseResourceLite(&r->nonpaged->load_tree_lock); + if (empty) + break; + } + +free_shared: + while (!IsListEmpty(&Vcb->shared_extents)) { + shared_data* sd; + + le = RemoveHeadList(&Vcb->shared_extents); + sd = CONTAINING_RECORD(le, shared_data, list_entry); + + while (!IsListEmpty(&sd->entries)) { + LIST_ENTRY* le2 = RemoveHeadList(&sd->entries); + shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry); + + ExFreePool(sde); + } + + ExFreePool(sd); } } -static void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) { +void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) { rollback_item* ri; ri = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_item), ALLOC_TAG); @@ -1031,6 +809,13 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, ptp, rollback); +#ifdef DEBUG_PARANOID + if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { + ERR("ERROR - tree_lock not held exclusively\n"); + int3; + } +#endif + searchkey.obj_id = obj_id; searchkey.obj_type = obj_type; searchkey.offset = offset; @@ -1123,7 +908,7 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN if (!tp.tree->write) { tp.tree->write = TRUE; - Vcb->write_trees++; + Vcb->need_write = TRUE; } if (ptp) @@ -1185,6 +970,11 @@ 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 (tp->item->ignore) { ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); int3; @@ -1195,7 +985,7 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR if (!tp->tree->write) { tp->tree->write = TRUE; - Vcb->write_trees++; + Vcb->need_write = TRUE; } tp->tree->header.num_items--; @@ -1237,6 +1027,9 @@ void clear_rollback(LIST_ENTRY* rollback) { case ROLLBACK_DELETE_ITEM: ExFreePool(ri->ptr); break; + + default: + break; } ExFreePool(ri); @@ -1247,7 +1040,7 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) { rollback_item* ri; while (!IsListEmpty(rollback)) { - LIST_ENTRY* le = RemoveHeadList(rollback); + LIST_ENTRY* le = RemoveTailList(rollback); ri = CONTAINING_RECORD(le, rollback_item, list_entry); switch (ri->type) { @@ -1286,6 +1079,22 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) { ExFreePool(tp); break; } + + case ROLLBACK_INSERT_EXTENT: + { + extent* ext = ri->ptr; + + ext->ignore = TRUE; + break; + } + + case ROLLBACK_DELETE_EXTENT: + { + extent* ext = ri->ptr; + + ext->ignore = FALSE; + break; + } } ExFreePool(ri); diff --git a/reactos/drivers/filesystems/btrfs/worker-thread.c b/reactos/drivers/filesystems/btrfs/worker-thread.c new file mode 100644 index 00000000000..84c289374dc --- /dev/null +++ b/reactos/drivers/filesystems/btrfs/worker-thread.c @@ -0,0 +1,108 @@ +#include "btrfs_drv.h" + +void do_read_job(PIRP Irp) { + NTSTATUS Status; + ULONG bytes_read; + BOOL top_level = is_top_level(Irp); + + Irp->IoStatus.Information = 0; + + Status = do_read(Irp, TRUE, &bytes_read); + + Irp->IoStatus.Status = Status; + +// // fastfat doesn't do this, but the Wine ntdll file test seems to think we ought to +// if (Irp->UserIosb) +// *Irp->UserIosb = Irp->IoStatus; + + TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status); + TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information); + TRACE("returning %08x\n", Status); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (top_level) + IoSetTopLevelIrp(NULL); +} + +void do_write_job(device_extension* Vcb, PIRP Irp) { + BOOL top_level = is_top_level(Irp); + NTSTATUS Status; + + _SEH2_TRY { + Status = write_file(Vcb, Irp, TRUE, TRUE); + } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + Status = _SEH2_GetExceptionCode(); + } _SEH2_END; + + Irp->IoStatus.Status = Status; + + TRACE("wrote %u bytes\n", Irp->IoStatus.Information); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (top_level) + IoSetTopLevelIrp(NULL); + + TRACE("returning %08x\n", Status); +} + +static void do_job(drv_thread* thread, LIST_ENTRY* le) { + thread_job* tj = CONTAINING_RECORD(le, thread_job, list_entry); + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(tj->Irp); + + if (IrpSp->MajorFunction == IRP_MJ_READ) { + do_read_job(tj->Irp); + } else if (IrpSp->MajorFunction == IRP_MJ_WRITE) { + do_write_job(thread->DeviceObject->DeviceExtension, tj->Irp); + } else { + ERR("unsupported major function %x\n", IrpSp->MajorFunction); + tj->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; + tj->Irp->IoStatus.Information = 0; + IoCompleteRequest(tj->Irp, IO_NO_INCREMENT); + } + + ExFreePool(tj); +} + +void STDCALL worker_thread(void* context) { + drv_thread* thread = context; + KIRQL irql; + + ObReferenceObject(thread->DeviceObject); + + while (TRUE) { + KeWaitForSingleObject(&thread->event, Executive, KernelMode, FALSE, NULL); + + FsRtlEnterFileSystem(); + + while (TRUE) { + LIST_ENTRY* le; + + KeAcquireSpinLock(&thread->spin_lock, &irql); + + if (IsListEmpty(&thread->jobs)) { + KeReleaseSpinLock(&thread->spin_lock, irql); + break; + } + + le = thread->jobs.Flink; + RemoveEntryList(le); + + KeReleaseSpinLock(&thread->spin_lock, irql); + + do_job(thread, le); + } + + FsRtlExitFileSystem(); + + if (thread->quit) + break; + } + + ObDereferenceObject(thread->DeviceObject); + + KeSetEvent(&thread->finished, 0, FALSE); + + PsTerminateSystemThread(STATUS_SUCCESS); +} diff --git a/reactos/drivers/filesystems/btrfs/write.c b/reactos/drivers/filesystems/btrfs/write.c index e166b5064ef..7df8f5ef596 100644 --- a/reactos/drivers/filesystems/btrfs/write.c +++ b/reactos/drivers/filesystems/btrfs/write.c @@ -19,6 +19,8 @@ #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node)) +// #define DEBUG_WRITE_LOOPS + // BOOL did_split; BOOL chunk_test = FALSE; @@ -45,13 +47,9 @@ typedef struct { TREE_BLOCK_REF tbr; } EXTENT_ITEM_SKINNY_METADATA; -typedef struct { - CHUNK_ITEM ci; - CHUNK_ITEM_STRIPE stripes[1]; -} CHUNK_ITEM2; - -static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len); -static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data); +// 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 NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { write_context* context = conptr; @@ -158,14 +156,13 @@ static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) Status = STATUS_INTERNAL_ERROR; #endif - // FIXME - work with RAID + RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM)); // FIXME - only write one superblock if on SSD (?) - while (superblock_addrs[i] > 0 && Vcb->length >= superblock_addrs[i] + sizeof(superblock)) { + while (superblock_addrs[i] > 0 && device->length >= superblock_addrs[i] + sizeof(superblock)) { TRACE("writing superblock %u\n", i); Vcb->superblock.sb_phys_addr = superblock_addrs[i]; - RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM)); crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum)); crc32 = ~crc32; @@ -179,296 +176,105 @@ static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) i++; } + + if (i == 0) { + ERR("no superblocks written!\n"); + } return Status; } static BOOL find_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) { LIST_ENTRY* le; - space *s, *bestfit = NULL; + space* s; TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address); - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); + if (IsListEmpty(&c->space_size)) + return FALSE; + + le = c->space_size.Flink; + while (le != &c->space_size) { + s = CONTAINING_RECORD(le, space, list_entry_size); - if (s->type == SPACE_TYPE_FREE) { - if (s->size == length) { - *address = s->offset; - TRACE("returning exact fit at %llx\n", s->offset); - return TRUE; - } else if (s->size > length && (!bestfit || bestfit->size > s->size)) { - bestfit = s; - } + if (s->size == length) { + *address = s->address; + return TRUE; + } else if (s->size < length) { + if (le == c->space_size.Flink) + return FALSE; + + s = CONTAINING_RECORD(le->Blink, space, list_entry_size); + + *address = s->address; + return TRUE; } le = le->Flink; } - if (bestfit) { - TRACE("returning best fit at %llx\n", bestfit->offset); - *address = bestfit->offset; + s = CONTAINING_RECORD(c->space_size.Blink, space, list_entry_size); + + if (s->size > length) { + *address = s->address; return TRUE; } return FALSE; } -void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type) { - LIST_ENTRY *le = c->space.Flink, *nextle, *insbef; - space *s, *s2, *s3; -#ifdef DEBUG_PARANOID - UINT64 lastaddr; -#endif - - TRACE("(%p, %llx, %llx, %x)\n", c, offset, size, type); - -#ifdef DEBUG_PARANOID - // TESTING - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - - TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type); - - le = le->Flink; - } -#endif - - c->space_changed = TRUE; - - le = c->space.Flink; - insbef = &c->space; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - nextle = le->Flink; - - if (s->offset >= offset + size) { - insbef = le; - break; - } - - if (s->offset >= offset && s->offset + s->size <= offset + size) { // delete entirely - if (s->offset + s->size == offset + size) { - insbef = s->list_entry.Flink; - RemoveEntryList(&s->list_entry); - ExFreePool(s); - break; - } - - RemoveEntryList(&s->list_entry); - ExFreePool(s); - } else if (s->offset < offset && s->offset + s->size > offset + size) { // split in two - s3 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); - if (!s3) { - ERR("out of memory\n"); - return; - } - - s3->offset = offset + size; - s3->size = s->size - size - offset + s->offset; - s3->type = s->type; - InsertHeadList(&s->list_entry, &s3->list_entry); - insbef = &s3->list_entry; - - s->size = offset - s->offset; - break; - } else if (s->offset + s->size > offset && s->offset + s->size <= offset + size) { // truncate before - s->size = offset - s->offset; - } else if (s->offset < offset + size && s->offset + s->size > offset + size) { // truncate after - s->size -= offset + size - s->offset; - s->offset = offset + size; - - insbef = le; - break; - } - - le = nextle; - } - - s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); - if (!s2) { - ERR("out of memory\n"); - return; - } - - s2->offset = offset; - s2->size = size; - s2->type = type; - InsertTailList(insbef, &s2->list_entry); - - // merge entries if same type - - if (s2->list_entry.Blink != &c->space) { - s = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry); - - if (s->type == type) { - s->size += s2->size; - - RemoveEntryList(&s2->list_entry); - ExFreePool(s2); - - s2 = s; - } - } - - if (s2->list_entry.Flink != &c->space) { - s = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry); - - if (s->type == type) { - s2->size += s->size; - - RemoveEntryList(&s->list_entry); - ExFreePool(s); - } - } - - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - - TRACE("%llx,%llx,%x\n", s->offset, s->size, s->type); - - le = le->Flink; - } - -#ifdef DEBUG_PARANOID - // TESTING - lastaddr = c->offset; - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - - if (s->offset != lastaddr) { - ERR("inconsistency detected!\n"); - int3; - } - - lastaddr = s->offset + s->size; - - le = le->Flink; - } - - if (lastaddr != c->offset + c->chunk_item->size) { - ERR("inconsistency detected - space doesn't run all the way to end of chunk\n"); - int3; - } -#endif -} - chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) { LIST_ENTRY* le2; chunk* c; + ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE); + le2 = Vcb->chunks.Flink; while (le2 != &Vcb->chunks) { c = CONTAINING_RECORD(le2, chunk, list_entry); // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size); - if (address >= c->offset && address < c->offset + c->chunk_item->size) + if (address >= c->offset && address < c->offset + c->chunk_item->size) { + ExReleaseResourceLite(&Vcb->chunk_lock); return c; + } le2 = le2->Flink; } + ExReleaseResourceLite(&Vcb->chunk_lock); + return NULL; } typedef struct { - disk_hole* dh; + space* dh; device* device; } stripe; -static void add_provisional_disk_hole(device_extension* Vcb, stripe* s, UINT64 max_stripe_size) { -// LIST_ENTRY* le = s->device->disk_holes.Flink; -// disk_hole* dh; - -// ERR("old holes:\n"); -// while (le != &s->device->disk_holes) { -// dh = CONTAINING_RECORD(le, disk_hole, listentry); -// -// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional); -// -// le = le->Flink; -// } - - if (s->dh->size <= max_stripe_size) { - s->dh->provisional = TRUE; - } else { - disk_hole* newdh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG); - if (!newdh) { - ERR("out of memory\n"); - return; - } - - newdh->address = s->dh->address + max_stripe_size; - newdh->size = s->dh->size - max_stripe_size; - newdh->provisional = FALSE; - InsertTailList(&s->device->disk_holes, &newdh->listentry); - - s->dh->size = max_stripe_size; - s->dh->provisional = TRUE; - } - -// ERR("new holes:\n"); -// le = s->device->disk_holes.Flink; -// while (le != &s->device->disk_holes) { -// dh = CONTAINING_RECORD(le, disk_hole, listentry); -// -// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional); -// -// le = le->Flink; -// } -} - static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) { - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b; UINT64 lastaddr; - NTSTATUS Status; - - searchkey.obj_id = 0x100; - searchkey.obj_type = TYPE_CHUNK_ITEM; - searchkey.offset = 0; - - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return 0xffffffffffffffff; - } + LIST_ENTRY* le; lastaddr = 0; - do { - if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) { - 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 { - CHUNK_ITEM* ci = (CHUNK_ITEM*)tp.item->data; - - if (tp.item->key.offset >= lastaddr + size) - return lastaddr; - - lastaddr = tp.item->key.offset + ci->size; - } - } + le = Vcb->chunks.Flink; + while (le != &Vcb->chunks) { + chunk* c = CONTAINING_RECORD(le, chunk, list_entry); - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type) - break; - } - } while (b); + if (c->offset >= lastaddr + size) + return lastaddr; + + lastaddr = c->offset + c->chunk_item->size; + + le = le->Flink; + } return lastaddr; } -static BOOL increase_dev_item_used(device_extension* Vcb, device* device, UINT64 size, LIST_ENTRY* rollback) { +static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTRY* rollback) { KEY searchkey; traverse_ptr tp; DEV_ITEM* di; @@ -481,106 +287,57 @@ static BOOL increase_dev_item_used(device_extension* Vcb, device* device, UINT64 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); - return FALSE; + return Status; } if (keycmp(&tp.item->key, &searchkey)) { ERR("error - could not find DEV_ITEM for device %llx\n", device->devitem.dev_id); - return FALSE; + return STATUS_INTERNAL_ERROR; } delete_tree_item(Vcb, &tp, rollback); - device->devitem.bytes_used += size; - di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG); if (!di) { ERR("out of memory\n"); - return FALSE; + return STATUS_INSUFFICIENT_RESOURCES; } 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)) { ERR("insert_tree_item failed\n"); - return FALSE; + return STATUS_INTERNAL_ERROR; } - return TRUE; + return STATUS_SUCCESS; } -static void reset_disk_holes(device* device, BOOL commit) { - LIST_ENTRY* le = device->disk_holes.Flink; - disk_hole* dh; - -// ERR("old holes:\n"); -// while (le != &device->disk_holes) { -// dh = CONTAINING_RECORD(le, disk_hole, listentry); -// -// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional); -// -// le = le->Flink; -// } +static void regen_bootstrap(device_extension* Vcb) { + sys_chunk* sc2; + USHORT i = 0; + LIST_ENTRY* le; - le = device->disk_holes.Flink; - while (le != &device->disk_holes) { - LIST_ENTRY* le2 = le->Flink; + i = 0; + le = Vcb->sys_chunks.Flink; + while (le != &Vcb->sys_chunks) { + sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); - dh = CONTAINING_RECORD(le, disk_hole, listentry); + TRACE("%llx,%x,%llx\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset); - if (dh->provisional) { - if (commit) { - RemoveEntryList(le); - ExFreePool(dh); - } else { - dh->provisional = FALSE; - } - } + RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY)); + i += sizeof(KEY); - le = le2; + RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size); + i += sc2->size; + + le = le->Flink; } - - if (!commit) { - le = device->disk_holes.Flink; - while (le != &device->disk_holes) { - LIST_ENTRY* le2 = le->Flink; - - dh = CONTAINING_RECORD(le, disk_hole, listentry); - - while (le2 != &device->disk_holes) { - disk_hole* dh2 = CONTAINING_RECORD(le2, disk_hole, listentry); - - if (dh2->address == dh->address + dh->size) { - LIST_ENTRY* le3 = le2->Flink; - dh->size += dh2->size; - - RemoveEntryList(le2); - ExFreePool(dh2); - - le2 = le3; - } else - break; - } - - le = le->Flink; - } - } - -// ERR("new holes:\n"); -// le = device->disk_holes.Flink; -// while (le != &device->disk_holes) { -// dh = CONTAINING_RECORD(le, disk_hole, listentry); -// -// ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional); -// -// le = le->Flink; -// } } static NTSTATUS add_to_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, ULONG size) { sys_chunk *sc, *sc2; LIST_ENTRY* le; - USHORT i; if (Vcb->superblock.n + sizeof(KEY) + size > SYS_CHUNK_ARRAY_SIZE) { ERR("error - bootstrap is full\n"); @@ -619,38 +376,125 @@ static NTSTATUS add_to_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj Vcb->superblock.n += sizeof(KEY) + size; - i = 0; - le = Vcb->sys_chunks.Flink; - while (le != &Vcb->sys_chunks) { - sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); - - TRACE("%llx,%x,%llx\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset); - - RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY)); - i += sizeof(KEY); - - RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size); - i += sc2->size; - - le = le->Flink; - } + regen_bootstrap(Vcb); return STATUS_SUCCESS; } +static BOOL find_new_dup_stripes(device_extension* Vcb, stripe* stripes, UINT64 max_stripe_size) { + UINT64 j, devnum, devusage = 0xffffffffffffffff; + space *devdh1 = NULL, *devdh2 = NULL; + + for (j = 0; j < Vcb->superblock.num_devices; j++) { + UINT64 usage; + + usage = (Vcb->devices[j].devitem.bytes_used * 4096) / Vcb->devices[j].devitem.num_bytes; + + // favour devices which have been used the least + if (usage < devusage) { + if (!IsListEmpty(&Vcb->devices[j].space)) { + LIST_ENTRY* le; + space *dh1 = NULL, *dh2 = NULL; + + le = Vcb->devices[j].space.Flink; + while (le != &Vcb->devices[j].space) { + space* dh = CONTAINING_RECORD(le, space, list_entry); + + if (dh->size >= max_stripe_size && (!dh1 || dh->size < dh1->size)) { + dh2 = dh1; + dh1 = dh; + } + + le = le->Flink; + } + + if (dh1 && (dh2 || dh1->size >= 2 * max_stripe_size)) { + devnum = j; + devusage = usage; + devdh1 = dh1; + devdh2 = dh2 ? dh2 : dh1; + } + } + } + } + + if (!devdh1) + return FALSE; + + stripes[0].device = &Vcb->devices[devnum]; + stripes[0].dh = devdh1; + stripes[1].device = stripes[0].device; + stripes[1].dh = devdh2; + + return TRUE; +} + +static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size, UINT16 type) { + UINT64 j, k, devnum = 0xffffffffffffffff, devusage = 0xffffffffffffffff; + space* devdh = NULL; + + for (j = 0; j < Vcb->superblock.num_devices; j++) { + UINT64 usage; + BOOL skip = FALSE; + + // skip this device if it already has a stripe + if (i > 0) { + for (k = 0; k < i; k++) { + if (stripes[k].device == &Vcb->devices[j]) { + skip = TRUE; + break; + } + } + } + + if (!skip) { + usage = (Vcb->devices[j].devitem.bytes_used * 4096) / Vcb->devices[j].devitem.num_bytes; + + // favour devices which have been used the least + if (usage < devusage) { + if (!IsListEmpty(&Vcb->devices[j].space)) { + LIST_ENTRY* le; + + le = Vcb->devices[j].space.Flink; + while (le != &Vcb->devices[j].space) { + space* dh = CONTAINING_RECORD(le, space, list_entry); + + if ((devnum != j && dh->size >= max_stripe_size) || + (devnum == j && dh->size >= max_stripe_size && dh->size < devdh->size) + ) { + devdh = dh; + devnum = j; + devusage = usage; + } + + le = le->Flink; + } + } + } + } + } + + if (!devdh) + return FALSE; + + stripes[i].dh = devdh; + stripes[i].device = &Vcb->devices[devnum]; + + return TRUE; +} + chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { - UINT64 max_stripe_size, max_chunk_size, stripe_size; - UINT64 total_size = 0, i, j, logaddr; - int num_stripes; - disk_hole* dh; - stripe* stripes; + 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; + stripe* stripes = NULL; ULONG cisize; - CHUNK_ITEM* ci; CHUNK_ITEM_STRIPE* cis; chunk* c = NULL; space* s = NULL; BOOL success = FALSE; - BLOCK_GROUP_ITEM* bgi; + + ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); for (i = 0; i < Vcb->superblock.num_devices; i++) { total_size += Vcb->devices[i].devitem.num_bytes; @@ -672,80 +516,74 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { max_chunk_size = 2 * max_stripe_size; } - // FIXME - make sure whole number of sectors? max_chunk_size = min(max_chunk_size, total_size / 10); // cap at 10% TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size); if (flags & BLOCK_FLAG_DUPLICATE) { - num_stripes = 2; + min_stripes = 2; + max_stripes = 2; + sub_stripes = 1; + type = BLOCK_FLAG_DUPLICATE; } else if (flags & BLOCK_FLAG_RAID0) { - FIXME("RAID0 not yet supported\n"); - return NULL; + min_stripes = 2; + max_stripes = Vcb->superblock.num_devices; + sub_stripes = 1; + type = BLOCK_FLAG_RAID0; } else if (flags & BLOCK_FLAG_RAID1) { - FIXME("RAID1 not yet supported\n"); - return NULL; + min_stripes = 2; + max_stripes = 2; + sub_stripes = 1; + type = BLOCK_FLAG_RAID1; } else if (flags & BLOCK_FLAG_RAID10) { - FIXME("RAID10 not yet supported\n"); - return NULL; + min_stripes = 4; + max_stripes = Vcb->superblock.num_devices; + sub_stripes = 2; + type = BLOCK_FLAG_RAID10; } else if (flags & BLOCK_FLAG_RAID5) { FIXME("RAID5 not yet supported\n"); - return NULL; + goto end; } else if (flags & BLOCK_FLAG_RAID6) { FIXME("RAID6 not yet supported\n"); - return NULL; + goto end; } else { // SINGLE - num_stripes = 1; + min_stripes = 1; + max_stripes = 1; + sub_stripes = 1; + type = 0; } - stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * num_stripes, ALLOC_TAG); + stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * max_stripes, ALLOC_TAG); if (!stripes) { ERR("out of memory\n"); - return NULL; + goto end; } - for (i = 0; i < num_stripes; i++) { - stripes[i].dh = NULL; - - for (j = 0; j < Vcb->superblock.num_devices; j++) { - LIST_ENTRY* le = Vcb->devices[j].disk_holes.Flink; - - while (le != &Vcb->devices[j].disk_holes) { - dh = CONTAINING_RECORD(le, disk_hole, listentry); - - if (!dh->provisional) { - if (!stripes[i].dh || dh->size > stripes[i].dh->size) { - stripes[i].dh = dh; - stripes[i].device = &Vcb->devices[j]; - - if (stripes[i].dh->size >= max_stripe_size) - break; - } - } - - le = le->Flink; - } - - if (stripes[i].dh && stripes[i].dh->size >= max_stripe_size) - break; - } - - if (stripes[i].dh) { - TRACE("good DH: device %llx, address %llx, size %llx\n", stripes[i].device->devitem.dev_id, stripes[i].dh->address, stripes[i].dh->size); - } else { - TRACE("good DH not found\n"); + num_stripes = 0; + + if (type == BLOCK_FLAG_DUPLICATE) { + if (!find_new_dup_stripes(Vcb, stripes, max_stripe_size)) goto end; + else + num_stripes = max_stripes; + } else { + for (i = 0; i < max_stripes; i++) { + if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, type)) + break; + else + num_stripes++; } - - add_provisional_disk_hole(Vcb, &stripes[i], max_stripe_size); } - stripe_size = min(stripes[0].dh->size, max_stripe_size); - for (i = 1; i < num_stripes; i++) { - stripe_size = min(stripe_size, stripes[1].dh->size); + // for RAID10, round down to an even number of stripes + if (type == BLOCK_FLAG_RAID10 && (num_stripes % sub_stripes) != 0) { + num_stripes -= num_stripes % sub_stripes; + } + + if (num_stripes < min_stripes) { + WARN("found %u stripes, needed at least %u\n", num_stripes, min_stripes); + goto end; } - // FIXME - make sure stripe_size aligned properly - // FIXME - obey max_chunk_size c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG); if (!c) { @@ -753,82 +591,90 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { 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)); - ci = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG); - if (!ci) { - ERR("out of memory\n"); - goto end; - } - - ci->size = stripe_size; // FIXME for RAID - ci->root_id = Vcb->extent_root->id; - ci->stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel - ci->type = flags; - ci->opt_io_alignment = ci->stripe_length; - ci->opt_io_width = ci->stripe_length; - ci->sector_size = stripes[0].device->devitem.minimal_io_size; - ci->num_stripes = num_stripes; - ci->sub_stripes = 1; - - c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG); - if (!c->devices) { - ERR("out of memory\n"); - ExFreePool(ci); - goto end; - } - - for (i = 0; i < num_stripes; i++) { - if (i == 0) - cis = (CHUNK_ITEM_STRIPE*)&ci[1]; - else - cis = &cis[1]; - - cis->dev_id = stripes[i].device->devitem.dev_id; - cis->offset = stripes[i].dh->address; - cis->dev_uuid = stripes[i].device->devitem.device_uuid; - - c->devices[i] = stripes[i].device; - } - - logaddr = find_new_chunk_address(Vcb, ci->size); - if (logaddr == 0xffffffffffffffff) { - ERR("find_new_chunk_address failed\n"); - ExFreePool(ci); - goto end; - } - - if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ci); - goto end; - } - - if (flags & BLOCK_FLAG_SYSTEM) { - NTSTATUS Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, logaddr, ci, cisize); - if (!NT_SUCCESS(Status)) { - ERR("add_to_bootstrap returned %08x\n", Status); - goto end; - } - } - - Vcb->superblock.chunk_root_generation = Vcb->superblock.generation; - c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG); if (!c->chunk_item) { ERR("out of memory\n"); goto end; } - RtlCopyMemory(c->chunk_item, ci, cisize); + stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel + + stripe_size = max_stripe_size; + for (i = 0; i < num_stripes; i++) { + if (stripes[i].dh->size < stripe_size) + stripe_size = stripes[i].dh->size; + } + + if (type == 0 || type == BLOCK_FLAG_DUPLICATE || type == BLOCK_FLAG_RAID1) + factor = 1; + else if (type == BLOCK_FLAG_RAID0) + factor = num_stripes; + else if (type == BLOCK_FLAG_RAID10) + factor = num_stripes / sub_stripes; + + if (stripe_size * factor > max_chunk_size) + stripe_size = max_chunk_size / factor; + + if (stripe_size % stripe_length > 0) + stripe_size -= stripe_size % stripe_length; + + if (stripe_size == 0) + goto end; + + c->chunk_item->size = stripe_size * factor; + c->chunk_item->root_id = Vcb->extent_root->id; + c->chunk_item->stripe_length = stripe_length; + c->chunk_item->type = flags; + c->chunk_item->opt_io_alignment = c->chunk_item->stripe_length; + c->chunk_item->opt_io_width = c->chunk_item->stripe_length; + c->chunk_item->sector_size = stripes[0].device->devitem.minimal_io_size; + c->chunk_item->num_stripes = num_stripes; + c->chunk_item->sub_stripes = sub_stripes; + + c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG); + if (!c->devices) { + ERR("out of memory\n"); + goto end; + } + + cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; + for (i = 0; i < num_stripes; i++) { + cis[i].dev_id = stripes[i].device->devitem.dev_id; + + if (type == BLOCK_FLAG_DUPLICATE && i == 1 && stripes[i].dh == stripes[0].dh) + cis[i].offset = stripes[0].dh->address + stripe_size; + else + cis[i].offset = stripes[i].dh->address; + + cis[i].dev_uuid = stripes[i].device->devitem.device_uuid; + + c->devices[i] = stripes[i].device; + } + + logaddr = find_new_chunk_address(Vcb, c->chunk_item->size); + + Vcb->superblock.chunk_root_generation = Vcb->superblock.generation; + c->size = cisize; c->offset = logaddr; c->used = c->oldused = 0; - c->space_changed = FALSE; - c->cache_size = 0; - c->cache_inode = 0; + c->cache = NULL; InitializeListHead(&c->space); + InitializeListHead(&c->space_size); + InitializeListHead(&c->deleting); + InitializeListHead(&c->changed_extents); + + ExInitializeResourceLite(&c->nonpaged->lock); + ExInitializeResourceLite(&c->nonpaged->changed_extents_lock); s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); if (!s) { @@ -836,217 +682,454 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) { goto end; } - s->offset = c->offset; + s->address = c->offset; s->size = c->chunk_item->size; - s->type = SPACE_TYPE_FREE; InsertTailList(&c->space, &s->list_entry); + InsertTailList(&c->space_size, &s->list_entry_size); protect_superblocks(Vcb, c); - // add BLOCK_GROUP_ITEM to tree 2 - - bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG); - if (!bgi) { - ERR("out of memory\n"); - goto end; - } - - bgi->used = 0; - bgi->chunk_tree = 0x100; - bgi->flags = flags; - - if (!insert_tree_item(Vcb, Vcb->extent_root, logaddr, TYPE_BLOCK_GROUP_ITEM, ci->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(bgi); - goto end; - } - - // add DEV_EXTENTs to tree 4 - for (i = 0; i < num_stripes; i++) { - DEV_EXTENT* de; + stripes[i].device->devitem.bytes_used += stripe_size; - de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG); - if (!de) { - ERR("out of memory\n"); - goto end; - } - - de->chunktree = Vcb->chunk_root->id; - de->objid = 0x100; - de->address = logaddr; - de->length = ci->size; - de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid; - - if (!insert_tree_item(Vcb, Vcb->dev_root, stripes[i].device->devitem.dev_id, TYPE_DEV_EXTENT, stripes[i].dh->address, de, sizeof(DEV_EXTENT), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(de); - goto end; - } - - if (!increase_dev_item_used(Vcb, stripes[i].device, ci->size, rollback)) { - ERR("increase_dev_item_used failed\n"); - goto end; - } - } - - for (i = 0; i < num_stripes; i++) { - BOOL b = FALSE; - for (j = 0; j < i; j++) { - if (stripes[j].device == stripes[i].device) - b = TRUE; - } - - if (!b) - reset_disk_holes(stripes[i].device, TRUE); + space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, rollback); } success = TRUE; end: - ExFreePool(stripes); + if (stripes) + ExFreePool(stripes); if (!success) { - for (i = 0; i < num_stripes; i++) { - BOOL b = FALSE; - for (j = 0; j < i; j++) { - if (stripes[j].device == stripes[i].device) - b = TRUE; - } - - if (!b) - reset_disk_holes(stripes[i].device, FALSE); - } - + if (c && c->chunk_item) ExFreePool(c->chunk_item); if (c) ExFreePool(c); if (s) ExFreePool(s); - } else - InsertTailList(&Vcb->chunks, &c->list_entry); + } else { + LIST_ENTRY* le; + BOOL done = FALSE; + + le = Vcb->chunks.Flink; + while (le != &Vcb->chunks) { + chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry); + + if (c2->offset > c->offset) { + InsertHeadList(le->Blink, &c->list_entry); + done = TRUE; + break; + } + + le = le->Flink; + } + + if (!done) + InsertTailList(&Vcb->chunks, &c->list_entry); + + c->created = TRUE; + InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed); + } + + ExReleaseResourceLite(&Vcb->chunk_lock); return success ? c : NULL; } -NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length) { - KEY searchkey; - traverse_ptr tp; - CHUNK_ITEM2* ci; +NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp) { NTSTATUS Status; UINT32 i; + chunk* c; + CHUNK_ITEM_STRIPE* cis; + write_data_stripe* stripe; + UINT64 *stripestart = NULL, *stripeend = NULL; + UINT8** stripedata = NULL; + BOOL need_free2; TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length); - // FIXME - use version cached in Vcb - - searchkey.obj_id = 0x100; // fixed? - searchkey.obj_type = TYPE_CHUNK_ITEM; - searchkey.offset = address; - - Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; + c = get_chunk_from_address(Vcb, address); + if (!c) { + ERR("could not get chunk for address %llx\n", address); + return STATUS_INTERNAL_ERROR; } - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { - ERR("error - unexpected item in chunk tree\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; + if (c->chunk_item->type & BLOCK_FLAG_RAID5) { + FIXME("RAID5 not yet supported\n"); + return STATUS_NOT_IMPLEMENTED; + } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) { + FIXME("RAID6 not yet supported\n"); + return STATUS_NOT_IMPLEMENTED; } - if (tp.item->size < sizeof(CHUNK_ITEM2)) { - 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_ITEM2)); - Status = STATUS_INTERNAL_ERROR; - goto end; + stripestart = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); + if (!stripestart) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; } - ci = (CHUNK_ITEM2*)tp.item->data; - - if (tp.item->key.offset > address || tp.item->key.offset + ci->ci.size < address) { - ERR("error - address %llx was out of chunk bounds\n", address); - Status = STATUS_INTERNAL_ERROR; - goto end; + stripeend = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); + if (!stripeend) { + ERR("out of memory\n"); + ExFreePool(stripestart); + return STATUS_INSUFFICIENT_RESOURCES; } - // FIXME - only do this for chunks marked DUPLICATE? - // FIXME - for multiple writes, if PENDING do waits at the end - // FIXME - work with RAID - for (i = 0; i < ci->ci.num_stripes; i++) { - Status = write_data_phys(Vcb->devices[0].devobj, address - tp.item->key.offset + ci->stripes[i].offset, data, length); - if (!NT_SUCCESS(Status)) { - ERR("error - write_data_phys failed\n"); + stripedata = ExAllocatePoolWithTag(PagedPool, sizeof(UINT8*) * c->chunk_item->num_stripes, ALLOC_TAG); + if (!stripedata) { + ERR("out of memory\n"); + ExFreePool(stripeend); + ExFreePool(stripestart); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(stripedata, sizeof(UINT8*) * c->chunk_item->num_stripes); + + cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; + + if (c->chunk_item->type & BLOCK_FLAG_RAID0) { + UINT64 startoff, endoff; + UINT16 startoffstripe, endoffstripe, stripenum; + UINT64 pos, *stripeoff; + + stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); + if (!stripeoff) { + ERR("out of memory\n"); + ExFreePool(stripedata); + ExFreePool(stripeend); + ExFreePool(stripestart); + return STATUS_INSUFFICIENT_RESOURCES; + } + + get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe); + get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe); + + for (i = 0; i < c->chunk_item->num_stripes; i++) { + if (startoffstripe > i) { + stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; + } else if (startoffstripe == i) { + stripestart[i] = startoff; + } else { + stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length); + } + + if (endoffstripe > i) { + stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; + } else if (endoffstripe == i) { + stripeend[i] = endoff + 1; + } else { + stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length); + } + + if (stripestart[i] != stripeend[i]) { + stripedata[i] = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG); + + if (!stripedata[i]) { + ERR("out of memory\n"); + ExFreePool(stripeoff); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + } + } + + pos = 0; + RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes); + + stripenum = startoffstripe; + while (pos < length) { + if (pos == 0) { + UINT32 writelen = min(stripeend[stripenum] - stripestart[stripenum], + c->chunk_item->stripe_length - (stripestart[stripenum] % c->chunk_item->stripe_length)); + + RtlCopyMemory(stripedata[stripenum], data, writelen); + stripeoff[stripenum] += writelen; + pos += writelen; + } else if (length - pos < c->chunk_item->stripe_length) { + RtlCopyMemory(stripedata[stripenum] + stripeoff[stripenum], (UINT8*)data + pos, length - pos); + break; + } else { + RtlCopyMemory(stripedata[stripenum] + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length); + stripeoff[stripenum] += c->chunk_item->stripe_length; + pos += c->chunk_item->stripe_length; + } + + stripenum = (stripenum + 1) % c->chunk_item->num_stripes; + } + + ExFreePool(stripeoff); + + if (need_free) + ExFreePool(data); + + need_free2 = TRUE; + } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) { + UINT64 startoff, endoff; + UINT16 startoffstripe, endoffstripe, stripenum; + UINT64 pos, *stripeoff; + + stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes, ALLOC_TAG); + if (!stripeoff) { + ERR("out of memory\n"); + ExFreePool(stripedata); + ExFreePool(stripeend); + ExFreePool(stripestart); + return STATUS_INSUFFICIENT_RESOURCES; + } + + get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &startoff, &startoffstripe); + get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &endoff, &endoffstripe); + + startoffstripe *= c->chunk_item->sub_stripes; + endoffstripe *= c->chunk_item->sub_stripes; + + for (i = 0; i < c->chunk_item->num_stripes; i += c->chunk_item->sub_stripes) { + UINT16 j; + + if (startoffstripe > i) { + stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; + } else if (startoffstripe == i) { + stripestart[i] = startoff; + } else { + stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length); + } + + if (endoffstripe > i) { + stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; + } else if (endoffstripe == i) { + stripeend[i] = endoff + 1; + } else { + stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length); + } + + if (stripestart[i] != stripeend[i]) { + stripedata[i] = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG); + + if (!stripedata[i]) { + ERR("out of memory\n"); + ExFreePool(stripeoff); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + } + + for (j = 1; j < c->chunk_item->sub_stripes; j++) { + stripestart[i+j] = stripestart[i]; + stripeend[i+j] = stripeend[i]; + stripedata[i+j] = stripedata[i]; + } + } + + pos = 0; + RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes); + + stripenum = startoffstripe / c->chunk_item->sub_stripes; + while (pos < length) { + if (pos == 0) { + UINT32 writelen = min(stripeend[stripenum * c->chunk_item->sub_stripes] - stripestart[stripenum * c->chunk_item->sub_stripes], + c->chunk_item->stripe_length - (stripestart[stripenum * c->chunk_item->sub_stripes] % c->chunk_item->stripe_length)); + + RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes], data, writelen); + stripeoff[stripenum] += writelen; + pos += writelen; + } else if (length - pos < c->chunk_item->stripe_length) { + RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes] + stripeoff[stripenum], (UINT8*)data + pos, length - pos); + break; + } else { + RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes] + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length); + stripeoff[stripenum] += c->chunk_item->stripe_length; + pos += c->chunk_item->stripe_length; + } + + stripenum = (stripenum + 1) % (c->chunk_item->num_stripes / c->chunk_item->sub_stripes); + } + + ExFreePool(stripeoff); + + if (need_free) + ExFreePool(data); + + need_free2 = TRUE; + } else { + for (i = 0; i < c->chunk_item->num_stripes; i++) { + stripestart[i] = address - c->offset; + stripeend[i] = stripestart[i] + length; + stripedata[i] = data; + } + need_free2 = need_free; + } + + for (i = 0; i < c->chunk_item->num_stripes; i++) { + PIO_STACK_LOCATION IrpSp; + + // FIXME - handle missing devices + + stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_stripe), ALLOC_TAG); + if (!stripe) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } + + if (stripestart[i] == stripeend[i]) { + stripe->status = WriteDataStatus_Ignore; + stripe->Irp = NULL; + stripe->buf = NULL; + } else { + stripe->context = (struct write_data_context*)wtc; + stripe->buf = stripedata[i]; + stripe->need_free = need_free2; + stripe->device = c->devices[i]; + RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK)); + stripe->status = WriteDataStatus_Pending; + + if (!Irp) { + stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE); + + if (!stripe->Irp) { + ERR("IoAllocateIrp failed\n"); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + } else { + stripe->Irp = IoMakeAssociatedIrp(Irp, stripe->device->devobj->StackSize); + + if (!stripe->Irp) { + ERR("IoMakeAssociatedIrp failed\n"); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + } + + IrpSp = IoGetNextIrpStackLocation(stripe->Irp); + IrpSp->MajorFunction = IRP_MJ_WRITE; + + if (stripe->device->devobj->Flags & DO_BUFFERED_IO) { + stripe->Irp->AssociatedIrp.SystemBuffer = stripedata[i]; + + stripe->Irp->Flags = IRP_BUFFERED_IO; + } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) { + stripe->Irp->MdlAddress = IoAllocateMdl(stripedata[i], stripeend[i] - stripestart[i], FALSE, FALSE, NULL); + if (!stripe->Irp->MdlAddress) { + ERR("IoAllocateMdl failed\n"); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess); + } else { + stripe->Irp->UserBuffer = stripedata[i]; + } + + IrpSp->Parameters.Write.Length = stripeend[i] - stripestart[i]; + IrpSp->Parameters.Write.ByteOffset.QuadPart = stripestart[i] + cis[i].offset; + + stripe->Irp->UserIosb = &stripe->iosb; + wtc->stripes_left++; + + IoSetCompletionRoutine(stripe->Irp, write_data_completion, stripe, TRUE, TRUE, TRUE); + } + + InsertTailList(&wtc->stripes, &stripe->list_entry); } + Status = STATUS_SUCCESS; + end: + + if (stripestart) ExFreePool(stripestart); + if (stripeend) ExFreePool(stripeend); + if (stripedata) ExFreePool(stripedata); + + if (!NT_SUCCESS(Status)) { + free_write_data_stripes(wtc); + ExFreePool(wtc); + } return Status; } -static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) { - LIST_ENTRY *le, *nextle; - space *s, *s2; +NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp) { + write_data_context* wtc; + NTSTATUS Status; -// // TESTING -// le = c->space.Flink; -// while (le != &c->space) { -// s = CONTAINING_RECORD(le, space, list_entry); -// -// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type); -// -// le = le->Flink; -// } - - le = c->space.Flink; - while (le != &c->space) { - s = CONTAINING_RECORD(le, space, list_entry); - nextle = le->Flink; - - if (s->type == SPACE_TYPE_DELETING) - s->type = SPACE_TYPE_FREE; - else if (s->type == SPACE_TYPE_WRITING) - s->type = SPACE_TYPE_USED; - - if (le->Blink != &c->space) { - s2 = CONTAINING_RECORD(le->Blink, space, list_entry); - - if (s2->type == s->type) { // do merge - s2->size += s->size; - - RemoveEntryList(&s->list_entry); - ExFreePool(s); - } - } + wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG); + if (!wtc) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } - le = nextle; + KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE); + InitializeListHead(&wtc->stripes); + wtc->tree = FALSE; + wtc->stripes_left = 0; + + Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp); + if (!NT_SUCCESS(Status)) { + ERR("write_data returned %08x\n", Status); + free_write_data_stripes(wtc); + ExFreePool(wtc); + return Status; } -// le = c->space.Flink; -// while (le != &c->space) { -// s = CONTAINING_RECORD(le, space, list_entry); -// -// TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type); -// -// le = le->Flink; -// } + if (wtc->stripes.Flink != &wtc->stripes) { + // launch writes and wait + LIST_ENTRY* le = wtc->stripes.Flink; + while (le != &wtc->stripes) { + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); + + if (stripe->status != WriteDataStatus_Ignore) + IoCallDriver(stripe->device->devobj, stripe->Irp); + + le = le->Flink; + } + + KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL); + + le = wtc->stripes.Flink; + while (le != &wtc->stripes) { + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); + + if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { + Status = stripe->iosb.Status; + break; + } + + le = le->Flink; + } + + free_write_data_stripes(wtc); + } + + ExFreePool(wtc); + + return STATUS_SUCCESS; +} + +static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) { + // FIXME - loop through c->deleting and do TRIM if device supports it + // FIXME - also find way of doing TRIM of dropped chunks + + while (!IsListEmpty(&c->deleting)) { + space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry); + + RemoveEntryList(&s->list_entry); + ExFreePool(s); + } } static void clean_space_cache(device_extension* Vcb) { - LIST_ENTRY* le; chunk* c; TRACE("(%p)\n", Vcb); - le = Vcb->chunks.Flink; - while (le != &Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); + while (!IsListEmpty(&Vcb->chunks_changed)) { + c = CONTAINING_RECORD(Vcb->chunks_changed.Flink, chunk, list_entry_changed); - if (c->space_changed) { - clean_space_cache_chunk(Vcb, c); - c->space_changed = FALSE; - } + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); - le = le->Flink; + clean_space_cache_chunk(Vcb, c); + RemoveEntryList(&c->list_entry_changed); + c->list_entry_changed.Flink = NULL; + + ExReleaseResourceLite(&c->nonpaged->lock); } } @@ -1059,14 +1142,26 @@ static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) { tree* t = CONTAINING_RECORD(le, tree, list_entry); if (t->write) { - if (t->header.num_items == 0 && t->parent) + if (t->header.num_items == 0 && t->parent) { +#ifdef DEBUG_WRITE_LOOPS + ERR("empty tree found, looping again\n"); +#endif return FALSE; + } - if (t->size > maxsize) + if (t->size > maxsize) { +#ifdef DEBUG_WRITE_LOOPS + ERR("overlarge tree found (%u > %u), looping again\n", t->size, maxsize); +#endif return FALSE; + } - if (!t->has_new_address) + if (!t->has_new_address) { +#ifdef DEBUG_WRITE_LOOPS + ERR("tree found without new address, looping again\n"); +#endif return FALSE; + } } le = le->Flink; @@ -1076,66 +1171,77 @@ 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; - le = Vcb->trees.Flink; - while (le != &Vcb->trees) { - tree* t = CONTAINING_RECORD(le, tree, list_entry); + for (level = 0; level <= 255; level++) { + BOOL nothing_found = TRUE; - if (t->write) { - if (t->parent) { - if (!t->parent->write) { + TRACE("level = %u\n", level); + + le = Vcb->trees.Flink; + while (le != &Vcb->trees) { + tree* t = CONTAINING_RECORD(le, tree, list_entry); + + if (t->write && t->header.level == level) { + TRACE("tree %p: root = %llx, level = %x, parent = %p\n", t, t->header.tree_id, t->header.level, t->parent); + + nothing_found = FALSE; + + if (t->parent) { + if (!t->parent->write) + TRACE("adding tree %p (level %x)\n", t->parent, t->header.level); + t->parent->write = TRUE; - Vcb->write_trees++; - } - } 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; + } 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->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"); + 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; } - } else { - if (!tp.tree->write) { + + 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; - Vcb->write_trees++; } } } + + le = le->Flink; } - le = le->Flink; + if (nothing_found) + break; } return STATUS_SUCCESS; @@ -1148,11 +1254,7 @@ static void add_parents_to_cache(device_extension* Vcb, tree* t) { while (t->parent) { t = t->parent; - - if (!t->write) { - t->write = TRUE; - Vcb->write_trees++; - } + t->write = TRUE; } if (t->root == Vcb->root_root || t->root == Vcb->chunk_root) @@ -1173,10 +1275,7 @@ static void add_parents_to_cache(device_extension* Vcb, tree* t) { return; } - if (!tp.tree->write) { - tp.tree->write = TRUE; - Vcb->write_trees++; - } + 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) { @@ -1201,8 +1300,12 @@ static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 return FALSE; } - add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING); + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + + space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback); + ExReleaseResourceLite(&c->nonpaged->lock); + add_parents_to_cache(Vcb, insert_tp.tree); return TRUE; @@ -1255,7 +1358,11 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i return FALSE; } - add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING); + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + + space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback); + + ExReleaseResourceLite(&c->nonpaged->lock); add_parents_to_cache(Vcb, insert_tp.tree); @@ -1295,32 +1402,48 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba } } + ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); + le = Vcb->chunks.Flink; while (le != &Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + ExReleaseResourceLite(&Vcb->chunk_lock); t->new_address = addr; t->has_new_address = TRUE; return STATUS_SUCCESS; } } + + ExReleaseResourceLite(&c->nonpaged->lock); le = le->Flink; } // allocate new chunk if necessary if ((c = alloc_chunk(Vcb, flags, rollback))) { + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + ExReleaseResourceLite(&Vcb->chunk_lock); t->new_address = addr; t->has_new_address = TRUE; return STATUS_SUCCESS; } } + + ExReleaseResourceLite(&c->nonpaged->lock); } + ExReleaseResourceLite(&Vcb->chunk_lock); + ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb->superblock.node_size); return STATUS_DISK_FULL; @@ -1330,7 +1453,6 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre KEY searchkey; traverse_ptr tp; chunk* c; - EXTENT_ITEM_SKINNY_METADATA* eism; NTSTATUS Status; searchkey.obj_id = address; @@ -1354,44 +1476,17 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre } delete_tree_item(Vcb, &tp, rollback); - - eism = (EXTENT_ITEM_SKINNY_METADATA*)tp.item->data; - if (t && t->header.level == 0 && eism->ei.flags & EXTENT_ITEM_SHARED_BACKREFS && eism->type == TYPE_TREE_BLOCK_REF) { - // convert shared data extents - - LIST_ENTRY* le = t->itemlist.Flink; - while (le != &t->itemlist) { - tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); - - TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset); - - if (!td->ignore && !td->inserted) { - if (td->key.obj_type == TYPE_EXTENT_DATA) { - EXTENT_DATA* ed = (EXTENT_DATA*)td->data; - - if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - - if (ed2->address != 0) { - TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size); - convert_shared_data_extent(Vcb, ed2->address, ed2->size, rollback); - } - } - } - } - - le = le->Flink; - } - - t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF; - } c = get_chunk_from_address(Vcb, address); if (c) { + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + decrease_chunk_usage(c, Vcb->superblock.node_size); - add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_DELETING); + space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback); + + ExReleaseResourceLite(&c->nonpaged->lock); } else ERR("could not find chunk for address %llx\n", address); @@ -1577,36 +1672,6 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei->refcount); return STATUS_INTERNAL_ERROR; } - - if (t && t->header.level == 0 && ei->flags & EXTENT_ITEM_SHARED_BACKREFS) { - // convert shared data extents - - LIST_ENTRY* le = t->itemlist.Flink; - while (le != &t->itemlist) { - tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); - - TRACE("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset); - - if (!td->ignore && !td->inserted) { - if (td->key.obj_type == TYPE_EXTENT_DATA) { - EXTENT_DATA* ed = (EXTENT_DATA*)td->data; - - if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - - if (ed2->address != 0) { - TRACE("trying to convert shared data extent %llx,%llx\n", ed2->address, ed2->size); - convert_shared_data_extent(Vcb, ed2->address, ed2->size, rollback); - } - } - } - } - - le = le->Flink; - } - - t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF; - } } delete_tree_item(Vcb, &tp, rollback); @@ -1665,9 +1730,13 @@ 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); + decrease_chunk_usage(c, tp.item->key.offset); - add_to_space_list(c, address, tp.item->key.offset, SPACE_TYPE_DELETING); + space_list_add(Vcb, c, TRUE, address, tp.item->key.offset, rollback); + + ExReleaseResourceLite(&c->nonpaged->lock); } else ERR("could not find chunk for address %llx\n", address); @@ -1792,31 +1861,32 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) { return STATUS_SUCCESS; } -static NTSTATUS STDCALL write_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { - write_tree_stripe* stripe = conptr; - write_tree_context* context = (write_tree_context*)stripe->context; +static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { + write_data_stripe* stripe = conptr; + write_data_context* context = (write_data_context*)stripe->context; LIST_ENTRY* le; - BOOL complete; - if (stripe->status == WriteTreeStatus_Cancelling) { - stripe->status = WriteTreeStatus_Cancelled; + // FIXME - we need a lock here + + if (stripe->status == WriteDataStatus_Cancelling) { + stripe->status = WriteDataStatus_Cancelled; goto end; } stripe->iosb = Irp->IoStatus; if (NT_SUCCESS(Irp->IoStatus.Status)) { - stripe->status = WriteTreeStatus_Success; + stripe->status = WriteDataStatus_Success; } else { le = context->stripes.Flink; - stripe->status = WriteTreeStatus_Error; + stripe->status = WriteDataStatus_Error; while (le != &context->stripes) { - write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* s2 = CONTAINING_RECORD(le, write_data_stripe, list_entry); - if (s2->status == WriteTreeStatus_Pending) { - s2->status = WriteTreeStatus_Cancelling; + if (s2->status == WriteDataStatus_Pending) { + s2->status = WriteDataStatus_Cancelling; IoCancelIrp(s2->Irp); } @@ -1825,109 +1895,24 @@ static NTSTATUS STDCALL write_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP } end: - le = context->stripes.Flink; - complete = TRUE; - - while (le != &context->stripes) { - write_tree_stripe* s2 = CONTAINING_RECORD(le, write_tree_stripe, list_entry); - - if (s2->status == WriteTreeStatus_Pending || s2->status == WriteTreeStatus_Cancelling) { - complete = FALSE; - break; - } - - le = le->Flink; - } - - if (complete) + if (InterlockedDecrement(&context->stripes_left) == 0) KeSetEvent(&context->Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } -NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc) { - chunk* c; - CHUNK_ITEM_STRIPE* cis; - write_tree_stripe* stripe; - UINT64 i; - - c = get_chunk_from_address(Vcb, addr); - - if (!c) { - ERR("get_chunk_from_address failed\n"); - return STATUS_INTERNAL_ERROR; - } - - cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; - - // FIXME - make this work with RAID - - for (i = 0; i < c->chunk_item->num_stripes; i++) { - PIO_STACK_LOCATION IrpSp; - - // FIXME - handle missing devices - - stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_stripe), ALLOC_TAG); - if (!stripe) { - ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - stripe->context = (struct write_tree_context*)wtc; - stripe->buf = data; - stripe->device = c->devices[i]; - RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK)); - stripe->status = WriteTreeStatus_Pending; - - stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE); - - if (!stripe->Irp) { - ERR("IoAllocateIrp failed\n"); - return STATUS_INTERNAL_ERROR; - } - - IrpSp = IoGetNextIrpStackLocation(stripe->Irp); - IrpSp->MajorFunction = IRP_MJ_WRITE; - - if (stripe->device->devobj->Flags & DO_BUFFERED_IO) { - stripe->Irp->AssociatedIrp.SystemBuffer = data; - - stripe->Irp->Flags = IRP_BUFFERED_IO; - } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) { - stripe->Irp->MdlAddress = IoAllocateMdl(data, Vcb->superblock.node_size, FALSE, FALSE, NULL); - if (!stripe->Irp->MdlAddress) { - ERR("IoAllocateMdl failed\n"); - return STATUS_INTERNAL_ERROR; - } - - MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess); - } else { - stripe->Irp->UserBuffer = data; - } - - IrpSp->Parameters.Write.Length = Vcb->superblock.node_size; - IrpSp->Parameters.Write.ByteOffset.QuadPart = addr - c->offset + cis[i].offset; - - stripe->Irp->UserIosb = &stripe->iosb; - - IoSetCompletionRoutine(stripe->Irp, write_tree_completion, stripe, TRUE, TRUE, TRUE); - - InsertTailList(&wtc->stripes, &stripe->list_entry); - } - - return STATUS_SUCCESS; -} - -void free_write_tree_stripes(write_tree_context* wtc) { +void free_write_data_stripes(write_data_context* wtc) { LIST_ENTRY *le, *le2, *nextle; le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); - if (stripe->device->devobj->Flags & DO_DIRECT_IO) { - MmUnlockPages(stripe->Irp->MdlAddress); - IoFreeMdl(stripe->Irp->MdlAddress); + if (stripe->Irp) { + if (stripe->device->devobj->Flags & DO_DIRECT_IO) { + MmUnlockPages(stripe->Irp->MdlAddress); + IoFreeMdl(stripe->Irp->MdlAddress); + } } le = le->Flink; @@ -1935,16 +1920,16 @@ void free_write_tree_stripes(write_tree_context* wtc) { le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); nextle = le->Flink; - if (stripe->buf) { + if (stripe->buf && stripe->need_free) { ExFreePool(stripe->buf); le2 = le->Flink; while (le2 != &wtc->stripes) { - write_tree_stripe* s2 = CONTAINING_RECORD(le2, write_tree_stripe, list_entry); + write_data_stripe* s2 = CONTAINING_RECORD(le2, write_data_stripe, list_entry); if (s2->buf == stripe->buf) s2->buf = NULL; @@ -1966,7 +1951,7 @@ static NTSTATUS write_trees(device_extension* Vcb) { UINT32 crc32; NTSTATUS Status; LIST_ENTRY* le; - write_tree_context* wtc; + write_data_context* wtc; TRACE("(%p)\n", Vcb); @@ -2072,7 +2057,7 @@ static NTSTATUS write_trees(device_extension* Vcb) { TRACE("allocated tree extents\n"); - wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG); + wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG); if (!wtc) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; @@ -2080,6 +2065,8 @@ static NTSTATUS write_trees(device_extension* Vcb) { KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE); InitializeListHead(&wtc->stripes); + wtc->tree = TRUE; + wtc->stripes_left = 0; le = Vcb->trees.Flink; while (le != &Vcb->trees) { @@ -2206,9 +2193,9 @@ static NTSTATUS write_trees(device_extension* Vcb) { *((UINT32*)data) = crc32; TRACE("setting crc32 to %08x\n", crc32); - Status = write_tree(Vcb, t->new_address, data, wtc); + Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL); if (!NT_SUCCESS(Status)) { - ERR("write_tree returned %08x\n", Status); + ERR("write_data returned %08x\n", Status); goto end; } } @@ -2222,9 +2209,10 @@ static NTSTATUS write_trees(device_extension* Vcb) { // launch writes and wait le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); - IoCallDriver(stripe->device->devobj, stripe->Irp); + if (stripe->status != WriteDataStatus_Ignore) + IoCallDriver(stripe->device->devobj, stripe->Irp); le = le->Flink; } @@ -2233,9 +2221,9 @@ static NTSTATUS write_trees(device_extension* Vcb) { le = wtc->stripes.Flink; while (le != &wtc->stripes) { - write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry); + write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); - if (!NT_SUCCESS(stripe->iosb.Status)) { + if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { Status = stripe->iosb.Status; break; } @@ -2243,7 +2231,7 @@ static NTSTATUS write_trees(device_extension* Vcb) { le = le->Flink; } - free_write_tree_stripes(wtc); + free_write_data_stripes(wtc); } end: @@ -2365,19 +2353,172 @@ 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) { + LIST_ENTRY *le, *le2; + NTSTATUS Status; + UINT64 old_size; + + le = ce->refs.Flink; + while (le != &ce->refs) { + changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry); + LIST_ENTRY* le3 = le->Flink; + UINT64 old_count = 0; + + le2 = ce->old_refs.Flink; + while (le2 != &ce->old_refs) { + changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); + + if (cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) { + old_count = cer2->edr.count; + + RemoveEntryList(&cer2->list_entry); + ExFreePool(cer2); + break; + } + + le2 = le2->Flink; + } + + 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); + + if (!NT_SUCCESS(Status)) { + ERR("increase_extent_refcount_data returned %08x\n", Status); + return Status; + } + } 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); + + if (!NT_SUCCESS(Status)) { + ERR("decrease_extent_refcount_data returned %08x\n", Status); + return Status; + } + } + + if (ce->size != ce->old_size && ce->old_count > 0) { + KEY searchkey; + traverse_ptr tp; + void* data; + + searchkey.obj_id = ce->address; + searchkey.obj_type = TYPE_EXTENT_ITEM; + searchkey.offset = ce->old_size; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + if (keycmp(&searchkey, &tp.item->key)) { + ERR("could not find (%llx,%x,%llx) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + return STATUS_INTERNAL_ERROR; + } + + if (tp.item->size > 0) { + data = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); + + if (!data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(data, tp.item->data, tp.item->size); + } else + data = NULL; + + if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, rollback)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + delete_tree_item(Vcb, &tp, rollback); + } + + RemoveEntryList(&cer->list_entry); + ExFreePool(cer); + + le = le3; + } + +#ifdef DEBUG_PARANOID + if (!IsListEmpty(&ce->old_refs)) + WARN("old_refs not empty\n"); +#endif + + if (ce->count == 0) { + if (!ce->no_csum) { + LIST_ENTRY changed_sector_list; + + changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); + if (!sc) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + sc->ol.key = ce->address; + sc->checksums = NULL; + sc->length = ce->size / Vcb->superblock.sector_size; + + sc->deleted = TRUE; + + InitializeListHead(&changed_sector_list); + insert_into_ordered_list(&changed_sector_list, &sc->ol); + + ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); + commit_checksum_changes(Vcb, &changed_sector_list); + ExReleaseResourceLite(&Vcb->checksum_lock); + } + + decrease_chunk_usage(c, ce->size); + + space_list_add(Vcb, c, TRUE, ce->address, ce->size, rollback); + } + + RemoveEntryList(&ce->list_entry); + ExFreePool(ce); + + return STATUS_SUCCESS; +} + static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) { - LIST_ENTRY* le = Vcb->chunks.Flink; + LIST_ENTRY *le = Vcb->chunks.Flink, *le2; chunk* c; KEY searchkey; traverse_ptr tp; BLOCK_GROUP_ITEM* bgi; NTSTATUS Status; + BOOL flushed_extents = FALSE; TRACE("(%p)\n", Vcb); + ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE); + while (le != &Vcb->chunks) { c = CONTAINING_RECORD(le, chunk, list_entry); + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + if (!NT_SUCCESS(Status)) { + ERR("flush_changed_extent returned %08x\n", Status); + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; + } + + flushed_extents = TRUE; + + le2 = le3; + } + if (c->used != c->oldused) { searchkey.obj_id = c->offset; searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; @@ -2386,24 +2527,31 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); - return Status; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } if (keycmp(&searchkey, &tp.item->key)) { ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); int3; - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + ExReleaseResourceLite(&c->nonpaged->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)); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); if (!bgi) { ERR("out of memory\n"); - return STATUS_INSUFFICIENT_RESOURCES; + Status = STATUS_INSUFFICIENT_RESOURCES; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } RtlCopyMemory(bgi, tp.item->data, tp.item->size); @@ -2416,36 +2564,32 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, rollback)) { ERR("insert_tree_item failed\n"); ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used); TRACE("chunk_item type = %llx\n", c->chunk_item->type); if (c->chunk_item->type & BLOCK_FLAG_RAID0) { - FIXME("RAID0 not yet supported\n"); - ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; - } else if (c->chunk_item->type & BLOCK_FLAG_RAID1) { - FIXME("RAID1 not yet supported\n"); - ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; - } else if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE) { - Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + (2 * (c->used - c->oldused)); - } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) { - FIXME("RAID10 not yet supported\n"); - ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; + Vcb->superblock.bytes_used += c->used - c->oldused; + } else if (c->chunk_item->type & BLOCK_FLAG_RAID1 || c->chunk_item->type & BLOCK_FLAG_DUPLICATE || c->chunk_item->type & BLOCK_FLAG_RAID10) { + Vcb->superblock.bytes_used += 2 * (c->used - c->oldused); } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) { FIXME("RAID5 not yet supported\n"); ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) { FIXME("RAID6 not yet supported\n"); ExFreePool(bgi); - return STATUS_INTERNAL_ERROR; + Status = STATUS_INTERNAL_ERROR; + ExReleaseResourceLite(&c->nonpaged->lock); + goto end; } else { // SINGLE - Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + c->used - c->oldused; + Vcb->superblock.bytes_used += c->used - c->oldused; } TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used); @@ -2453,10 +2597,25 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) c->oldused = c->used; } + ExReleaseResourceLite(&c->nonpaged->lock); + le = le->Flink; } - return STATUS_SUCCESS; + if (flushed_extents) { + ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); + if (!IsListEmpty(&Vcb->sector_checksums)) { + update_checksum_tree(Vcb, rollback); + } + ExReleaseResourceLite(&Vcb->checksum_lock); + } + + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(&Vcb->chunk_lock); + + return Status; } static void get_first_item(tree* t, KEY* key) { @@ -2571,7 +2730,6 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* t->size = size; t->header.num_items = numitems; nt->write = TRUE; - Vcb->write_trees++; InterlockedIncrement(&Vcb->open_trees); InsertTailList(&Vcb->trees, &nt->list_entry); @@ -2617,7 +2775,6 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* td->ignore = FALSE; td->inserted = TRUE; td->treeholder.tree = nt; - init_tree_holder(&td->treeholder); // td->treeholder.nonpaged->status = tree_holder_loaded; nt->paritem = td; @@ -2675,7 +2832,6 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* td->treeholder.address = 0; td->treeholder.generation = Vcb->superblock.generation; td->treeholder.tree = t; - init_tree_holder(&td->treeholder); // td->treeholder.nonpaged->status = tree_holder_loaded; InsertTailList(&pt->itemlist, &td->list_entry); t->paritem = td; @@ -2692,13 +2848,11 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* td->treeholder.address = 0; td->treeholder.generation = Vcb->superblock.generation; td->treeholder.tree = nt; - init_tree_holder(&td->treeholder); // td->treeholder.nonpaged->status = tree_holder_loaded; InsertTailList(&pt->itemlist, &td->list_entry); nt->paritem = td; pt->write = TRUE; - Vcb->write_trees++; t->root->treeholder.tree = pt; @@ -2875,10 +3029,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* par = next_tree->parent; while (par) { - if (!par->write) { - par->write = TRUE; - Vcb->write_trees++; - } + par->write = TRUE; par = par->parent; } @@ -2948,10 +3099,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* par = next_tree; while (par) { - if (!par->write) { - par->write = TRUE; - Vcb->write_trees++; - } + par->write = TRUE; par = par->parent; } } @@ -3107,6 +3255,8 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { ERR("reduce_tree_extent returned %08x\n", Status); return Status; } + + t->has_new_address = FALSE; } else if (t->has_address) { Status = reduce_tree_extent(Vcb,t->header.address, t, rollback); @@ -3114,6 +3264,8 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { ERR("reduce_tree_extent returned %08x\n", Status); return Status; } + + t->has_address = FALSE; } if (!t->paritem->ignore) { @@ -3216,6 +3368,8 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { ERR("reduce_tree_extent returned %08x\n", Status); return Status; } + + t->has_new_address = FALSE; } else if (t->has_address) { Status = reduce_tree_extent(Vcb,t->header.address, t, rollback); @@ -3223,6 +3377,8 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) { ERR("reduce_tree_extent returned %08x\n", Status); return Status; } + + t->has_address = FALSE; } if (!td->treeholder.tree) { // load first item if not already loaded @@ -3265,7 +3421,7 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* if (level > 0) { if (!th->tree) { - Status = load_tree(Vcb, th->address, r, &th->tree); + Status = load_tree(Vcb, th->address, r, &th->tree, NULL); if (!NT_SUCCESS(Status)) { ERR("load_tree(%llx) returned %08x\n", th->address, Status); @@ -3380,13 +3536,1561 @@ 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) { + CHUNK_ITEM* ci; + CHUNK_ITEM_STRIPE* cis; + BLOCK_GROUP_ITEM* bgi; + UINT16 i, factor; + NTSTATUS Status; + + ci = ExAllocatePoolWithTag(PagedPool, c->size, ALLOC_TAG); + if (!ci) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + 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)) { + ERR("insert_tree_item failed\n"); + ExFreePool(ci); + return STATUS_INTERNAL_ERROR; + } + + if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) { + Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size); + if (!NT_SUCCESS(Status)) { + ERR("add_to_bootstrap returned %08x\n", Status); + return Status; + } + } + + // add BLOCK_GROUP_ITEM to tree 2 + + bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG); + if (!bgi) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + bgi->used = c->used; + 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)) { + ERR("insert_tree_item failed\n"); + ExFreePool(bgi); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (c->chunk_item->type & BLOCK_FLAG_RAID0) + factor = c->chunk_item->num_stripes; + else if (c->chunk_item->type & BLOCK_FLAG_RAID10) + factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes; + else // SINGLE, DUPLICATE, RAID1 + factor = 1; + + // add DEV_EXTENTs to tree 4 + + cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; + + for (i = 0; i < c->chunk_item->num_stripes; i++) { + DEV_EXTENT* de; + + de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG); + if (!de) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + de->chunktree = Vcb->chunk_root->id; + de->objid = 0x100; + de->address = c->offset; + 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)) { + 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); + if (!NT_SUCCESS(Status)) { + ERR("update_dev_item returned %08x\n", Status); + return Status; + } + } + + c->created = FALSE; + + return STATUS_SUCCESS; +} + +static void remove_from_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset) { + sys_chunk* sc2; + LIST_ENTRY* le; + + le = Vcb->sys_chunks.Flink; + while (le != &Vcb->sys_chunks) { + sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); + + if (sc2->key.obj_id == obj_id && sc2->key.obj_type == obj_type && sc2->key.offset == offset) { + RemoveEntryList(&sc2->list_entry); + + Vcb->superblock.n -= sizeof(KEY) + sc2->size; + + ExFreePool(sc2->data); + ExFreePool(sc2); + regen_bootstrap(Vcb); + return; + } + + le = le->Flink; + } +} + +static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) { + NTSTATUS Status; + KEY searchkey; + traverse_ptr tp; + UINT64 i, factor; + CHUNK_ITEM_STRIPE* cis; + + TRACE("dropping chunk %llx\n", c->offset); + + // remove free space cache + if (c->cache) { + c->cache->deleted = TRUE; + + flush_fcb(c->cache, TRUE, rollback); + + free_fcb(c->cache); + + searchkey.obj_id = FREE_SPACE_CACHE_ID; + searchkey.obj_type = 0; + searchkey.offset = c->offset; + + 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 (!keycmp(&tp.item->key, &searchkey)) { + delete_tree_item(Vcb, &tp, rollback); + } + } + + if (c->chunk_item->type & BLOCK_FLAG_RAID0) + factor = c->chunk_item->num_stripes; + else if (c->chunk_item->type & BLOCK_FLAG_RAID10) + factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes; + else // SINGLE, DUPLICATE, RAID1 + factor = 1; + + cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; + for (i = 0; i < c->chunk_item->num_stripes; i++) { + if (!c->created) { + // remove DEV_EXTENTs from tree 4 + searchkey.obj_id = cis[i].dev_id; + searchkey.obj_type = TYPE_DEV_EXTENT; + searchkey.offset = cis[i].offset; + + Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + if (!keycmp(&tp.item->key, &searchkey)) { + delete_tree_item(Vcb, &tp, rollback); + + if (tp.item->size >= sizeof(DEV_EXTENT)) { + DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data; + + c->devices[i]->devitem.bytes_used -= de->length; + + space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, de->length, rollback); + } + } else + WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); + } else { + UINT64 len = c->chunk_item->size / factor; + + c->devices[i]->devitem.bytes_used -= len; + space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, len, rollback); + } + } + + // modify DEV_ITEMs in chunk tree + for (i = 0; i < c->chunk_item->num_stripes; i++) { + if (c->devices[i]) { + UINT64 j; + DEV_ITEM* di; + + searchkey.obj_id = 1; + searchkey.obj_type = TYPE_DEV_ITEM; + searchkey.offset = c->devices[i]->devitem.dev_id; + + Status = find_item(Vcb, Vcb->chunk_root, &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 DEV_ITEM for device %llx\n", searchkey.offset); + return STATUS_INTERNAL_ERROR; + } + + delete_tree_item(Vcb, &tp, rollback); + + di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG); + if (!di) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + 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)) { + ERR("insert_tree_item failed\n"); + return STATUS_INTERNAL_ERROR; + } + + for (j = i + 1; j < c->chunk_item->num_stripes; j++) { + if (c->devices[j] == c->devices[i]) + c->devices[j] = NULL; + } + } + } + + if (!c->created) { + // remove CHUNK_ITEM from chunk tree + searchkey.obj_id = 0x100; + searchkey.obj_type = TYPE_CHUNK_ITEM; + searchkey.offset = c->offset; + + Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return Status; + } + + if (!keycmp(&tp.item->key, &searchkey)) + delete_tree_item(Vcb, &tp, rollback); + else + WARN("could not find CHUNK_ITEM for chunk %llx\n", c->offset); + + // remove BLOCK_GROUP_ITEM from extent tree + searchkey.obj_id = c->offset; + searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; + searchkey.offset = 0xffffffffffffffff; + + Status = find_item(Vcb, Vcb->extent_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) + delete_tree_item(Vcb, &tp, rollback); + else + WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c->offset); + } + + if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) + remove_from_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset); + + RemoveEntryList(&c->list_entry); + + if (c->list_entry_changed.Flink) + RemoveEntryList(&c->list_entry_changed); + + ExFreePool(c->chunk_item); + ExFreePool(c->devices); + + while (!IsListEmpty(&c->space)) { + space* s = CONTAINING_RECORD(c->space.Flink, space, list_entry); + + RemoveEntryList(&s->list_entry); + ExFreePool(s); + } + + while (!IsListEmpty(&c->deleting)) { + space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry); + + RemoveEntryList(&s->list_entry); + ExFreePool(s); + } + + ExDeleteResourceLite(&c->nonpaged->lock); + ExDeleteResourceLite(&c->nonpaged->changed_extents_lock); + ExFreePool(c->nonpaged); + + ExFreePool(c); + + return STATUS_SUCCESS; +} + +static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) { + LIST_ENTRY *le = Vcb->chunks_changed.Flink, *le2; + NTSTATUS Status; + UINT64 used_minus_cache; + + ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); + + // FIXME - do tree chunks before data chunks + + while (le != &Vcb->chunks_changed) { + chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed); + + le2 = le->Flink; + + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + + used_minus_cache = c->used; + + // subtract self-hosted cache + if (used_minus_cache > 0 && c->chunk_item->type & BLOCK_FLAG_DATA && c->cache && c->cache->inode_item.st_size == c->used) { + LIST_ENTRY* le3; + + le3 = c->cache->extents.Flink; + while (le3 != &c->cache->extents) { + extent* ext = CONTAINING_RECORD(le3, extent, list_entry); + EXTENT_DATA* ed = ext->data; + + 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)); + break; + } + + if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { + EXTENT_DATA2* 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)); + break; + } + + if (ed2->size != 0 && ed2->address >= c->offset && ed2->address + ed2->size <= c->offset + c->chunk_item->size) + used_minus_cache -= ed2->size; + } + } + + le3 = le3->Flink; + } + } + + if (used_minus_cache == 0) { + Status = drop_chunk(Vcb, c, rollback); + if (!NT_SUCCESS(Status)) { + ERR("drop_chunk returned %08x\n", Status); + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&Vcb->chunk_lock); + return Status; + } + } else if (c->created) { + Status = create_chunk(Vcb, c, rollback); + if (!NT_SUCCESS(Status)) { + ERR("create_chunk returned %08x\n", Status); + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&Vcb->chunk_lock); + return Status; + } + } + + if (used_minus_cache > 0) + ExReleaseResourceLite(&c->nonpaged->lock); + + le = le2; + } + + ExReleaseResourceLite(&Vcb->chunk_lock); + + 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) { + KEY searchkey; + traverse_ptr tp; + ULONG xasize; + DIR_ITEM* xa; + NTSTATUS Status; + + TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen); + + searchkey.obj_id = inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = crc32; + + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + 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 + + if (!keycmp(&tp.item->key, &searchkey)) { // key exists + UINT8* newdata; + ULONG size = tp.item->size; + + xa = (DIR_ITEM*)tp.item->data; + + if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); + } else { + while (TRUE) { + ULONG oldxasize; + + if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + break; + } + + oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n; + + if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) { + UINT64 pos; + + // replace + newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG); + if (!newdata) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + pos = (UINT8*)xa - tp.item->data; + if (pos + oldxasize < tp.item->size) { // copy after changed xattr + RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize); + } + + if (pos > 0) { // copy before changed xattr + RtlCopyMemory(newdata, tp.item->data, pos); + xa = (DIR_ITEM*)(newdata + pos); + } else + xa = (DIR_ITEM*)newdata; + + xa->key.obj_id = 0; + xa->key.obj_type = 0; + xa->key.offset = 0; + xa->transid = Vcb->superblock.generation; + xa->m = datalen; + xa->n = (UINT16)strlen(name); + xa->type = BTRFS_TYPE_EA; + RtlCopyMemory(xa->name, name, strlen(name)); + RtlCopyMemory(xa->name + strlen(name), data, datalen); + + delete_tree_item(Vcb, &tp, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback); + + break; + } + + if (xa->m + xa->n >= size) { // FIXME - test this works + // not found, add to end of data + newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG); + if (!newdata) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(newdata, tp.item->data, tp.item->size); + + xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size); + xa->key.obj_id = 0; + xa->key.obj_type = 0; + xa->key.offset = 0; + xa->transid = Vcb->superblock.generation; + xa->m = datalen; + xa->n = (UINT16)strlen(name); + xa->type = BTRFS_TYPE_EA; + RtlCopyMemory(xa->name, name, strlen(name)); + RtlCopyMemory(xa->name + strlen(name), data, datalen); + + delete_tree_item(Vcb, &tp, rollback); + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback); + + break; + } else { + xa = (DIR_ITEM*)&xa->name[xa->m + xa->n]; + size -= oldxasize; + } + } + } + } else { + // add new DIR_ITEM struct + + xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG); + if (!xa) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + xa->key.obj_id = 0; + xa->key.obj_type = 0; + xa->key.offset = 0; + xa->transid = Vcb->superblock.generation; + xa->m = datalen; + xa->n = (UINT16)strlen(name); + xa->type = BTRFS_TYPE_EA; + RtlCopyMemory(xa->name, name, strlen(name)); + RtlCopyMemory(xa->name + strlen(name), data, datalen); + + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback); + } + + return STATUS_SUCCESS; +} + +static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) { + KEY searchkey; + traverse_ptr tp; + DIR_ITEM* xa; + NTSTATUS Status; + + TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32); + + searchkey.obj_id = inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = crc32; + + Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + return FALSE; + } + + if (!keycmp(&tp.item->key, &searchkey)) { // key exists + ULONG size = tp.item->size; + + if (tp.item->size < sizeof(DIR_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(DIR_ITEM)); + + return FALSE; + } else { + xa = (DIR_ITEM*)tp.item->data; + + while (TRUE) { + ULONG oldxasize; + + if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + + return FALSE; + } + + oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n; + + if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) { + ULONG newsize; + UINT8 *newdata, *dioff; + + newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m); + + delete_tree_item(Vcb, &tp, rollback); + + if (newsize == 0) { + TRACE("xattr %s deleted\n", name); + + return TRUE; + } + + // FIXME - deleting collisions almost certainly works, but we should test it properly anyway + newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG); + if (!newdata) { + ERR("out of memory\n"); + return FALSE; + } + + if ((UINT8*)xa > tp.item->data) { + RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data); + dioff = newdata + ((UINT8*)xa - tp.item->data); + } else { + dioff = newdata; + } + + if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size) + RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data)); + + insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback); + + + return TRUE; + } + + if (xa->m + xa->n >= size) { // FIXME - test this works + WARN("xattr %s not found\n", name); + + return FALSE; + } else { + xa = (DIR_ITEM*)&xa->name[xa->m + xa->n]; + size -= oldxasize; + } + } + } + } else { + WARN("xattr %s not found\n", name); + + return FALSE; + } +} + +void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) { + traverse_ptr tp; + KEY searchkey; + NTSTATUS Status; + INODE_ITEM* ii; + UINT64 ii_offset; +#ifdef DEBUG_PARANOID + UINT64 old_size = 0; + BOOL extents_changed; +#endif + +// ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); + + while (!IsListEmpty(&fcb->index_list)) { + LIST_ENTRY* le = RemoveHeadList(&fcb->index_list); + index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry); + + if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer); + if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer); + ExFreePool(ie); + } + + fcb->index_loaded = FALSE; + + if (fcb->ads) { + if (fcb->deleted) + delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, rollback); + else { + Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, rollback); + if (!NT_SUCCESS(Status)) { + ERR("set_xattr returned %08x\n", Status); + goto end; + } + } + goto end; + } + +#ifdef DEBUG_PARANOID + extents_changed = fcb->extents_changed; +#endif + + if (fcb->extents_changed) { + BOOL b; + traverse_ptr next_tp; + LIST_ENTRY* le; + BOOL prealloc = FALSE; + + // delete ignored extent items + le = fcb->extents.Flink; + while (le != &fcb->extents) { + LIST_ENTRY* le2 = le->Flink; + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + if (ext->ignore) { + RemoveEntryList(&ext->list_entry); + ExFreePool(ext->data); + ExFreePool(ext); + } + + le = le2; + } + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + LIST_ENTRY* le2 = le->Flink; + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + + if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && le->Flink != &fcb->extents) { + extent* nextext = CONTAINING_RECORD(le->Flink, extent, list_entry); + + if (ext->data->type == nextext->data->type) { + 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) { + chunk* c; + + ext->data->generation = fcb->Vcb->superblock.generation; + ed2->num_bytes += ned2->num_bytes; + + RemoveEntryList(&nextext->list_entry); + ExFreePool(nextext->data); + ExFreePool(nextext); + + 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); + if (!NT_SUCCESS(Status)) { + ERR("update_changed_extent_ref returned %08x\n", Status); + goto end; + } + } + + le2 = le; + } + } + } + + le = le2; + } + + // delete existing EXTENT_DATA items + + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_EXTENT_DATA; + searchkey.offset = 0; + + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; + } + + do { + 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); + + if (b) { + tp = next_tp; + + 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)) + break; + } + } 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; + + 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, rollback)) { + ERR("insert_tree_item failed\n"); + goto end; + } + + if (!prealloc && ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC) + prealloc = TRUE; + + le = le->Flink; + } + + // 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; + } + + if (!fcb->created || cache) { + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_INODE_ITEM; + searchkey.offset = 0xffffffffffffffff; + + 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 (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { + if (cache) { + ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); + if (!ii) { + ERR("out of memory\n"); + goto end; + } + + 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)) { + ERR("insert_tree_item failed\n"); + goto end; + } + + ii_offset = 0; + } else { + ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); + goto end; + } + } else { +#ifdef DEBUG_PARANOID + INODE_ITEM* ii2 = (INODE_ITEM*)tp.item->data; + + old_size = ii2->st_size; +#endif + + ii_offset = tp.item->key.offset; + } + + if (!cache) + delete_tree_item(fcb->Vcb, &tp, rollback); + else { + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_INODE_ITEM; + searchkey.offset = ii_offset; + + 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("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id); + goto end; + } else + RtlCopyMemory(tp.item->data, &fcb->inode_item, min(tp.item->size, sizeof(INODE_ITEM))); + } + } else + ii_offset = 0; + +#ifdef DEBUG_PARANOID + if (!extents_changed && fcb->type != BTRFS_TYPE_DIRECTORY && old_size != fcb->inode_item.st_size) { + ERR("error - size has changed but extents not marked as changed\n"); + int3; + } +#endif + + fcb->created = FALSE; + + if (fcb->deleted) { + traverse_ptr tp2; + + // delete XATTR_ITEMs + + searchkey.obj_id = fcb->inode; + searchkey.obj_type = TYPE_XATTR_ITEM; + searchkey.offset = 0; + + Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + goto end; + } + + while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) { + tp = tp2; + + if (tp.item->key.obj_id == fcb->inode) { + // FIXME - do metadata thing here too? + if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { + delete_tree_item(fcb->Vcb, &tp, rollback); + TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + } + } else + break; + } + + goto end; + } + + if (!cache) { + ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); + if (!ii) { + ERR("out of memory\n"); + goto end; + } + + 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)) { + 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); + if (!NT_SUCCESS(Status)) { + ERR("set_xattr returned %08x\n", Status); + } + + fcb->sd_dirty = FALSE; + } + + if (fcb->atts_changed) { + if (!fcb->atts_deleted) { + char val[64]; + + 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); + 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); + + fcb->atts_changed = FALSE; + fcb->atts_deleted = FALSE; + } + + 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); + 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); + + fcb->reparse_xattr_changed = FALSE; + } + +end: + fcb->dirty = FALSE; + +// ExReleaseResourceLite(fcb->Header.Resource); + return; +} + +static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + + searchkey.obj_id = parsubvolid; + searchkey.obj_type = TYPE_ROOT_REF; + searchkey.offset = subvolid; + + 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 (!keycmp(&searchkey, &tp.item->key)) { + if (tp.item->size < sizeof(ROOT_REF)) { + ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF)); + return STATUS_INTERNAL_ERROR; + } else { + ROOT_REF* rr; + ULONG len; + + rr = (ROOT_REF*)tp.item->data; + len = tp.item->size; + + do { + ULONG itemlen; + + if (len < sizeof(ROOT_REF) || len < sizeof(ROOT_REF) - 1 + rr->n) { + ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + break; + } + + itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n; + + if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) { + ULONG newlen = tp.item->size - itemlen; + + delete_tree_item(Vcb, &tp, rollback); + + if (newlen == 0) { + TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + } else { + UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff; + + if (!newrr) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); + + if ((UINT8*)rr > tp.item->data) { + RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data); + rroff = newrr + ((UINT8*)rr - tp.item->data); + } else { + rroff = newrr; + } + + if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size) + RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data)); + + insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, rollback); + } + + break; + } + + if (len > itemlen) { + len -= itemlen; + rr = (ROOT_REF*)&rr->name[rr->n]; + } else + break; + } while (len > 0); + } + } else { + WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id); + return STATUS_NOT_FOUND; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) { + KEY searchkey; + traverse_ptr tp; + NTSTATUS Status; + + searchkey.obj_id = parsubvolid; + searchkey.obj_type = TYPE_ROOT_REF; + searchkey.offset = subvolid; + + 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 (!keycmp(&searchkey, &tp.item->key)) { + ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n; + UINT8* rr2; + + rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); + if (!rr2) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (tp.item->size > 0) + RtlCopyMemory(rr2, tp.item->data, tp.item->size); + + RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n); + ExFreePool(rr); + + delete_tree_item(Vcb, &tp, rollback); + + if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, 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)) { + ERR("error - failed to insert item\n"); + ExFreePool(rr); + return STATUS_INTERNAL_ERROR; + } + } + + return STATUS_SUCCESS; +} + +static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) { + KEY searchkey; + traverse_ptr tp; + UINT8* data; + ULONG datalen; + NTSTATUS Status; + + searchkey.obj_id = parsubvolid; + searchkey.obj_type = TYPE_ROOT_REF; + searchkey.offset = subvolid; + + 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 (!keycmp(&tp.item->key, &searchkey) && tp.item->size > 0) { + datalen = tp.item->size; + + data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG); + if (!data) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(data, tp.item->data, datalen); + } else { + datalen = 0; + } + + searchkey.obj_id = subvolid; + searchkey.obj_type = TYPE_ROOT_BACKREF; + searchkey.offset = parsubvolid; + + 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 (!keycmp(&tp.item->key, &searchkey)) + 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)) { + ERR("error - failed to insert item\n"); + ExFreePool(data); + return STATUS_INTERNAL_ERROR; + } + } + + return STATUS_SUCCESS; +} + +static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) { + NTSTATUS Status; + + // if fileref created and then immediately deleted, do nothing + if (fileref->created && fileref->deleted) { + fileref->dirty = FALSE; + return STATUS_SUCCESS; + } + + if (fileref->fcb->ads) { + fileref->dirty = FALSE; + return STATUS_SUCCESS; + } + + if (fileref->created) { + ULONG disize; + DIR_ITEM *di, *di2; + UINT32 crc32; + + crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length); + + 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; + } + + 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); + + di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); + if (!di2) { + ERR("out of memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + 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)) { + 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); + 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); + if (!NT_SUCCESS(Status)) { + ERR("add_inode_ref returned %08x\n", Status); + return Status; + } + } else { + ULONG rrlen; + ROOT_REF* rr; + + 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; + } + } + + fileref->created = FALSE; + } else if (fileref->deleted) { + UINT32 crc32; + KEY searchkey; + traverse_ptr tp; + ANSI_STRING* name; + + if (fileref->oldutf8.Buffer) + name = &fileref->oldutf8; + else + name = &fileref->utf8; + + crc32 = calc_crc32c(0xfffffffe, (UINT8*)name->Buffer, name->Length); + + TRACE("deleting %.*S\n", file_desc_fileref(fileref)); + + // delete DIR_ITEM (0x54) + + Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, rollback); + if (!NT_SUCCESS(Status)) { + ERR("delete_dir_item 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, name, 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); + 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); + 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); + 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); + } + + if (fileref->oldutf8.Buffer) { + 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); + oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->oldutf8.Buffer, fileref->oldutf8.Length); + + // delete DIR_ITEM (0x54) + + Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->oldutf8, 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, 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_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); + 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; + return Status; + } + + ExFreePool(fileref->oldutf8.Buffer); + fileref->oldutf8.Buffer = NULL; + } + } + + fileref->dirty = FALSE; + + return STATUS_SUCCESS; +} + +static void convert_shared_data_refs(device_extension* Vcb, 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->header.level == 0 && + (t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF))) { + LIST_ENTRY* le2; + BOOL old = !(t->header.flags & HEADER_FLAG_MIXED_BACKREF); + + le2 = Vcb->shared_extents.Flink; + while (le2 != &Vcb->shared_extents) { + shared_data* sd = CONTAINING_RECORD(le2, shared_data, list_entry); + + if (sd->address == t->header.address) { + LIST_ENTRY* le3 = sd->entries.Flink; + while (le3 != &sd->entries) { + shared_data_entry* sde = CONTAINING_RECORD(le3, shared_data_entry, list_entry); + + 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); + + 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); + + 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); + + if (!NT_SUCCESS(Status)) + WARN("decrease_extent_refcount_shared_data returned %08x\n", Status); + } + + le3 = le3->Flink; + } + break; + } + + le2 = le2->Flink; + } + + t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF; + t->header.flags |= HEADER_FLAG_MIXED_BACKREF; + } + + le = le->Flink; + } +} + NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { NTSTATUS Status; LIST_ENTRY* le; - BOOL cache_changed; + BOOL cache_changed = FALSE; + +#ifdef DEBUG_WRITE_LOOPS + UINT loops = 0; +#endif TRACE("(%p)\n", Vcb); + while (!IsListEmpty(&Vcb->dirty_filerefs)) { + dirty_fileref* dirt; + + le = RemoveHeadList(&Vcb->dirty_filerefs); + + dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry); + + flush_fileref(dirt->fileref, rollback); + free_fileref(dirt->fileref); + ExFreePool(dirt); + } + + 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->subvol != Vcb->root_root || dirt->fcb->deleted) { + RemoveEntryList(le); + + flush_fcb(dirt->fcb, FALSE, rollback); + free_fcb(dirt->fcb); + ExFreePool(dirt); + } + + le = le2; + } + + convert_shared_data_refs(Vcb, rollback); + + ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); + if (!IsListEmpty(&Vcb->sector_checksums)) { + update_checksum_tree(Vcb, rollback); + } + ExReleaseResourceLite(&Vcb->checksum_lock); + if (!IsListEmpty(&Vcb->drop_roots)) { Status = drop_roots(Vcb, rollback); @@ -3396,10 +5100,20 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { } } + if (!IsListEmpty(&Vcb->chunks_changed)) { + Status = update_chunks(Vcb, rollback); + + if (!NT_SUCCESS(Status)) { + ERR("update_chunks returned %08x\n", Status); + return Status; + } + } + // If only changing superblock, e.g. changing label, we still need to rewrite // the root tree so the generations match, otherwise you won't be able to mount on Linux. - if (Vcb->write_trees == 0) { + if (!Vcb->root_root->treeholder.tree || !Vcb->root_root->treeholder.tree->write) { KEY searchkey; + traverse_ptr tp; searchkey.obj_id = 0; @@ -3412,10 +5126,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { return Status; } - if (!Vcb->root_root->treeholder.tree->write) { - Vcb->root_root->treeholder.tree->write = TRUE; - Vcb->write_trees++; - } + Vcb->root_root->treeholder.tree->write = TRUE; } do { @@ -3448,8 +5159,19 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { ERR("allocate_cache returned %08x\n", Status); goto end; } + +#ifdef DEBUG_WRITE_LOOPS + loops++; + + if (cache_changed) + ERR("cache has changed, looping again\n"); +#endif } while (cache_changed || !trees_consistent(Vcb, rollback)); +#ifdef DEBUG_WRITE_LOOPS + ERR("%u loops\n", loops); +#endif + TRACE("trees consistent\n"); Status = update_root_root(Vcb, rollback); @@ -3482,12 +5204,44 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) { while (le != &Vcb->trees) { tree* t = CONTAINING_RECORD(le, tree, list_entry); +#ifdef DEBUG_PARANOID + KEY searchkey; + traverse_ptr tp; + + searchkey.obj_id = t->header.address; + searchkey.obj_type = TYPE_METADATA_ITEM; + searchkey.offset = 0xffffffffffffffff; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + int3; + } + + if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { + searchkey.obj_id = t->header.address; + searchkey.obj_type = TYPE_EXTENT_ITEM; + searchkey.offset = 0xffffffffffffffff; + + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + if (!NT_SUCCESS(Status)) { + ERR("error - find_item returned %08x\n", Status); + int3; + } + + if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { + ERR("error - could not find entry in extent tree for tree at %llx\n", t->header.address); + int3; + } + } +#endif + t->write = FALSE; le = le->Flink; } - Vcb->write_trees = 0; + Vcb->need_write = FALSE; while (!IsListEmpty(&Vcb->drop_roots)) { LIST_ENTRY* le = RemoveHeadList(&Vcb->drop_roots); @@ -3504,455 +5258,6 @@ end: return Status; } -NTSTATUS consider_write(device_extension* Vcb) { - // FIXME - call do_write if Vcb->write_trees high -#if 0 - LIST_ENTRY rollback; - NTSTATUS Status = STATUS_SUCCESS; - - InitializeListHead(&rollback); - - if (Vcb->write_trees > 0) - Status = do_write(Vcb, &rollback); - - free_tree_cache(&Vcb->tree_cache); - - if (!NT_SUCCESS(Status)) - do_rollback(Vcb, &rollback); - else - clear_rollback(&rollback); - - return Status; -#else - return STATUS_SUCCESS; -#endif -} - -// NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback) { -// KEY searchkey; -// traverse_ptr tp; -// EXTENT_ITEM* ei; -// UINT8 *siptr, *type; -// ULONG len; -// UINT64 hash; -// EXTENT_DATA_REF* edr; -// NTSTATUS Status; -// -// TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset); -// -// searchkey.obj_id = address; -// searchkey.obj_type = TYPE_EXTENT_ITEM; -// searchkey.offset = size; -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// // create new entry -// -// len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); -// -// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); -// if (!ei) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// ei->refcount = 1; -// ei->generation = Vcb->superblock.generation; -// ei->flags = EXTENT_ITEM_DATA; -// -// type = (UINT8*)&ei[1]; -// *type = TYPE_EXTENT_DATA_REF; -// -// edr = (EXTENT_DATA_REF*)&type[1]; -// edr->root = subvol->id; -// edr->objid = inode; -// edr->offset = offset; -// edr->count = 1; -// -// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) { -// ERR("error - failed to insert item\n"); -// return STATUS_INTERNAL_ERROR; -// } -// -// // FIXME - update free space in superblock and CHUNK_ITEM -// -// return STATUS_SUCCESS; -// } -// -// if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert -// NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback); -// if (!NT_SUCCESS(Status)) { -// ERR("convert_old_data_extent returned %08x\n", Status); -// return Status; -// } -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// WARN("extent item not found for address %llx, size %llx\n", address, size); -// return STATUS_SUCCESS; -// } -// } -// -// ei = (EXTENT_ITEM*)tp.item->data; -// -// if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); -// return STATUS_INTERNAL_ERROR; -// } -// -// if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) { -// NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback); -// if (!NT_SUCCESS(Status)) { -// ERR("convert_shared_data_extent returned %08x\n", Status); -// return Status; -// } -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// WARN("extent item not found for address %llx, size %llx\n", address, size); -// return STATUS_SUCCESS; -// } -// -// ei = (EXTENT_ITEM*)tp.item->data; -// } -// -// if (ei->flags != EXTENT_ITEM_DATA) { -// ERR("error - flag was not EXTENT_ITEM_DATA\n"); -// return STATUS_INTERNAL_ERROR; -// } -// -// hash = get_extent_data_ref_hash(subvol->id, inode, offset); -// -// len = tp.item->size - sizeof(EXTENT_ITEM); -// siptr = (UINT8*)&ei[1]; -// -// do { -// if (*siptr == TYPE_EXTENT_DATA_REF) { -// UINT64 sihash; -// -// edr = (EXTENT_DATA_REF*)&siptr[1]; -// -// // already exists - increase refcount -// if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) { -// ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); -// -// if (!ei) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// RtlCopyMemory(ei, tp.item->data, tp.item->size); -// -// edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data)); -// edr->count++; -// ei->refcount++; -// -// delete_tree_item(Vcb, &tp, rollback); -// -// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) { -// ERR("error - failed to insert item\n"); -// ExFreePool(ei); -// return STATUS_INTERNAL_ERROR; -// } -// -// return STATUS_SUCCESS; -// } -// -// sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset); -// -// if (sihash >= hash) -// break; -// -// siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF); -// -// if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) { -// len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8); -// } else -// break; -// // FIXME - TYPE_TREE_BLOCK_REF 0xB0 -// } else { -// ERR("unrecognized extent subitem %x\n", *siptr); -// return STATUS_INTERNAL_ERROR; -// } -// } while (len > 0); -// -// len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big -// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); -// if (!ei) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data); -// ei->refcount++; -// -// type = (UINT8*)ei + (siptr - tp.item->data); -// *type = TYPE_EXTENT_DATA_REF; -// -// edr = (EXTENT_DATA_REF*)&type[1]; -// edr->root = subvol->id; -// edr->objid = inode; -// edr->offset = offset; -// edr->count = 1; -// -// if (siptr < tp.item->data + tp.item->size) -// RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr); -// -// delete_tree_item(Vcb, &tp, rollback); -// -// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) { -// ERR("error - failed to insert item\n"); -// ExFreePool(ei); -// return STATUS_INTERNAL_ERROR; -// } -// -// return STATUS_SUCCESS; -// } - -static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len) { - UINT8* siptr = (UINT8*)&ei[1]; - - do { - if (*siptr == TYPE_TREE_BLOCK_REF) { - siptr += sizeof(TREE_BLOCK_REF) + 1; - len -= sizeof(TREE_BLOCK_REF) + 1; - } else if (*siptr == TYPE_EXTENT_DATA_REF) { - siptr += sizeof(EXTENT_DATA_REF) + 1; - len -= sizeof(EXTENT_DATA_REF) + 1; - } else if (*siptr == TYPE_SHARED_BLOCK_REF) { - return TRUE; - } else if (*siptr == TYPE_SHARED_DATA_REF) { - return TRUE; - } else { - ERR("unrecognized extent subitem %x\n", *siptr); - return FALSE; - } - } while (len > 0); - - return FALSE; -} - -// static NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { -// KEY searchkey; -// traverse_ptr tp; -// EXTENT_ITEM* ei; -// UINT8* siptr; -// ULONG len; -// EXTENT_DATA_REF* edr; -// BOOL found; -// NTSTATUS Status; -// -// TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset); -// -// searchkey.obj_id = address; -// searchkey.obj_type = TYPE_EXTENT_ITEM; -// searchkey.offset = size; -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// WARN("extent item not found for address %llx, size %llx\n", address, size); -// return STATUS_SUCCESS; -// } -// -// if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert -// NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback); -// if (!NT_SUCCESS(Status)) { -// ERR("convert_old_data_extent returned %08x\n", Status); -// return Status; -// } -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// WARN("extent item not found for address %llx, size %llx\n", address, size); -// return STATUS_SUCCESS; -// } -// } -// -// ei = (EXTENT_ITEM*)tp.item->data; -// -// if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); -// return STATUS_INTERNAL_ERROR; -// } -// -// if (!(ei->flags & EXTENT_ITEM_DATA)) { -// ERR("error - EXTENT_ITEM_DATA flag not set\n"); -// return STATUS_INTERNAL_ERROR; -// } -// -// if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) { -// NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback); -// if (!NT_SUCCESS(Status)) { -// ERR("convert_shared_data_extent returned %08x\n", Status); -// return Status; -// } -// -// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); -// if (!NT_SUCCESS(Status)) { -// ERR("error - find_item returned %08x\n", Status); -// return Status; -// } -// -// if (keycmp(&tp.item->key, &searchkey)) { -// WARN("extent item not found for address %llx, size %llx\n", address, size); -// return STATUS_SUCCESS; -// } -// -// ei = (EXTENT_ITEM*)tp.item->data; -// } -// -// len = tp.item->size - sizeof(EXTENT_ITEM); -// siptr = (UINT8*)&ei[1]; -// found = FALSE; -// -// do { -// if (*siptr == TYPE_EXTENT_DATA_REF) { -// edr = (EXTENT_DATA_REF*)&siptr[1]; -// -// if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) { -// if (edr->count > 1) { -// ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); -// -// if (!ei) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// RtlCopyMemory(ei, tp.item->data, tp.item->size); -// -// edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data)); -// edr->count--; -// ei->refcount--; -// -// delete_tree_item(Vcb, &tp, rollback); -// -// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) { -// ERR("error - failed to insert item\n"); -// ExFreePool(ei); -// return STATUS_INTERNAL_ERROR; -// } -// -// return STATUS_SUCCESS; -// } -// -// found = TRUE; -// break; -// } -// -// siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF); -// -// if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) { -// len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8); -// } else -// break; -// // // FIXME - TYPE_TREE_BLOCK_REF 0xB0 -// } else { -// ERR("unrecognized extent subitem %x\n", *siptr); -// return STATUS_INTERNAL_ERROR; -// } -// } while (len > 0); -// -// if (!found) { -// WARN("could not find extent data ref\n"); -// return STATUS_SUCCESS; -// } -// -// // FIXME - decrease subitem refcount if there already? -// -// len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF); -// -// delete_tree_item(Vcb, &tp, rollback); -// -// if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed -// chunk* c; -// LIST_ENTRY* le2; -// -// if (changed_sector_list) { -// changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); -// if (!sc) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// sc->ol.key = address; -// sc->checksums = NULL; -// sc->length = size / Vcb->superblock.sector_size; -// -// sc->deleted = TRUE; -// -// insert_into_ordered_list(changed_sector_list, &sc->ol); -// } -// -// c = NULL; -// le2 = Vcb->chunks.Flink; -// while (le2 != &Vcb->chunks) { -// c = CONTAINING_RECORD(le2, chunk, list_entry); -// -// TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size); -// -// if (address >= c->offset && address + size < c->offset + c->chunk_item->size) -// break; -// -// le2 = le2->Flink; -// } -// if (le2 == &Vcb->chunks) c = NULL; -// -// if (c) { -// decrease_chunk_usage(c, size); -// -// add_to_space_list(c, address, size, SPACE_TYPE_DELETING); -// } -// -// return STATUS_SUCCESS; -// } -// -// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); -// if (!ei) { -// ERR("out of memory\n"); -// return STATUS_INSUFFICIENT_RESOURCES; -// } -// -// RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data); -// ei->refcount--; -// ei->generation = Vcb->superblock.generation; -// -// if (tp.item->data + len != siptr) -// RtlCopyMemory((UINT8*)ei + (siptr - tp.item->data), siptr + sizeof(UINT8) + sizeof(EXTENT_DATA_REF), tp.item->size - (siptr - tp.item->data) - sizeof(UINT8) - sizeof(EXTENT_DATA_REF)); -// -// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) { -// ERR("error - failed to insert item\n"); -// ExFreePool(ei); -// return STATUS_INTERNAL_ERROR; -// } -// -// return STATUS_SUCCESS; -// } - static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) { LIST_ENTRY* le = list->Flink; ordered_list* ol; @@ -3971,449 +5276,611 @@ static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) { return FALSE; } -static BOOL is_file_prealloc_inode(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 start_data, UINT64 end_data) { - NTSTATUS Status; - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b; +static changed_extent* get_changed_extent_item(chunk* c, UINT64 address, UINT64 size, BOOL no_csum) { + LIST_ENTRY* le; + changed_extent* ce; - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; - - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return FALSE; + le = c->changed_extents.Flink; + while (le != &c->changed_extents) { + ce = CONTAINING_RECORD(le, changed_extent, list_entry); + + if (ce->address == address && ce->size == size) + return ce; + + le = le->Flink; } - if (tp.item->key.obj_id != inode || tp.item->key.obj_type != TYPE_EXTENT_DATA) - return FALSE; + ce = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent), ALLOC_TAG); + if (!ce) { + ERR("out of memory\n"); + return NULL; + } - do { - EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - UINT64 len; - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - return FALSE; - } - - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return FALSE; - } - - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - if (tp.item->key.offset < end_data && tp.item->key.offset + len >= start_data && ed->type == EXTENT_TYPE_PREALLOC) - return TRUE; - - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data) - break; - } - } while (b); + ce->address = address; + ce->size = size; + ce->old_size = size; + ce->count = 0; + ce->old_count = 0; + ce->no_csum = no_csum; + InitializeListHead(&ce->refs); + InitializeListHead(&ce->old_refs); - return FALSE; + InsertTailList(&c->changed_extents, &ce->list_entry); + + return ce; } -NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp, next_tp; +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) { + LIST_ENTRY* le; + changed_extent* ce; + changed_extent_ref* cer; NTSTATUS Status; - BOOL b, deleted_prealloc = FALSE; + KEY searchkey; + traverse_ptr tp; + UINT64 old_count; - searchkey.obj_id = inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; + ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE); - Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; + ce = get_changed_extent_item(c, address, size, no_csum); + + if (!ce) { + ERR("get_changed_extent_item failed\n"); + Status = STATUS_INTERNAL_ERROR; + goto end; } - - do { - EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - UINT64 len; + + if (IsListEmpty(&ce->refs) && IsListEmpty(&ce->old_refs)) { // new entry + searchkey.obj_id = address; + searchkey.obj_type = TYPE_EXTENT_ITEM; + searchkey.offset = 0xffffffffffffffff; - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); + Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); + 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_type != searchkey.obj_type) { + ERR("could not find address %llx in extent tree\n", address); Status = STATUS_INTERNAL_ERROR; goto end; } - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - - if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) - goto cont; - - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); + if (tp.item->key.offset != size) { + ERR("extent %llx had size %llx, not %llx as expected\n", address, tp.item->key.offset, size); Status = STATUS_INTERNAL_ERROR; goto end; } - len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; + if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { + EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data; + + ce->count = ce->old_count = eiv0->refcount; + } else if (tp.item->size >= sizeof(EXTENT_ITEM)) { + EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data; + + ce->count = ce->old_count = ei->refcount; + } else { + ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + } + + ce->size = new_size; + + le = ce->refs.Flink; + while (le != &ce->refs) { + cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry); - if (tp.item->key.offset < end_data && tp.item->key.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", subvol->id, inode, tp.item->key.offset, ed->encryption); - Status = STATUS_NOT_SUPPORTED; - goto end; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("other encodings not supported\n"); - Status = STATUS_NOT_SUPPORTED; - goto end; - } - - if (ed->type == EXTENT_TYPE_INLINE) { - if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all - delete_tree_item(Vcb, &tp, rollback); - - if (ii) - ii->st_blocks -= len; - } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning - EXTENT_DATA* ned; - UINT64 size; - - delete_tree_item(Vcb, &tp, rollback); - - size = len - (end_data - tp.item->key.offset); - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - - RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size); - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (ii) - ii->st_blocks -= end_data - tp.item->key.offset; - } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove end - EXTENT_DATA* ned; - UINT64 size; - - delete_tree_item(Vcb, &tp, rollback); - - size = start_data - tp.item->key.offset; - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - - RtlCopyMemory(&ned->data[0], &ed->data[0], size); - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (ii) - ii->st_blocks -= tp.item->key.offset + len - start_data; - } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove middle - EXTENT_DATA* ned; - UINT64 size; - - delete_tree_item(Vcb, &tp, rollback); - - size = start_data - tp.item->key.offset; - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - - RtlCopyMemory(&ned->data[0], &ed->data[0], size); - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - size = tp.item->key.offset + len - end_data; - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - - RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size); - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (ii) - ii->st_blocks -= end_data - start_data; - } - } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { - if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all - if (ed2->address != 0) { - Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, inode, tp.item->key.offset - ed2->offset, 1, changed_sector_list, rollback); - if (!NT_SUCCESS(Status)) { - ERR("decrease_extent_refcount_data returned %08x\n", Status); - goto end; - } - - if (ii) - ii->st_blocks -= len; - } - - if (ed->type == EXTENT_TYPE_PREALLOC) - deleted_prealloc = TRUE; - - delete_tree_item(Vcb, &tp, rollback); - } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning - EXTENT_DATA* ned; - EXTENT_DATA2* ned2; - - if (ed2->address != 0 && ii) - ii->st_blocks -= end_data - tp.item->key.offset; - - delete_tree_item(Vcb, &tp, rollback); - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned2 = (EXTENT_DATA2*)&ned->data[0]; - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = ed->decoded_size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - ned2->address = ed2->address; - ned2->size = ed2->size; - ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset)); - ned2->num_bytes = ed2->num_bytes - (end_data - tp.item->key.offset); - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove end - EXTENT_DATA* ned; - EXTENT_DATA2* ned2; - - if (ed2->address != 0 && ii) - ii->st_blocks -= tp.item->key.offset + len - start_data; - - delete_tree_item(Vcb, &tp, rollback); - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned2 = (EXTENT_DATA2*)&ned->data[0]; - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = ed->decoded_size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - ned2->address = ed2->address; - ned2->size = ed2->size; - ned2->offset = ed2->address == 0 ? 0 : ed2->offset; - ned2->num_bytes = start_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove middle - EXTENT_DATA* ned; - EXTENT_DATA2* ned2; - - if (ed2->address != 0 && ii) - ii->st_blocks -= end_data - start_data; - - delete_tree_item(Vcb, &tp, rollback); - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned2 = (EXTENT_DATA2*)&ned->data[0]; - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = ed->decoded_size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - ned2->address = ed2->address; - ned2->size = ed2->size; - ned2->offset = ed2->address == 0 ? 0 : ed2->offset; - ned2->num_bytes = start_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); - if (!ned) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - ned2 = (EXTENT_DATA2*)&ned->data[0]; - - ned->generation = Vcb->superblock.generation; - ned->decoded_size = ed->decoded_size; - ned->compression = ed->compression; - ned->encryption = ed->encryption; - ned->encoding = ed->encoding; - ned->type = ed->type; - ned2->address = ed2->address; - ned2->size = ed2->size; - ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset)); - ned2->num_bytes = tp.item->key.offset + len - end_data; - - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (ed2->address != 0) { - Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, inode, tp.item->key.offset - ed2->offset, 1, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - goto end; - } - } - } - } + if (cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) { + ce->count += count; + cer->edr.count += count; + Status = STATUS_SUCCESS; + goto end; } - -cont: - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data) - break; + + le = le->Flink; + } + + old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset); + + if (old_count > 0) { + cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); + + if (!cer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; } - } while (b); + + cer->edr.root = root; + cer->edr.objid = objid; + cer->edr.offset = offset; + cer->edr.count = old_count; + + InsertTailList(&ce->old_refs, &cer->list_entry); + } - // FIXME - do bitmap analysis of changed extents, and free what we can + cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); - if (ii && deleted_prealloc && !is_file_prealloc_inode(Vcb, subvol, inode, 0, sector_align(ii->st_size, Vcb->superblock.sector_size))) - ii->flags &= ~BTRFS_INODE_PREALLOC; + if (!cer) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + cer->edr.root = root; + cer->edr.objid = objid; + cer->edr.offset = offset; + cer->edr.count = old_count + count; + + InsertTailList(&ce->refs, &cer->list_entry); + + ce->count += count; + + Status = STATUS_SUCCESS; end: + ExReleaseResourceLite(&c->nonpaged->changed_extents_lock); return Status; } -NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { +NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) { NTSTATUS Status; + LIST_ENTRY* le; - TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, changed_sector_list); - - Status = excise_extents_inode(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, start_data, end_data, changed_sector_list, rollback); - if (!NT_SUCCESS(Status)) { - ERR("excise_extents_inode returned %08x\n"); - return Status; + le = fcb->extents.Flink; + + while (le != &fcb->extents) { + LIST_ENTRY* le2 = le->Flink; + extent* ext = CONTAINING_RECORD(le, extent, list_entry); + EXTENT_DATA* ed = ext->data; + EXTENT_DATA2* ed2; + UINT64 len; + + if (!ext->ignore) { + 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)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + 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)); + Status = STATUS_INTERNAL_ERROR; + goto end; + } + + ed2 = (EXTENT_DATA2*)ed->data; + } + + 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 (ed->type == EXTENT_TYPE_INLINE) { + if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all + remove_fcb_extent(ext, rollback); + + fcb->inode_item.st_blocks -= len; + } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning + EXTENT_DATA* ned; + UINT64 size; + extent* newext; + + size = len - (end_data - ext->offset); + + ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned); + goto end; + } + + ned->generation = Vcb->superblock.generation; + ned->decoded_size = size; + ned->compression = ed->compression; + ned->encryption = ed->encryption; + ned->encoding = ed->encoding; + ned->type = ed->type; + + RtlCopyMemory(&ned->data[0], &ed->data[end_data - ext->offset], size); + + newext->offset = end_data; + newext->data = ned; + newext->datalen = sizeof(EXTENT_DATA) - 1 + size; + newext->unique = ext->unique; + newext->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext->list_entry); + + remove_fcb_extent(ext, rollback); + + fcb->inode_item.st_blocks -= end_data - ext->offset; + } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end + EXTENT_DATA* ned; + UINT64 size; + extent* newext; + + size = start_data - ext->offset; + + ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned); + goto end; + } + + ned->generation = Vcb->superblock.generation; + ned->decoded_size = size; + ned->compression = ed->compression; + ned->encryption = ed->encryption; + ned->encoding = ed->encoding; + ned->type = ed->type; + + RtlCopyMemory(&ned->data[0], &ed->data[0], size); + + newext->offset = ext->offset; + newext->data = ned; + newext->datalen = sizeof(EXTENT_DATA) - 1 + size; + newext->unique = ext->unique; + newext->ignore = FALSE; + InsertHeadList(&ext->list_entry, &newext->list_entry); + + remove_fcb_extent(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 + EXTENT_DATA *ned1, *ned2; + UINT64 size; + extent *newext1, *newext2; + + size = start_data - ext->offset; + + ned1 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); + if (!ned1) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned1); + goto end; + } + + ned1->generation = Vcb->superblock.generation; + ned1->decoded_size = size; + ned1->compression = ed->compression; + ned1->encryption = ed->encryption; + ned1->encoding = ed->encoding; + ned1->type = ed->type; + + RtlCopyMemory(&ned1->data[0], &ed->data[0], size); + + newext1->offset = ext->offset; + newext1->data = ned1; + newext1->datalen = sizeof(EXTENT_DATA) - 1 + size; + newext1->unique = FALSE; + newext1->ignore = FALSE; + + size = ext->offset + len - end_data; + + ned2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG); + if (!ned2) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned1); + ExFreePool(newext1); + goto end; + } + + newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext2) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned1); + ExFreePool(newext1); + ExFreePool(ned2); + goto end; + } + + ned2->generation = Vcb->superblock.generation; + ned2->decoded_size = size; + ned2->compression = ed->compression; + ned2->encryption = ed->encryption; + ned2->encoding = ed->encoding; + ned2->type = ed->type; + + RtlCopyMemory(&ned2->data[0], &ed->data[end_data - ext->offset], size); + + newext2->offset = end_data; + newext2->data = ned2; + newext2->datalen = sizeof(EXTENT_DATA) - 1 + size; + newext2->unique = FALSE; + newext2->ignore = FALSE; + + InsertHeadList(&ext->list_entry, &newext1->list_entry); + InsertHeadList(&newext1->list_entry, &newext2->list_entry); + + remove_fcb_extent(ext, rollback); + + fcb->inode_item.st_blocks -= end_data - start_data; + } + } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { + if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all + if (ed2->address != 0) { + chunk* c; + + fcb->inode_item.st_blocks -= len; + + 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); + goto end; + } + } + } + + remove_fcb_extent(ext, rollback); + } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning + EXTENT_DATA* ned; + EXTENT_DATA2* ned2; + extent* newext; + + if (ed2->address != 0) + fcb->inode_item.st_blocks -= end_data - ext->offset; + + ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned); + goto end; + } + + ned2 = (EXTENT_DATA2*)&ned->data[0]; + + ned->generation = Vcb->superblock.generation; + ned->decoded_size = ed->decoded_size; + ned->compression = ed->compression; + ned->encryption = ed->encryption; + ned->encoding = ed->encoding; + ned->type = ed->type; + ned2->address = ed2->address; + ned2->size = ed2->size; + ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - ext->offset)); + ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset); + + newext->offset = end_data; + newext->data = ned; + newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); + 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 + len) { // remove end + EXTENT_DATA* ned; + EXTENT_DATA2* ned2; + extent* newext; + + if (ed2->address != 0) + fcb->inode_item.st_blocks -= ext->offset + len - start_data; + + ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); + if (!ned) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(ned); + goto end; + } + + ned2 = (EXTENT_DATA2*)&ned->data[0]; + + ned->generation = Vcb->superblock.generation; + ned->decoded_size = ed->decoded_size; + ned->compression = ed->compression; + ned->encryption = ed->encryption; + ned->encoding = ed->encoding; + ned->type = ed->type; + ned2->address = ed2->address; + ned2->size = ed2->size; + ned2->offset = ed2->address == 0 ? 0 : ed2->offset; + ned2->num_bytes = start_data - ext->offset; + + newext->offset = ext->offset; + newext->data = ned; + newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); + 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 + len) { // remove middle + EXTENT_DATA *neda, *nedb; + EXTENT_DATA2 *neda2, *nedb2; + extent *newext1, *newext2; + + if (ed2->address != 0) { + chunk* c; + + fcb->inode_item.st_blocks -= end_data - start_data; + + 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); + goto end; + } + } + } + + neda = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); + if (!neda) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto end; + } + + newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(neda); + goto end; + } + + nedb = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); + if (!nedb) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(neda); + ExFreePool(newext1); + goto end; + } + + newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext1) { + ERR("out of memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(neda); + ExFreePool(newext1); + ExFreePool(nedb); + goto end; + } + + neda2 = (EXTENT_DATA2*)&neda->data[0]; + + neda->generation = Vcb->superblock.generation; + neda->decoded_size = ed->decoded_size; + neda->compression = ed->compression; + neda->encryption = ed->encryption; + neda->encoding = ed->encoding; + neda->type = ed->type; + neda2->address = ed2->address; + neda2->size = ed2->size; + neda2->offset = ed2->address == 0 ? 0 : ed2->offset; + neda2->num_bytes = start_data - ext->offset; + + nedb2 = (EXTENT_DATA2*)&nedb->data[0]; + + nedb->generation = Vcb->superblock.generation; + nedb->decoded_size = ed->decoded_size; + nedb->compression = ed->compression; + nedb->encryption = ed->encryption; + nedb->encoding = ed->encoding; + nedb->type = ed->type; + nedb2->address = ed2->address; + nedb2->size = ed2->size; + nedb2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - ext->offset)); + nedb2->num_bytes = ext->offset + len - end_data; + + newext1->offset = ext->offset; + newext1->data = neda; + newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); + newext1->unique = FALSE; + newext1->ignore = FALSE; + + newext2->offset = end_data; + newext2->data = nedb; + newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); + newext2->unique = FALSE; + newext2->ignore = FALSE; + + InsertHeadList(&ext->list_entry, &newext1->list_entry); + InsertHeadList(&newext1->list_entry, &newext2->list_entry); + + remove_fcb_extent(ext, rollback); + } + } + } + } + + le = le2; } - return STATUS_SUCCESS; + // FIXME - do bitmap analysis of changed extents, and free what we can + + Status = STATUS_SUCCESS; + +end: + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + + return Status; } -static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, UINT64 length, LIST_ENTRY* changed_sector_list) { +static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, UINT64 length, LIST_ENTRY* changed_sector_list, PIRP Irp) { NTSTATUS Status; changed_sector* sc; int i; - Status = write_data(Vcb, address, data, length); + Status = write_data_complete(Vcb, address, data, length, Irp); if (!NT_SUCCESS(Status)) { ERR("write_data returned %08x\n", Status); return Status; @@ -4447,27 +5914,126 @@ static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, return STATUS_SUCCESS; } -BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data, - UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { +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; + + ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!ext) { + ERR("out of memory\n"); + return FALSE; + } + + ext->offset = offset; + ext->data = ed; + ext->datalen = edsize; + ext->unique = unique; + ext->ignore = FALSE; + + le = fcb->extents.Flink; + while (le != &fcb->extents) { + extent* oldext = CONTAINING_RECORD(le, extent, list_entry); + + if (!oldext->ignore) { + if (oldext->offset > offset) { + InsertHeadList(le->Blink, &ext->list_entry); + goto end; + } + } + + le = le->Flink; + } + + InsertTailList(&fcb->extents, &ext->list_entry); + +end: + add_rollback(rollback, ROLLBACK_INSERT_EXTENT, ext); + + return TRUE; +} + +void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback) { + if (!ext->ignore) { + ext->ignore = TRUE; + add_rollback(rollback, ROLLBACK_DELETE_EXTENT, ext); + } +} + +static void add_changed_extent_ref(chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, UINT32 count, BOOL no_csum) { + changed_extent* ce; + changed_extent_ref* cer; + LIST_ENTRY* le; + + ce = get_changed_extent_item(c, address, size, no_csum); + + if (!ce) { + ERR("get_changed_extent_item failed\n"); + return; + } + + le = ce->refs.Flink; + while (le != &ce->refs) { + cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry); + + if (cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) { + ce->count += count; + cer->edr.count += count; + return; + } + + le = le->Flink; + } + + cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); + + if (!cer) { + ERR("out of memory\n"); + return; + } + + cer->edr.root = root; + cer->edr.objid = objid; + cer->edr.offset = offset; + cer->edr.count = count; + + InsertTailList(&ce->refs, &cer->list_entry); + + ce->count += count; +} + +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) { UINT64 address; NTSTATUS Status; EXTENT_DATA* ed; EXTENT_DATA2* ed2; ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); +// #ifdef DEBUG_PARANOID +// traverse_ptr tp; +// KEY searchkey; +// #endif - TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb, subvol->id, inode, c->offset, start_data, length, data, changed_sector_list); + TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, prealloc, data, changed_sector_list, rollback); if (!find_address_in_chunk(Vcb, c, length, &address)) return FALSE; - Status = increase_extent_refcount_data(Vcb, address, length, subvol, inode, start_data, 1, rollback); - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - return FALSE; - } +// #ifdef DEBUG_PARANOID +// searchkey.obj_id = address; +// searchkey.obj_type = TYPE_EXTENT_ITEM; +// searchkey.offset = 0xffffffffffffffff; +// +// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); +// if (!NT_SUCCESS(Status)) { +// ERR("error - find_item returned %08x\n", Status); +// } else if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { +// ERR("address %llx already allocated\n", address); +// int3; +// } +// #endif if (data) { - Status = do_write_data(Vcb, address, data, length, changed_sector_list); + Status = do_write_data(Vcb, address, data, length, changed_sector_list, Irp); if (!NT_SUCCESS(Status)) { ERR("do_write_data returned %08x\n", Status); return FALSE; @@ -4494,96 +6060,95 @@ BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode ed2->offset = 0; ed2->num_bytes = length; - if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, start_data, ed, edsize, NULL, rollback)) { - ERR("insert_tree_item failed\n"); + if (!add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, rollback)) { + ERR("add_extent_to_fcb failed\n"); ExFreePool(ed); return FALSE; } increase_chunk_usage(c, length); - add_to_space_list(c, address, length, SPACE_TYPE_WRITING); + space_list_subtract(Vcb, c, FALSE, address, length, rollback); - if (inode_item) { - inode_item->st_blocks += length; - - if (prealloc) - inode_item->flags |= BTRFS_INODE_PREALLOC; - } + fcb->inode_item.st_blocks += length; + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + return TRUE; } -static 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, LIST_ENTRY* rollback) { - return insert_extent_chunk_inode(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, c, start_data, length, prealloc, data, changed_sector_list, rollback); -} - static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, - LIST_ENTRY* changed_sector_list, traverse_ptr* edtp, traverse_ptr* eitp, LIST_ENTRY* rollback) { + LIST_ENTRY* changed_sector_list, extent* ext, chunk* c, PIRP Irp, LIST_ENTRY* rollback) { EXTENT_DATA* ed; EXTENT_DATA2* ed2; - EXTENT_ITEM* ei; + extent* newext; + UINT64 addr; NTSTATUS Status; - changed_sector* sc; - chunk* c; - int i; TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, - length, data, changed_sector_list, edtp, eitp); + length, data, changed_sector_list, ext, c, rollback); - ed = ExAllocatePoolWithTag(PagedPool, edtp->item->size, ALLOC_TAG); + ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); if (!ed) { ERR("out of memory\n"); return FALSE; } - RtlCopyMemory(ed, edtp->item->data, edtp->item->size); - - ed->decoded_size += length; - ed2 = (EXTENT_DATA2*)ed->data; - ed2->size += length; - ed2->num_bytes += length; - - delete_tree_item(Vcb, edtp, rollback); - - if (!insert_tree_item(Vcb, fcb->subvol, edtp->item->key.obj_id, edtp->item->key.obj_type, edtp->item->key.offset, ed, edtp->item->size, NULL, rollback)) { - TRACE("insert_tree_item failed\n"); - - ExFreePool(ed); - return FALSE; - } - - ei = ExAllocatePoolWithTag(PagedPool, eitp->item->size, ALLOC_TAG); - if (!ei) { + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { ERR("out of memory\n"); ExFreePool(ed); return FALSE; } - RtlCopyMemory(ei, eitp->item->data, eitp->item->size); + RtlCopyMemory(ed, ext->data, ext->datalen); - if (!insert_tree_item(Vcb, Vcb->extent_root, eitp->item->key.obj_id, eitp->item->key.obj_type, eitp->item->key.offset + length, ei, eitp->item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - - ExFreePool(ei); + 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; } - delete_tree_item(Vcb, eitp, rollback); + ed2->size += length; + ed2->num_bytes += length; - Status = write_data(Vcb, eitp->item->key.obj_id + eitp->item->key.offset, data, length); + RtlCopyMemory(newext, ext, sizeof(extent)); + newext->data = ed; + + InsertHeadList(&ext->list_entry, &newext->list_entry); + + remove_fcb_extent(ext, rollback); + + 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); + if (!NT_SUCCESS(Status)) { - ERR("write_data returned %08x\n", Status); + ERR("update_changed_extent_ref returned %08x\n", Status); return FALSE; } if (changed_sector_list) { - sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); + int i; + changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); if (!sc) { ERR("out of memory\n"); return FALSE; } - sc->ol.key = eitp->item->key.obj_id + eitp->item->key.offset; + sc->ol.key = addr; sc->length = length / Vcb->superblock.sector_size; sc->deleted = FALSE; @@ -4600,51 +6165,57 @@ static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT insert_into_ordered_list(changed_sector_list, &sc->ol); } - c = get_chunk_from_address(Vcb, eitp->item->key.obj_id); - - if (c) { - increase_chunk_usage(c, length); - - add_to_space_list(c, eitp->item->key.obj_id + eitp->item->key.offset, length, SPACE_TYPE_WRITING); - } - + increase_chunk_usage(c, length); + + space_list_subtract(Vcb, c, FALSE, addr, length, rollback); + fcb->inode_item.st_blocks += length; return TRUE; } static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, - LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp, tp2; + LIST_ENTRY* changed_sector_list, PIRP Irp, UINT64* written, LIST_ENTRY* rollback) { BOOL success = FALSE; EXTENT_DATA* ed; EXTENT_DATA2* ed2; - EXTENT_ITEM* ei; chunk* c; LIST_ENTRY* le; space* s; - NTSTATUS Status; + extent* ext = NULL; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; + le = fcb->extents.Flink; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return FALSE; + 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 (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) { + if (!ext) { WARN("previous EXTENT_DATA not found\n"); goto end; } - ed = (EXTENT_DATA*)tp.item->data; + if (!ext->unique) { + TRACE("extent was not unique\n"); + goto end; + } - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); + 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; } @@ -4655,13 +6226,13 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, ed2 = (EXTENT_DATA2*)ed->data; - if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - 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(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); + 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; } - if (tp.item->key.offset + ed2->num_bytes != start_data) { - TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp.item->key.offset, ed2->num_bytes, start_data); + 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; } @@ -4685,147 +6256,114 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, goto end; } - searchkey.obj_id = ed2->address; - searchkey.obj_type = TYPE_EXTENT_ITEM; - searchkey.offset = ed2->size; - - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - if (keycmp(&tp2.item->key, &searchkey)) { - ERR("error - extent %llx,%llx not found in tree\n", ed2->address, ed2->size); - int3; // TESTING - goto end; - } - - if (tp2.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert - NTSTATUS Status = convert_old_data_extent(Vcb, ed2->address, ed2->size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_old_data_extent returned %08x\n", Status); - goto end; - } - - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - if (keycmp(&tp2.item->key, &searchkey)) { - WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size); - goto end; - } - } - - ei = (EXTENT_ITEM*)tp2.item->data; - - if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); - goto end; - } - - // FIXME - test this - if (extent_item_is_shared(ei, tp2.item->size - sizeof(EXTENT_ITEM))) { - NTSTATUS Status = convert_shared_data_extent(Vcb, ed2->address, ed2->size, rollback); - if (!NT_SUCCESS(Status)) { - ERR("convert_shared_data_extent returned %08x\n", Status); - goto end; - } - - Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto end; - } - - if (keycmp(&tp2.item->key, &searchkey)) { - WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size); - goto end; - } - - ei = (EXTENT_ITEM*)tp2.item->data; - - if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); - goto end; - } - } - - if (ei->refcount != 1) { - TRACE("extent refcount was not 1\n"); - goto end; - } - - if (ei->flags != EXTENT_ITEM_DATA) { - ERR("error - extent was not a data extent\n"); + 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; } c = get_chunk_from_address(Vcb, ed2->address); + ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE); + le = c->space.Flink; while (le != &c->space) { s = CONTAINING_RECORD(le, space, list_entry); - if (s->offset == ed2->address + ed2->size) { - if (s->type == SPACE_TYPE_FREE && s->size >= length) { - success = extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, &tp, &tp2, rollback); - } + if (s->address == ed2->address + ed2->size) { + UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE - ed2->size); + + success = extend_data(Vcb, fcb, start_data, newlen, data, changed_sector_list, ext, c, Irp, rollback); + + if (success) + *written += newlen; + break; - } else if (s->offset > ed2->address + ed2->size) + } else if (s->address > ed2->address + ed2->size) break; le = le->Flink; } + ExReleaseResourceLite(&c->nonpaged->lock); + end: return success; } static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { - LIST_ENTRY* le = fcb->Vcb->chunks.Flink; + LIST_ENTRY* le; chunk* c; +#ifdef __REACTOS__ UINT64 flags; +#else + UINT64 flags, origlength = length; +#endif + NTSTATUS Status; - // FIXME - how do we know which RAID level to put this to? - flags = BLOCK_FLAG_DATA; // SINGLE + flags = fcb->Vcb->data_flags; - // FIXME - if length is more than max chunk size, loop through and - // create the new chunks first + // FIXME - try and maximize contiguous ranges first. If we can't do that, + // allocate all the free space we find until it's enough. - while (le != &fcb->Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); + ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); + + do { + UINT64 extlen = min(MAX_EXTENT_SIZE, length); - if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, TRUE, NULL, NULL, rollback)) - return STATUS_SUCCESS; - } + le = fcb->Vcb->chunks.Flink; + while (le != &fcb->Vcb->chunks) { + c = CONTAINING_RECORD(le, chunk, list_entry); + + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + goto cont; + } + } + + ExReleaseResourceLite(&c->nonpaged->lock); - le = le->Flink; - } - - if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) { - if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, TRUE, NULL, NULL, rollback)) - return STATUS_SUCCESS; + le = le->Flink; } - } + + if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) { + ExAcquireResourceExclusiveLite(&c->nonpaged->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); + goto cont; + } + } + + ExReleaseResourceLite(&c->nonpaged->lock); + } + + WARN("couldn't find any data chunks with %llx bytes free\n", origlength); + Status = STATUS_DISK_FULL; + goto end; + +cont: + length -= extlen; + start += extlen; + } while (length > 0); - // FIXME - rebalance chunks if free space elsewhere? - WARN("couldn't find any data chunks with %llx bytes free\n", length); + Status = STATUS_SUCCESS; + +end: + ExReleaseResourceLite(&fcb->Vcb->chunk_lock); - return STATUS_DISK_FULL; + return Status; } -NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { +NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { EXTENT_DATA* ed; EXTENT_DATA2* ed2; - TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb, r->id, inode, start, length); + 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) { @@ -4833,7 +6371,7 @@ NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT return STATUS_INSUFFICIENT_RESOURCES; } - ed->generation = Vcb->superblock.generation; + ed->generation = fcb->Vcb->superblock.generation; ed->decoded_size = length; ed->compression = BTRFS_COMPRESSION_NONE; ed->encryption = BTRFS_ENCRYPTION_NONE; @@ -4845,13 +6383,15 @@ NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT ed2->size = 0; ed2->offset = 0; ed2->num_bytes = length; - - if (!insert_tree_item(Vcb, r, inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ed); + + 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; } @@ -4864,76 +6404,71 @@ NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT // } // } -static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - LIST_ENTRY* le = Vcb->chunks.Flink; +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) { + LIST_ENTRY* le; chunk* c; - KEY searchkey; - UINT64 flags; + UINT64 flags, orig_length = length, written = 0; TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list); // FIXME - split data up if not enough space for just one extent - if (start_data > 0 && try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, rollback)) - return STATUS_SUCCESS; + if (start_data > 0) { + try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, Irp, &written, rollback); + + if (written == length) + return STATUS_SUCCESS; + else if (written > 0) { + start_data += written; + length -= written; + data = &((UINT8*)data)[written]; + } + } // 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) { - traverse_ptr tp; NTSTATUS Status; EXTENT_DATA* ed; + extent* lastext = NULL; UINT64 len; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; - - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; + 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 (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) { -// traverse_ptr next_tp; -// -// ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n", -// searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); -// print_tree(tp.tree); -// -// if (find_next_item(Vcb, &tp, &next_tp, FALSE)) { -// ERR("---\n"); -// ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset); -// print_tree(next_tp.tree); -// -// free_traverse_ptr(&next_tp); -// } else -// ERR("next item not found\n"); -// -// int3; -// free_traverse_ptr(&tp); -// return STATUS_INTERNAL_ERROR; -// } - if (tp.item->key.obj_type == TYPE_EXTENT_DATA && tp.item->size >= sizeof(EXTENT_DATA)) { + if (lastext && lastext->datalen >= sizeof(EXTENT_DATA)) { EXTENT_DATA2* ed2; - ed = (EXTENT_DATA*)tp.item->data; + ed = lastext->data; ed2 = (EXTENT_DATA2*)ed->data; len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; } else ed = NULL; - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || !ed || tp.item->key.offset + len < start_data) { - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA) - Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data, rollback); - else if (!ed) - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - else { - Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + len, - start_data - tp.item->key.offset - len, rollback); - } + 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; @@ -4941,53 +6476,88 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data } } - // FIXME - how do we know which RAID level to put this to? - flags = BLOCK_FLAG_DATA; // SINGLE + flags = Vcb->data_flags; -// if (!chunk_test) { // TESTING -// if ((c = alloc_chunk(Vcb, flags, NULL))) { -// ERR("chunk_item->type = %llx\n", c->chunk_item->type); -// ERR("size = %llx\n", c->chunk_item->size); -// ERR("used = %llx\n", c->used); -// -// if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { -// if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) { -// // chunk_test = TRUE; -// ERR("SUCCESS\n"); -// return STATUS_SUCCESS; -// } else -// ERR(":-(\n"); -// } else -// ERR("???\n"); -// } -// } + ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); - while (le != &Vcb->chunks) { - c = CONTAINING_RECORD(le, chunk, list_entry); + while (written < orig_length) { + UINT64 newlen = min(length, MAX_EXTENT_SIZE); + BOOL done = FALSE; - if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(Vcb, fcb, c, start_data, length, FALSE, data, changed_sector_list, rollback)) - return STATUS_SUCCESS; - } + // 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. + + le = Vcb->chunks.Flink; + while (le != &Vcb->chunks) { + c = CONTAINING_RECORD(le, chunk, list_entry); + + ExAcquireResourceExclusiveLite(&c->nonpaged->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)) { + written += newlen; + + if (written == orig_length) { + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&Vcb->chunk_lock); + return STATUS_SUCCESS; + } else { + done = TRUE; + start_data += newlen; + length -= newlen; + data = &((UINT8*)data)[newlen]; + break; + } + } + } + + ExReleaseResourceLite(&c->nonpaged->lock); - le = le->Flink; - } - - if ((c = alloc_chunk(Vcb, flags, rollback))) { - if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) { - if (insert_extent_chunk(Vcb, fcb, c, start_data, length, FALSE, data, changed_sector_list, rollback)) - return STATUS_SUCCESS; + le = le->Flink; + } + + 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); + + 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)) { + written += newlen; + + if (written == orig_length) { + ExReleaseResourceLite(&c->nonpaged->lock); + ExReleaseResourceLite(&Vcb->chunk_lock); + return STATUS_SUCCESS; + } else { + done = TRUE; + start_data += newlen; + length -= newlen; + data = &((UINT8*)data)[newlen]; + } + } + } + + ExReleaseResourceLite(&c->nonpaged->lock); + } + + if (!done) { + FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME + break; } } - // FIXME - rebalance chunks if free space elsewhere? + ExReleaseResourceLite(&Vcb->chunk_lock); + WARN("couldn't find any data chunks with %llx bytes free\n", length); return STATUS_DISK_FULL; } -void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - LIST_ENTRY* le = changed_sector_list->Flink; +static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) { + LIST_ENTRY* le = Vcb->sector_checksums.Flink; changed_sector* cs; traverse_ptr tp, next_tp; KEY searchkey; @@ -4999,7 +6569,7 @@ void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list goto exit; } - while (le != changed_sector_list) { + while (le != &Vcb->sector_checksums) { UINT64 startaddr, endaddr; ULONG len; UINT32* checksums; @@ -5164,8 +6734,8 @@ void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list } exit: - while (!IsListEmpty(changed_sector_list)) { - le = RemoveHeadList(changed_sector_list); + while (!IsListEmpty(&Vcb->sector_checksums)) { + le = RemoveHeadList(&Vcb->sector_checksums); cs = (changed_sector*)le; if (cs->checksums) @@ -5175,18 +6745,20 @@ exit: } } +void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list) { + while (!IsListEmpty(changed_sector_list)) { + LIST_ENTRY* le = RemoveHeadList(changed_sector_list); + InsertTailList(&Vcb->sector_checksums, le); + } +} + NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) { - LIST_ENTRY changed_sector_list; NTSTATUS Status; - BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM; - - if (!nocsum) - InitializeListHead(&changed_sector_list); // FIXME - convert into inline extent if short enough Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size), - sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), nocsum ? NULL : &changed_sector_list, rollback); + sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback); if (!NT_SUCCESS(Status)) { ERR("error - excise_extents failed\n"); return Status; @@ -5202,45 +6774,45 @@ NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) { TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart); - if (!nocsum) - update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback); - return STATUS_SUCCESS; } -NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback) { +NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback) { UINT64 oldalloc, newalloc; - KEY searchkey; - traverse_ptr tp; BOOL cur_inline; NTSTATUS Status; - TRACE("(%p, %x, %p)\n", fcb, end, rollback); + TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc); if (fcb->ads) - return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE, rollback) ; + return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE, rollback); else { - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = 0xffffffffffffffff; + extent* ext = NULL; + LIST_ENTRY* le; - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; + le = fcb->extents.Blink; + while (le != &fcb->extents) { + extent* ext2 = CONTAINING_RECORD(le, extent, list_entry); + + if (!ext2->ignore) { + ext = ext2; + break; + } + + le = le->Blink; } oldalloc = 0; - if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) { - EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; + if (ext) { + EXTENT_DATA* ed = ext->data; EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); + 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; } - oldalloc = tp.item->key.offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes); + 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) { @@ -5248,6 +6820,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM; UINT64 origlength, length; UINT8* data; + UINT64 offset = ext->offset; TRACE("giving inline file proper extents\n"); @@ -5258,8 +6831,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS if (!nocsum) InitializeListHead(&changed_sector_list); - delete_tree_item(fcb->Vcb, &tp, rollback); - length = sector_align(origlength, fcb->Vcb->superblock.sector_size); data = ExAllocatePoolWithTag(PagedPool, length, ALLOC_TAG); @@ -5275,26 +6846,31 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS fcb->inode_item.st_blocks -= origlength; - Status = insert_extent(fcb->Vcb, fcb, tp.item->key.offset, length, data, nocsum ? NULL : &changed_sector_list, rollback); + remove_fcb_extent(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; } - oldalloc = tp.item->key.offset + length; + oldalloc = ext->offset + length; ExFreePool(data); - if (!nocsum) - update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback); + if (!nocsum) { + ExAcquireResourceExclusiveLite(&fcb->Vcb->checksum_lock, TRUE); + commit_checksum_changes(fcb->Vcb, &changed_sector_list); + ExReleaseResourceLite(&fcb->Vcb->checksum_lock); + } } if (cur_inline) { ULONG edsize; if (end > oldalloc) { - edsize = sizeof(EXTENT_DATA) - 1 + end - tp.item->key.offset; + edsize = sizeof(EXTENT_DATA) - 1 + end - ext->offset; ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG); if (!ed) { @@ -5303,17 +6879,20 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS } RtlZeroMemory(ed, edsize); - RtlCopyMemory(ed, tp.item->data, tp.item->size); + RtlCopyMemory(ed, ext->data, ext->datalen); - ed->decoded_size = end - tp.item->key.offset; + ed->decoded_size = end - ext->offset; - delete_tree_item(fcb->Vcb, &tp, rollback); + remove_fcb_extent(ext, rollback); - if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ed, edsize, NULL, rollback)) { - ERR("error - failed to insert item\n"); + if (!add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, rollback)) { + ERR("add_extent_to_fcb failed\n"); ExFreePool(ed); return STATUS_INTERNAL_ERROR; } + + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); } TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end); @@ -5338,13 +6917,16 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS return Status; } } else { - Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, oldalloc, newalloc - oldalloc, rollback); + 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; + mark_fcb_dirty(fcb); } fcb->inode_item.st_size = end; @@ -5367,7 +6949,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS return Status; } } else { - Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, 0, newalloc, rollback); + Status = insert_sparse_extent(fcb, 0, newalloc, rollback); if (!NT_SUCCESS(Status)) { ERR("insert_sparse_extent returned %08x\n", Status); @@ -5375,6 +6957,9 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS } } + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + fcb->inode_item.st_size = end; TRACE("setting st_size to %llx\n", end); @@ -5402,13 +6987,16 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS ed->type = EXTENT_TYPE_INLINE; RtlZeroMemory(ed->data, end); - - if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed, edsize, NULL, rollback)) { - ERR("error - failed to insert item\n"); + + if (!add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, rollback)) { + ERR("add_extent_to_fcb failed\n"); ExFreePool(ed); return STATUS_INTERNAL_ERROR; } + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); + fcb->inode_item.st_size = end; TRACE("setting st_size to %llx\n", end); @@ -5422,53 +7010,73 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIS return STATUS_SUCCESS; } -static UINT64 get_extent_item_refcount(device_extension* Vcb, UINT64 address) { - KEY searchkey; - traverse_ptr tp; - EXTENT_ITEM* ei; - UINT64 rc; - NTSTATUS Status; - - searchkey.obj_id = address; - searchkey.obj_type = TYPE_EXTENT_ITEM; - searchkey.offset = 0xffffffffffffffff; - - Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE); - 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("error - could not find EXTENT_ITEM for %llx\n", address); - return 0; - } - - if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM)); - return 0; - } - - ei = (EXTENT_ITEM*)tp.item->data; - rc = ei->refcount; - - return rc; -} - static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data) { - return is_file_prealloc_inode(fcb->Vcb, fcb->subvol, fcb->inode, start_data, 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, LIST_ENTRY* rollback) { +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, changed_sector_list, rollback); + 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, rollback); + 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); @@ -5481,779 +7089,723 @@ end: return Status; } -static NTSTATUS merge_data_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp, next_tp; +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; - BOOL b; - EXTENT_DATA* ed; + UINT64 last_written = start_data; + extent* ext = NULL; + LIST_ENTRY* le; + chunk* c; - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; + le = fcb->extents.Flink; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA) { - ERR("error - EXTENT_DATA not found\n"); - return STATUS_INTERNAL_ERROR; - } - - if (tp.item->key.offset > 0) { - traverse_ptr tp2, prev_tp; + while (le != &fcb->extents) { + extent* nextext = CONTAINING_RECORD(le, extent, list_entry); - tp2 = tp; - do { - b = find_prev_item(Vcb, &tp2, &prev_tp, FALSE); + if (!nextext->ignore) { + if (nextext->offset == start_data) { + ext = nextext; + break; + } else if (nextext->offset > start_data) + break; - if (b) { - if (!prev_tp.item->ignore) - break; - - tp2 = prev_tp; - } - } while (b); - - if (b) { - if (prev_tp.item->key.obj_id == fcb->inode && prev_tp.item->key.obj_type == TYPE_EXTENT_DATA) - tp = prev_tp; + ext = nextext; } - } - - ed = (EXTENT_DATA*)tp.item->data; - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - do { - b = find_next_item(Vcb, &tp, &next_tp, FALSE); - if (b) { - EXTENT_DATA* ned; + 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 (next_tp.item->key.obj_id != fcb->inode || next_tp.item->key.obj_type != TYPE_EXTENT_DATA) - return STATUS_SUCCESS; - - if (next_tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset, next_tp.item->size, sizeof(EXTENT_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)); return STATUS_INTERNAL_ERROR; } - ned = (EXTENT_DATA*)next_tp.item->data; - if ((ned->type == EXTENT_TYPE_REGULAR || ned->type == EXTENT_TYPE_PREALLOC) && next_tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset, next_tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - if (ed->type == ned->type && (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC)) { - EXTENT_DATA2 *ed2, *ned2; + 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; - ned2 = (EXTENT_DATA2*)ned->data; - - if (next_tp.item->key.offset == tp.item->key.offset + ed2->num_bytes && ed2->address == ned2->address && ed2->size == ned2->size && ned2->offset == ed2->offset + ed2->num_bytes) { - EXTENT_DATA* buf = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - EXTENT_DATA2* buf2; - traverse_ptr tp2; + } + + 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 (!buf) { + 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; } - RtlCopyMemory(buf, tp.item->data, tp.item->size); - buf->generation = Vcb->superblock.generation; - - buf2 = (EXTENT_DATA2*)buf->data; - buf2->num_bytes += ned2->num_bytes; - - delete_tree_item(Vcb, &tp, rollback); - delete_tree_item(Vcb, &next_tp, rollback); - - if (!insert_tree_item(Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, buf, tp.item->size, &tp2, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(buf); - return STATUS_INTERNAL_ERROR; + newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG); + if (!newext) { + ERR("out of memory\n"); + ExFreePool(ned); + return STATUS_INSUFFICIENT_RESOURCES; } - Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - buf2->offset, 1, NULL, rollback); + 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("decrease_extent_refcount_data returned %08x\n", Status); + ERR("do_write_data returned %08x\n", Status); return Status; } - - tp = tp2; - continue; + 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); } } - - tp = next_tp; - ed = ned; } - } while (b); - - return STATUS_SUCCESS; -} - -static NTSTATUS do_prealloc_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - NTSTATUS Status; - KEY searchkey; - traverse_ptr tp, next_tp; - BOOL b, deleted_prealloc = FALSE; - UINT64 last_written = start_data; - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; - - Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; + + le = le2; } - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA) - return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, rollback); - - do { - EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; - EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - return STATUS_INTERNAL_ERROR; - } - - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - return STATUS_INTERNAL_ERROR; - } - - b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE); - - if (ed->type == EXTENT_TYPE_PREALLOC) { - if (tp.item->key.offset > last_written) { - Status = do_cow_write(Vcb, fcb, last_written, tp.item->key.offset, (UINT8*)data + last_written - start_data, changed_sector_list, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("do_cow_write returned %08x\n", Status); - - return Status; - } - - last_written = tp.item->key.offset; - } - - if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed2->num_bytes) { // replace all - EXTENT_DATA* ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!ned) { - ERR("out of memory\n"); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(ned, tp.item->data, tp.item->size); - - ned->type = EXTENT_TYPE_REGULAR; - - delete_tree_item(Vcb, &tp, rollback); - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - - return STATUS_INTERNAL_ERROR; - } - - Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + tp.item->key.offset - start_data, ed2->num_bytes, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - - return Status; - } - - deleted_prealloc = TRUE; - - last_written = tp.item->key.offset + ed2->num_bytes; - } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed2->num_bytes) { // replace beginning - EXTENT_DATA *ned, *nedb; - EXTENT_DATA2* ned2; - - ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!ned) { - ERR("out of memory\n"); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - delete_tree_item(Vcb, &tp, rollback); - - RtlCopyMemory(ned, tp.item->data, tp.item->size); - - ned->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = end_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - ExFreePool(nedb); - - return STATUS_INTERNAL_ERROR; - } - - RtlCopyMemory(nedb, tp.item->data, tp.item->size); - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += end_data - tp.item->key.offset; - ned2->num_bytes -= end_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, nedb, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(nedb); - - return STATUS_INTERNAL_ERROR; - } - - Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + tp.item->key.offset - start_data, end_data - tp.item->key.offset, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - - return Status; - } - - Status = increase_extent_refcount_data(Vcb, ned2->address, ned2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 1, rollback); - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - return Status; - } - - last_written = end_data; - } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed2->num_bytes) { // replace end - EXTENT_DATA *ned, *nedb; - EXTENT_DATA2* ned2; - - // FIXME - test this - - ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!ned) { - ERR("out of memory\n"); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - delete_tree_item(Vcb, &tp, rollback); - - RtlCopyMemory(ned, tp.item->data, tp.item->size); - - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = start_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - ExFreePool(nedb); - - return STATUS_INTERNAL_ERROR; - } - - RtlCopyMemory(nedb, tp.item->data, tp.item->size); - - nedb->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += start_data - tp.item->key.offset; - ned2->num_bytes = tp.item->key.offset + ed2->num_bytes - start_data; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, nedb, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(nedb); - - return STATUS_INTERNAL_ERROR; - } - - Status = do_write_data(Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - - return Status; - } - - Status = increase_extent_refcount_data(Vcb, ned2->address, ned2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 1, rollback); - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - - return Status; - } - - last_written = start_data + ned2->num_bytes; - } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed2->num_bytes) { // replace middle - EXTENT_DATA *ned, *nedb, *nedc; - EXTENT_DATA2* ned2; - - // FIXME - test this - - ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!ned) { - ERR("out of memory\n"); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(ned); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - nedc = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); - - if (!nedb) { - ERR("out of memory\n"); - ExFreePool(nedb); - ExFreePool(ned); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - delete_tree_item(Vcb, &tp, rollback); - - RtlCopyMemory(ned, tp.item->data, tp.item->size); - RtlCopyMemory(nedb, tp.item->data, tp.item->size); - RtlCopyMemory(nedc, tp.item->data, tp.item->size); - - ned2 = (EXTENT_DATA2*)ned->data; - ned2->num_bytes = start_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(ned); - ExFreePool(nedb); - ExFreePool(nedc); - - return STATUS_INTERNAL_ERROR; - } - - nedb->type = EXTENT_TYPE_REGULAR; - ned2 = (EXTENT_DATA2*)nedb->data; - ned2->offset += start_data - tp.item->key.offset; - ned2->num_bytes = end_data - start_data; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, nedb, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(nedb); - ExFreePool(nedc); - - return STATUS_INTERNAL_ERROR; - } - - ned2 = (EXTENT_DATA2*)nedc->data; - ned2->offset += end_data - tp.item->key.offset; - ned2->num_bytes -= end_data - tp.item->key.offset; - - if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, nedc, tp.item->size, NULL, rollback)) { - ERR("insert_tree_item failed\n"); - ExFreePool(nedc); - - return STATUS_INTERNAL_ERROR; - } - - ned2 = (EXTENT_DATA2*)nedb->data; - Status = do_write_data(Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list); - if (!NT_SUCCESS(Status)) { - ERR("do_write_data returned %08x\n", Status); - - return Status; - } - - Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 2, rollback); - if (!NT_SUCCESS(Status)) { - ERR("increase_extent_refcount_data returned %08x\n", Status); - return Status; - } - - last_written = end_data; - } - } - - if (b) { - tp = next_tp; - - if (tp.item->key.obj_id > fcb->inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data) - break; - } - } while (b); - if (last_written < end_data) { - Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, rollback); + 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 = merge_data_extents(Vcb, fcb, start_data, end_data, rollback); - if (!NT_SUCCESS(Status)) { - ERR("merge_data_extents returned %08x\n", Status); - return Status; - } - - if (deleted_prealloc && !is_file_prealloc(fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size))) - fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC; + + fcb->extents_changed = TRUE; + mark_fcb_dirty(fcb); return STATUS_SUCCESS; } -static NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) { - KEY searchkey; - traverse_ptr tp, next_tp; +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; - EXTENT_DATA* ed; - BOOL b, do_cow; - EXTENT_DATA2* eds; - UINT64 size, new_start, new_end, last_write = 0; + 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, length, data, changed_sector_list); + TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, data, changed_sector_list); - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = start_data; + le = fcb->extents.Flink; - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - return Status; - } - - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset > start_data) { - ERR("previous EXTENT_DATA not found (found %llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - do { - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - Status = STATUS_INTERNAL_ERROR; - goto end; + 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; } - ed = (EXTENT_DATA*)tp.item->data; + 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; - if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); - Status = STATUS_INTERNAL_ERROR; - goto end; - } + ext = CONTAINING_RECORD(le, extent, list_entry); - eds = (EXTENT_DATA2*)&ed->data[0]; - - b = find_next_item(Vcb, &tp, &next_tp, TRUE); - - switch (ed->type) { - case EXTENT_TYPE_REGULAR: - { - UINT64 rc = get_extent_item_refcount(Vcb, eds->address); - - if (rc == 0) { - ERR("get_extent_item_refcount failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; + 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; } - do_cow = rc > 1; - break; + ed2 = (EXTENT_DATA2*)ed->data; } - - case EXTENT_TYPE_INLINE: + + if (ed->type == EXTENT_TYPE_REGULAR) { + do_cow = !ext->unique; + } else { do_cow = TRUE; - break; - - case EXTENT_TYPE_PREALLOC: - FIXME("FIXME - handle prealloc extents\n"); // FIXME - Status = STATUS_NOT_SUPPORTED; - goto end; - - default: - ERR("error - unknown extent type %x\n", ed->type); - Status = STATUS_NOT_SUPPORTED; - goto end; - } - - if (ed->compression != BTRFS_COMPRESSION_NONE) { - FIXME("FIXME: compression not yet supported\n"); - Status = STATUS_NOT_SUPPORTED; - goto end; - } - - if (ed->encryption != BTRFS_ENCRYPTION_NONE) { - WARN("encryption not supported\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (ed->encoding != BTRFS_ENCODING_NONE) { - WARN("other encodings not supported\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : eds->num_bytes; - - TRACE("extent: start = %llx, length = %llx\n", tp.item->key.offset, size); - - new_start = tp.item->key.offset < start_data ? start_data : tp.item->key.offset; - new_end = tp.item->key.offset + size > start_data + length ? (start_data + length) : (tp.item->key.offset + size); - - TRACE("new_start = %llx\n", new_start); - TRACE("new_end = %llx\n", new_end); - - if (do_cow) { - TRACE("doing COW write\n"); - - Status = excise_extents(Vcb, fcb, new_start, new_start + new_end, changed_sector_list, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - excise_extents returned %08x\n", Status); - goto end; } - Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, rollback); - - if (!NT_SUCCESS(Status)) { - ERR("error - insert_extent returned %08x\n", Status); - goto end; - } - } else { - UINT64 writeaddr; - - writeaddr = eds->address + eds->offset + new_start - tp.item->key.offset; - TRACE("doing non-COW write to %llx\n", writeaddr); - - Status = write_data(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start); - - if (!NT_SUCCESS(Status)) { - ERR("error - write_data returned %08x\n", Status); - goto end; + if (ed->compression != BTRFS_COMPRESSION_NONE) { + FIXME("FIXME: compression not yet supported\n"); + return STATUS_NOT_SUPPORTED; } - if (changed_sector_list) { - unsigned int i; - changed_sector* sc; + 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); - sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG); - if (!sc) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; + 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; } - sc->ol.key = writeaddr; - sc->length = (new_end - new_start) / Vcb->superblock.sector_size; - sc->deleted = FALSE; + Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback); - sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG); - if (!sc->checksums) { - ERR("out of memory\n"); - ExFreePool(sc); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; + 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; } - 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); - } + 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); + insert_into_ordered_list(changed_sector_list, &sc->ol); + } } - } - - last_write = new_end; - - if (b) { - tp = next_tp; - if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data + length) - b = FALSE; + last_written = new_end; } - } while (b); + + le = le2; + } - if (last_write < start_data + length) { - new_start = last_write; - new_end = start_data + length; - - TRACE("new_start = %llx\n", new_start); - TRACE("new_end = %llx\n", new_end); - - Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, rollback); - + 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("error - insert_extent returned %08x\n", Status); - goto end; + ERR("do_cow_write returned %08x\n", Status); + return Status; } } Status = STATUS_SUCCESS; -end: + 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]; - int i; - LIST_ENTRY* le; - - for (i = 0; i < spaces; i++) { - pref[i] = ' '; - } - pref[spaces] = 0; - - if (!t) { - ERR("%s(not loaded)\n", pref); - return; - } - - le = t->itemlist.Flink; - while (le != &t->itemlist) { - tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); - - ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE"); - - if (t->header.level > 0) { - print_loaded_trees(td->treeholder.tree, spaces+1); - } - - le = le->Flink; - } -} +// #ifdef DEBUG_PARANOID +// static void print_loaded_trees(tree* t, int spaces) { +// char pref[10]; +// int i; +// LIST_ENTRY* le; +// +// for (i = 0; i < spaces; i++) { +// pref[i] = ' '; +// } +// pref[spaces] = 0; +// +// if (!t) { +// ERR("%s(not loaded)\n", pref); +// return; +// } +// +// le = t->itemlist.Flink; +// while (le != &t->itemlist) { +// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); +// +// ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE"); +// +// if (t->header.level > 0) { +// print_loaded_trees(td->treeholder.tree, spaces+1); +// } +// +// le = le->Flink; +// } +// } -static void check_extents_consistent(device_extension* Vcb, fcb* fcb) { - KEY searchkey; - traverse_ptr tp, next_tp; - UINT64 length, oldlength, lastoff, alloc; - NTSTATUS Status; - EXTENT_DATA* ed; - EXTENT_DATA2* ed2; - - if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted) - return; - - TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id); - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_EXTENT_DATA; - searchkey.offset = 0; - - Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); - if (!NT_SUCCESS(Status)) { - ERR("error - find_item returned %08x\n", Status); - goto failure; - } - - if (keycmp(&searchkey, &tp.item->key)) { - ERR("could not find EXTENT_DATA at offset 0\n"); - goto failure; - } - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - goto failure; - } - - ed = (EXTENT_DATA*)tp.item->data; - ed2 = (EXTENT_DATA2*)&ed->data[0]; - - length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - lastoff = tp.item->key.offset; - - TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length); - - alloc = 0; - if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) { - alloc += length; - } - - while (find_next_item(Vcb, &tp, &next_tp, FALSE)) { - if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type) - break; - - tp = next_tp; - - if (tp.item->size < sizeof(EXTENT_DATA)) { - ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); - goto failure; - } - - ed = (EXTENT_DATA*)tp.item->data; - ed2 = (EXTENT_DATA2*)&ed->data[0]; - - length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; - - TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length); - - if (tp.item->key.offset != lastoff + oldlength) { - ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength); - goto failure; - } - - if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) { - alloc += length; - } - - oldlength = length; - lastoff = tp.item->key.offset; - } - - if (alloc != fcb->inode_item.st_blocks) { - ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks); - goto failure; - } - -// if (fcb->inode_item.st_blocks != lastoff + oldlength) { -// ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks); +// static void check_extents_consistent(device_extension* Vcb, fcb* fcb) { +// KEY searchkey; +// traverse_ptr tp, next_tp; +// UINT64 length, oldlength, lastoff, alloc; +// NTSTATUS Status; +// EXTENT_DATA* ed; +// EXTENT_DATA2* ed2; +// +// if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted) +// return; +// +// TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id); +// +// searchkey.obj_id = fcb->inode; +// searchkey.obj_type = TYPE_EXTENT_DATA; +// searchkey.offset = 0; +// +// Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE); +// if (!NT_SUCCESS(Status)) { +// ERR("error - find_item returned %08x\n", Status); // goto failure; // } - - return; - -failure: - if (fcb->subvol->treeholder.tree) - print_loaded_trees(fcb->subvol->treeholder.tree, 0); - - int3; -} +// +// if (keycmp(&searchkey, &tp.item->key)) { +// ERR("could not find EXTENT_DATA at offset 0\n"); +// goto failure; +// } +// +// if (tp.item->size < sizeof(EXTENT_DATA)) { +// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); +// goto failure; +// } +// +// ed = (EXTENT_DATA*)tp.item->data; +// ed2 = (EXTENT_DATA2*)&ed->data[0]; +// +// length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; +// lastoff = tp.item->key.offset; +// +// TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length); +// +// alloc = 0; +// if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) { +// alloc += length; +// } +// +// while (find_next_item(Vcb, &tp, &next_tp, FALSE)) { +// if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type) +// break; +// +// tp = next_tp; +// +// if (tp.item->size < sizeof(EXTENT_DATA)) { +// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); +// goto failure; +// } +// +// ed = (EXTENT_DATA*)tp.item->data; +// ed2 = (EXTENT_DATA2*)&ed->data[0]; +// +// length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; +// +// TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length); +// +// if (tp.item->key.offset != lastoff + oldlength) { +// ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength); +// goto failure; +// } +// +// if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) { +// alloc += length; +// } +// +// oldlength = length; +// lastoff = tp.item->key.offset; +// } +// +// if (alloc != fcb->inode_item.st_blocks) { +// ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks); +// goto failure; +// } +// +// // if (fcb->inode_item.st_blocks != lastoff + oldlength) { +// // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks); +// // goto failure; +// // } +// +// return; +// +// failure: +// if (fcb->subvol->treeholder.tree) +// print_loaded_trees(fcb->subvol->treeholder.tree, 0); +// +// int3; +// } // static void check_extent_tree_consistent(device_extension* Vcb) { // KEY searchkey; @@ -6327,20 +7879,27 @@ failure: // // int3; // } -#endif +// #endif -NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, LIST_ENTRY* rollback) { +static void STDCALL deferred_write_callback(void* context1, void* context2) { + PIRP Irp = context1; + device_extension* Vcb = context2; + + if (!add_thread_job(Vcb, Irp)) + do_write_job(Vcb, Irp); +} + +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) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; - KEY searchkey; - traverse_ptr tp; EXTENT_DATA* ed2; UINT64 newlength, start_data, end_data; UINT32 bufhead; BOOL make_inline; UINT8* data; LIST_ENTRY changed_sector_list; - INODE_ITEM *ii, *origii; + INODE_ITEM* origii; BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE, write_eof = FALSE*/; NTSTATUS Status; LARGE_INTEGER time; @@ -6348,7 +7907,8 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void fcb* fcb; ccb* ccb; file_ref* fileref; - BOOL paging_lock = FALSE; + BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE; + ULONG filter = 0; TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache); @@ -6378,6 +7938,15 @@ 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); + + return STATUS_PENDING; + } + + if (!wait && no_cache) + return STATUS_PENDING; + if (no_cache && !paging_io && FileObject->SectionObjectPointer->DataSectionObject) { IO_STATUS_BLOCK iosb; @@ -6397,14 +7966,33 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } if (paging_io) { - ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, TRUE); - paging_lock = TRUE; + if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, wait)) { + Status = STATUS_PENDING; + goto end; + } else + paging_lock = TRUE; + } + + if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { + if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { + Status = STATUS_PENDING; + goto end; + } else + tree_lock = TRUE; + } + + if (no_cache && !ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) { + if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) { + Status = STATUS_PENDING; + goto end; + } else + fcb_lock = TRUE; } nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM; nocow = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATACOW; - newlength = fcb->ads ? fcb->adssize : fcb->inode_item.st_size; + newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size; if (fcb->deleted) newlength = 0; @@ -6440,14 +8028,23 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void if (changed_length) { if (newlength > fcb->Header.AllocationSize.QuadPart) { - Status = extend_file(fcb, fileref, newlength, FALSE, rollback); + if (!tree_lock) { + // We need to acquire the tree lock if we don't have it already - + // we can't give an inline file proper extents at the same as we're + // doing a flush. + if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { + Status = STATUS_PENDING; + goto end; + } else + tree_lock = TRUE; + } + + Status = extend_file(fcb, fileref, newlength, FALSE, Irp, rollback); if (!NT_SUCCESS(Status)) { ERR("extend_file returned %08x\n", Status); goto end; } - } else if (fcb->ads) - fcb->adssize = newlength; - else + } else if (!fcb->ads) fcb->inode_item.st_size = newlength; fcb->Header.FileSize.QuadPart = newlength; @@ -6459,8 +8056,6 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } if (!no_cache) { - BOOL wait; - if (!FileObject->PrivateCacheMap || changed_length) { CC_FILE_SIZES ccfs; @@ -6473,15 +8068,11 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, FileObject); CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY); - } else { - CcSetFileSizes(FileObject, &ccfs); } + + CcSetFileSizes(FileObject, &ccfs); } - // FIXME - uncomment this when async is working -// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE; - wait = TRUE; - if (IrpSp->MinorFunction & IRP_MN_MDL) { CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus); @@ -6490,9 +8081,6 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } else { TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf); if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) { - TRACE("CcCopyWrite failed.\n"); - - IoMarkIrpPending(Irp); Status = STATUS_PENDING; goto end; } @@ -6504,51 +8092,43 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void } if (fcb->ads) { - UINT16 datalen; - UINT8* data2; - UINT32 maxlen; - - if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) { - ERR("get_xattr failed\n"); - Status = STATUS_INTERNAL_ERROR; - goto end; - } - - if (changed_length) { - // find maximum length of xattr - maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node); - - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_XATTR_ITEM; - searchkey.offset = fcb->adshash; +// UINT32 maxlen; - 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 (changed_length) { + char* data2; - 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; - } - - fcb->adssize = newlength; +// // 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; +// } data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG); if (!data2) { @@ -6557,33 +8137,39 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void goto end; } - RtlCopyMemory(data2, data, datalen); + if (fcb->adsdata.Buffer) { + RtlCopyMemory(data2, fcb->adsdata.Buffer, fcb->adsdata.Length); + ExFreePool(fcb->adsdata.Buffer); + } - if (offset.QuadPart > datalen) - RtlZeroMemory(&data2[datalen], offset.QuadPart - datalen); - } else - data2 = data; - - if (*length > 0) - RtlCopyMemory(&data2[offset.QuadPart], buf, *length); - - Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, newlength, rollback); - if (!NT_SUCCESS(Status)) { - ERR("set_xattr returned %08x\n", Status); - goto end; + if (newlength > fcb->adsdata.Length) + RtlZeroMemory(&data2[fcb->adsdata.Length], newlength - fcb->adsdata.Length); + + + fcb->adsdata.Buffer = data2; + fcb->adsdata.Length = fcb->adsdata.MaximumLength = newlength; + + fcb->Header.AllocationSize.QuadPart = newlength; + fcb->Header.FileSize.QuadPart = newlength; + fcb->Header.ValidDataLength.QuadPart = newlength; } - if (data) ExFreePool(data); - if (data2 != data) ExFreePool(data2); + if (*length > 0) + RtlCopyMemory(&fcb->adsdata.Buffer[offset.QuadPart], buf, *length); fcb->Header.ValidDataLength.QuadPart = newlength; + + mark_fcb_dirty(fcb); + + if (fileref) + mark_fileref_dirty(fileref); } else { if (make_inline) { start_data = 0; end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size); bufhead = sizeof(EXTENT_DATA) - 1; } else { - start_data = offset.QuadPart & ~(fcb->Vcb->superblock.sector_size - 1); + start_data = offset.QuadPart & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1); end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size); bufhead = 0; } @@ -6606,11 +8192,11 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) { if (changed_length) { if (fcb->inode_item.st_size > start_data) - Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL); + Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp); else Status = STATUS_SUCCESS; } else - Status = read_file(Vcb, fcb->subvol, fcb->inode, data + bufhead, start_data, end_data - start_data, NULL); + Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp); if (!NT_SUCCESS(Status)) { ERR("read_file returned %08x\n", Status); @@ -6625,7 +8211,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, nocsum ? NULL : &changed_sector_list, rollback); + Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback); if (!NT_SUCCESS(Status)) { ERR("error - excise_extents returned %08x\n", Status); ExFreePool(data); @@ -6640,12 +8226,17 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void ed2->encoding = BTRFS_ENCODING_NONE; ed2->type = EXTENT_TYPE_INLINE; - insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, NULL, rollback); + if (!add_extent_to_fcb(fcb, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, FALSE, rollback)) { + ERR("add_extent_to_fcb failed\n"); + ExFreePool(data); + Status = STATUS_INTERNAL_ERROR; + goto end; + } 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, rollback); + 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); @@ -6653,7 +8244,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void goto end; } } else { - Status = do_cow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, rollback); + 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); @@ -6664,7 +8255,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void ExFreePool(data); } else { - Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list, rollback); + Status = do_nocow_write(fcb->Vcb, 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); @@ -6712,41 +8303,24 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void origii->st_ctime = now; if (!fcb->ads) { - TRACE("setting st_size to %llx\n", newlength); - origii->st_size = newlength; + 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; } - searchkey.obj_id = fcb->inode; - searchkey.obj_type = TYPE_INODE_ITEM; - searchkey.offset = 0; + mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb); - 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 (!nocsum) { + ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE); + commit_checksum_changes(Vcb, &changed_sector_list); + ExReleaseResourceLite(&Vcb->checksum_lock); } - if (!keycmp(&tp.item->key, &searchkey)) - delete_tree_item(Vcb, &tp, rollback); - else - WARN("couldn't find existing INODE_ITEM\n"); - - ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); - if (!ii) { - ERR("out of memory\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto end; - } - - RtlCopyMemory(ii, origii, sizeof(INODE_ITEM)); - insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback); - - // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files) - - if (!nocsum) - update_checksum_tree(Vcb, &changed_sector_list, rollback); - if (changed_length) { CC_FILE_SIZES ccfs; @@ -6769,39 +8343,41 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void Status = STATUS_SUCCESS; + if (filter != 0) + send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, FILE_ACTION_MODIFIED); + end: - if (FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) { + if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) { TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart); FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + (NT_SUCCESS(Status) ? *length : 0); TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart); } + if (fcb_lock) + ExReleaseResourceLite(fcb->Header.Resource); + + if (tree_lock) + ExReleaseResourceLite(&Vcb->tree_lock); + if (paging_lock) ExReleaseResourceLite(fcb->Header.PagingIoResource); return Status; } -NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) { +NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); - device_extension* Vcb = DeviceObject->DeviceExtension; void* buf; NTSTATUS Status; LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset; PFILE_OBJECT FileObject = IrpSp->FileObject; fcb* fcb = FileObject ? FileObject->FsContext : NULL; - BOOL locked = FALSE; +// BOOL locked = FALSE; // LARGE_INTEGER freq, time1, time2; LIST_ENTRY rollback; InitializeListHead(&rollback); - if (Vcb->readonly) - return STATUS_MEDIA_WRITE_PROTECTED; - - if (fcb && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) - return STATUS_ACCESS_DENIED; - // time1 = KeQueryPerformanceCounter(&freq); TRACE("write\n"); @@ -6824,10 +8400,13 @@ NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) { TRACE("buf = %p\n", buf); - if (Irp->Flags & IRP_NOCACHE) { - acquire_tree_lock(Vcb, TRUE); - locked = TRUE; - } +// if (Irp->Flags & IRP_NOCACHE) { +// if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { +// Status = STATUS_PENDING; +// goto exit; +// } +// locked = TRUE; +// } if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) { WARN("tried to write to locked region\n"); @@ -6836,36 +8415,39 @@ NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } // ERR("Irp->Flags = %x\n", Irp->Flags); - Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback); - if (!NT_SUCCESS(Status)) { - if (Status != STATUS_PENDING) - ERR("write_file2 returned %08x\n", Status); + Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, + wait, deferred_write, &rollback); + + if (Status == STATUS_PENDING) + goto exit; + else if (!NT_SUCCESS(Status)) { + ERR("write_file2 returned %08x\n", Status); goto exit; } - if (locked) - Status = consider_write(Vcb); +// if (locked) +// Status = consider_write(Vcb); if (NT_SUCCESS(Status)) { Irp->IoStatus.Information = IrpSp->Parameters.Write.Length; #ifdef DEBUG_PARANOID - if (locked) - check_extents_consistent(Vcb, FileObject->FsContext); // TESTING +// if (locked) +// check_extents_consistent(Vcb, FileObject->FsContext); // TESTING // check_extent_tree_consistent(Vcb); #endif } exit: - if (locked) { +// if (locked) { if (NT_SUCCESS(Status)) clear_rollback(&rollback); else do_rollback(Vcb, &rollback); - - release_tree_lock(Vcb, TRUE); - } +// +// ExReleaseResourceLite(&Vcb->tree_lock); +// } // time2 = KeQueryPerformanceCounter(NULL); @@ -6873,3 +8455,89 @@ exit: return Status; } + +NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + NTSTATUS Status; + BOOL top_level; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + device_extension* Vcb = DeviceObject->DeviceExtension; + PFILE_OBJECT FileObject = IrpSp->FileObject; + fcb* fcb = FileObject ? FileObject->FsContext : NULL; + ccb* ccb = FileObject ? FileObject->FsContext2 : NULL; + + FsRtlEnterFileSystem(); + + top_level = is_top_level(Irp); + + if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) { + Status = part0_passthrough(DeviceObject, Irp); + goto exit; + } + + if (!fcb) { + ERR("fcb was NULL\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (!ccb) { + ERR("ccb was NULL\n"); + Status = STATUS_INVALID_PARAMETER; + goto end; + } + + if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) { + Status = STATUS_ACCESS_DENIED; + goto end; + } + + if (Vcb->readonly) { + Status = STATUS_MEDIA_WRITE_PROTECTED; + goto end; + } + + if (!(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { + WARN("insufficient permissions\n"); + Status = STATUS_ACCESS_DENIED; + goto end; + } + +// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE"); + + _SEH2_TRY { + if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { + CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress); + + Irp->MdlAddress = NULL; + Status = STATUS_SUCCESS; + } else { + Status = write_file(Vcb, Irp, IoIsOperationSynchronous(Irp), FALSE); + } + } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + Status = _SEH2_GetExceptionCode(); + } _SEH2_END; + +end: + Irp->IoStatus.Status = Status; + + TRACE("wrote %u bytes\n", Irp->IoStatus.Information); + + if (Status != STATUS_PENDING) + IoCompleteRequest(Irp, IO_NO_INCREMENT); + else { + IoMarkIrpPending(Irp); + + if (!add_thread_job(Vcb, Irp)) + do_write_job(Vcb, Irp); + } + +exit: + if (top_level) + IoSetTopLevelIrp(NULL); + + FsRtlExitFileSystem(); + + TRACE("returning %08x\n", Status); + + return Status; +}