Sync btrfs to 0.6.
CORE-11937
svn path=/trunk/; revision=72576
+add_subdirectory(zlib)
include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers
+ ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
inc)
list(APPEND SOURCE
btrfs.c
cache.c
+ compress.c
crc32c.c
create.c
dirctrl.c
add_definitions(-D__KERNEL__)
set_module_type(btrfs kernelmodedriver)
-target_link_libraries(btrfs ntoskrnl_vista ${PSEH_LIB})
+target_link_libraries(btrfs ntoskrnl_vista zlib_solo ${PSEH_LIB})
add_importlibs(btrfs ntoskrnl hal)
add_pch(btrfs btrfs_drv.h SOURCE)
add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)
#endif
#include <mountdev.h>
-#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
- BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
+ BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \
+ BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
#define COMPAT_RO_SUPPORTED 0
static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
LIST_ENTRY VcbList;
ERESOURCE global_loading_lock;
UINT32 debug_log_level = 0;
+UINT32 mount_compress = 0;
+UINT32 mount_compress_force = 0;
+UINT32 mount_compress_type = 0;
+UINT32 mount_zlib_level = 3;
+UINT32 mount_flush_interval = 30;
+UINT32 mount_max_inline = 2048;
BOOL log_started = FALSE;
UNICODE_STRING log_device, log_file, registry_path;
ExFreePool(registry_path.Buffer);
}
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, prev_tp;
NTSTATUS Status;
searchkey.obj_type = 0xff;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
}
- while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+ while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) {
tp = prev_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
return TRUE;
}
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
DIR_ITEM* xa;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
return FALSE;
}
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
UINT8* di2;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, Irp, rollback);
ExFreePool(di);
} else {
- insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, Irp, rollback);
}
return STATUS_SUCCESS;
break;
case FileFsDeviceInformation:
- FIXME("STUB: FileFsDeviceInformation\n");
+ {
+ FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileFsDeviceInformation\n");
+
+ ffdi->DeviceType = FILE_DEVICE_DISK;
+ ffdi->Characteristics = Vcb->devices[0].devobj->Characteristics;
+
+ if (Vcb->readonly)
+ ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
+ else
+ ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
+
+ BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
+ Status = STATUS_SUCCESS;
+
break;
+ }
case FileFsDriverPathInformation:
FIXME("STUB: FileFsDriverPathInformation\n");
// }
// }
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) {
root* r;
tree* t;
ROOT_ITEM* ri;
// We ask here for a traverse_ptr to the item we're inserting, so we can
// copy some of the tree's variables
- if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ri);
static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
ULONG utf8len;
NTSTATUS Status;
+ USHORT vollen, i;
TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
- Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
- if (!NT_SUCCESS(Status))
- goto end;
+ vollen = ffli->VolumeLabelLength;
- if (utf8len > MAX_LABEL_SIZE) {
- Status = STATUS_INVALID_VOLUME_LABEL;
- goto end;
+ for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
+ if (ffli->VolumeLabel[i] == 0) {
+ vollen = i * sizeof(WCHAR);
+ break;
+ } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
+ }
}
- // FIXME - check for '/' and '\\' and reject
-
-// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
-
- Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
- if (!NT_SUCCESS(Status))
- goto release;
+ if (vollen == 0) {
+ utf8len = 0;
+ } else {
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (utf8len > MAX_LABEL_SIZE) {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
+ }
+ }
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
- if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
- RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
+ if (utf8len > 0) {
+ Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
+ if (!NT_SUCCESS(Status))
+ goto release;
+ } else
+ Status = STATUS_SUCCESS;
+
+ if (utf8len < MAX_LABEL_SIZE)
+ RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
// test_tree_deletion(Vcb); // TESTING
// test_tree_splitting(Vcb);
return Status;
}
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
- insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
+ insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, Irp, rollback);
}
break;
return STATUS_SUCCESS;
}
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
BOOL changed = FALSE;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = parinode;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
- insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, Irp, rollback);
}
break;
searchkey.obj_type = TYPE_INODE_EXTREF;
searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
- insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
+ insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, Irp, rollback);
}
break;
hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
file_ref* parfr;
- Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+ Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
if (!NT_SUCCESS(Status)) {
ERR("open_fileref_by_inode returned %08x\n", Status);
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
LONG rc;
+// #ifdef DEBUG
+// if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) {
+// ERR("fcb_lock not acquired exclusively\n");
+// int3;
+// }
+// #endif
+
rc = InterlockedDecrement(&fcb->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
if (rc > 0)
return;
- ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+// ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
if (fcb->list_entry.Flink)
RemoveEntryList(&fcb->list_entry);
if (fcb->list_entry_all.Flink)
RemoveEntryList(&fcb->list_entry_all);
- ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+// ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
ExDeleteResourceLite(&fcb->nonpaged->resource);
ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
LONG rc;
+// #ifdef DEBUG
+// if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) {
+// ERR("fcb_lock not acquired exclusively\n");
+// int3;
+// }
+// #endif
+
rc = InterlockedDecrement(&fr->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
CcUninitializeCacheMap(FileObject, NULL, NULL);
+ if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
+ return STATUS_SUCCESS;
+
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
if (fileref)
LIST_ENTRY* le;
LARGE_INTEGER time;
-#ifndef __REACTOS__
+ Vcb->removing = TRUE;
+
RemoveEntryList(&Vcb->list_entry);
-#endif
Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
if (!NT_SUCCESS(Status))
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
- if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ if (Vcb->need_write && !Vcb->readonly)
+ do_write(Vcb, NULL, &rollback);
free_trees(Vcb);
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);
if (c->cache)
free_fcb(c->cache);
- ExDeleteResourceLite(&c->nonpaged->lock);
- ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
+ ExDeleteResourceLite(&c->lock);
+ ExDeleteResourceLite(&c->changed_extents_lock);
- ExFreePool(c->nonpaged);
ExFreePool(c->chunk_item);
ExFreePool(c);
}
ZwClose(Vcb->flush_thread_handle);
}
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback) {
LARGE_INTEGER newlength, time;
BTRFS_TIME now;
NTSTATUS Status;
// excise extents
if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
- Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
+ Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("excise_extents returned %08x\n", Status);
ExReleaseResourceLite(fileref->fcb->Header.Resource);
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
- Status = delete_fileref(fileref, FileObject, &rollback);
+ Status = delete_fileref(fileref, FileObject, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_fileref returned %08x\n", Status);
do_rollback(Vcb, &rollback);
return Status;
}
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp) {
ULONG att;
char* eaval;
UINT16 ealen;
// ii can be NULL
- if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
+ if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
if (ealen > 2) {
if (eaval[0] == '0' && eaval[1] == 'x') {
int i;
return att;
}
-NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
+static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) {
IO_STATUS_BLOCK* IoStatus;
LARGE_INTEGER Offset;
PIRP Irp;
goto exit;
}
+ Irp->Flags |= IRP_NOCACHE;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_READ;
+ if (override)
+ IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
if (DeviceObject->Flags & DO_BUFFERED_IO) {
FIXME("FIXME - buffered IO\n");
} else if (DeviceObject->Flags & DO_DIRECT_IO) {
IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
-// if (Override)
-// {
-// Stack = IoGetNextIrpStackLocation(Irp);
-// Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
-// }
-
-// TRACE("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver(DeviceObject, Irp);
-// TRACE("Waiting for IO Operation for %p\n", Irp);
if (Status == STATUS_PENDING) {
-// TRACE("Operation pending\n");
KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-// TRACE("Getting IO Status... for %p\n", Irp);
Status = context->iosb.Status;
}
if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
break;
- Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+ Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("Failed to read superblock %u: %08x\n", i, Status);
ExFreePool(sb);
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
+static NTSTATUS STDCALL look_for_roots(device_extension* Vcb, PIRP Irp) {
traverse_ptr tp, next_tp;
KEY searchkey;
BOOL b;
searchkey.obj_type = 0;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_tree returned %08x\n", Status);
return Status;
}
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b)
tp = next_tp;
return STATUS_SUCCESS;
}
-static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
+static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
searchkey.obj_type = TYPE_DEV_EXTENT;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_tree returned %08x\n", Status);
return Status;
}
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
}
}
-static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
+static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) {
traverse_ptr tp, next_tp;
KEY searchkey;
BOOL b;
Vcb->data_flags = 0;
- Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (tp.item->size < sizeof(CHUNK_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
} else {
- c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+ c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
if (!c) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
- c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
-
- if (!c->nonpaged) {
- ERR("out of memory\n");
- ExFreePool(c);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
c->size = tp.item->size;
c->offset = tp.item->key.offset;
c->used = c->oldused = 0;
c->cache = NULL;
c->created = FALSE;
- c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
if (!c->chunk_item) {
ERR("out of memory\n");
ExFreePool(c);
- ExFreePool(c->nonpaged);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (c->chunk_item->num_stripes > 0) {
CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
- c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+ c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
if (!c->devices) {
ERR("out of memory\n");
ExFreePool(c);
- ExFreePool(c->nonpaged);
ExFreePool(c->chunk_item);
return STATUS_INSUFFICIENT_RESOURCES;
}
} else
c->devices = NULL;
- ExInitializeResourceLite(&c->nonpaged->lock);
- ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+ ExInitializeResourceLite(&c->lock);
+ ExInitializeResourceLite(&c->changed_extents_lock);
InitializeListHead(&c->space);
InitializeListHead(&c->space_size);
}
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b)
tp = next_tp;
}
}
-static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
+static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb, PIRP Irp) {
LIST_ENTRY* le = Vcb->chunks.Flink;
chunk* c;
KEY searchkey;
searchkey.obj_id = c->offset;
searchkey.offset = c->chunk_item->size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
// FIXME - make sure we free occasionally after doing one of these, or we
// might use up a lot of memory with a big disk.
- Status = load_free_space_cache(Vcb, c);
+ Status = load_free_space_cache(Vcb, c, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_free_space_cache returned %08x\n", Status);
return Status;
return STATUS_SUCCESS;
}
-static root* find_default_subvol(device_extension* Vcb) {
+static root* find_default_subvol(device_extension* Vcb, PIRP Irp) {
LIST_ENTRY* le;
static char fn[] = "default";
static UINT32 crc32 = 0x8dbfc2d2;
+ if (Vcb->options.subvol_id != 0) {
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == Vcb->options.subvol_id)
+ return r;
+
+ le = le->Flink;
+ }
+ }
+
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
NTSTATUS Status;
KEY searchkey;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
ULONG i;
NTSTATUS Status;
- Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
+ Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL));
Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
if (!Vcb->threads.threads) {
}
}
+ Vcb->threads.pending_jobs = 0;
+
return STATUS_SUCCESS;
}
threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
+ if (Vcb->threads.pending_jobs >= Vcb->threads.num_threads)
+ return FALSE;
+
if (Vcb->threads.threads[threadnum].quit)
return FALSE;
tj->Irp = Irp;
+ InterlockedIncrement(&Vcb->threads.pending_jobs);
+
ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
TRACE("btrfs magic found\n");
}
- Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
+ Status = registry_load_volume_options(Vcb);
if (!NT_SUCCESS(Status)) {
ERR("registry_load_volume_options returned %08x\n", Status);
goto exit;
Vcb->readonly = TRUE;
}
+ if (Vcb->options.readonly)
+ Vcb->readonly = TRUE;
+
Vcb->superblock.generation++;
Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
- Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
+ Vcb->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
if (!Vcb->devices) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
Vcb->log_to_phys_loaded = FALSE;
- Vcb->max_inline = Vcb->superblock.node_size / 2;
-
add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
if (!Vcb->chunk_root) {
FsRtlNotifyInitializeSync(&Vcb->NotifySync);
- Status = load_chunk_root(Vcb);
+ Status = load_chunk_root(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_chunk_root returned %08x\n", Status);
goto exit;
goto exit;
}
- Status = look_for_roots(Vcb);
+ Status = look_for_roots(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("look_for_roots returned %08x\n", Status);
goto exit;
}
- Status = find_chunk_usage(Vcb);
- if (!NT_SUCCESS(Status)) {
- ERR("find_chunk_usage returned %08x\n", Status);
- goto exit;
+ if (!Vcb->readonly) {
+ Status = find_chunk_usage(Vcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_chunk_usage returned %08x\n", Status);
+ goto exit;
+ }
}
// We've already increased the generation by one
if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
- Status = clear_free_space_cache(Vcb);
+ Status = clear_free_space_cache(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("clear_free_space_cache returned %08x\n", Status);
goto exit;
}
}
- Vcb->volume_fcb = create_fcb();
+ Vcb->volume_fcb = create_fcb(NonPagedPool);
if (!Vcb->volume_fcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
Vcb->volume_fcb->Vcb = Vcb;
Vcb->volume_fcb->sd = NULL;
- root_fcb = create_fcb();
+ root_fcb = create_fcb(NonPagedPool);
if (!root_fcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
WARN("root FCB = %p\n", root_fcb);
#endif
- root_fcb->subvol = find_default_subvol(Vcb);
+ root_fcb->subvol = find_default_subvol(Vcb, Irp);
if (!root_fcb->subvol) {
ERR("could not find top subvol\n");
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
if (tp.item->size > 0)
RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
- fcb_get_sd(root_fcb, NULL);
+ fcb_get_sd(root_fcb, NULL, Irp);
- root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
+ root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
Vcb->root_fileref = create_fileref();
if (!Vcb->root_fileref) {
Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
Vcb->root_file->FsContext = root_fcb;
-#ifdef __REACTOS__
Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
Vcb->root_file->Vpb = DeviceObject->Vpb;
-#endif
RtlZeroMemory(root_ccb, sizeof(ccb));
root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
root_ccb->NodeSize = sizeof(ccb);
-#ifndef __REACTOS__
- Vcb->root_file->FsContext = root_ccb;
-#else
Vcb->root_file->FsContext2 = root_ccb;
_SEH2_TRY {
Status = _SEH2_GetExceptionCode();
goto exit;
} _SEH2_END;
-#endif
for (i = 0; i < Vcb->superblock.num_devices; i++) {
- Status = find_disk_holes(Vcb, &Vcb->devices[i]);
+ Status = find_disk_holes(Vcb, &Vcb->devices[i], Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_disk_holes returned %08x\n", Status);
goto exit;
return Status;
}
+static NTSTATUS verify_volume(PDEVICE_OBJECT device) {
+ device_extension* Vcb = device->DeviceExtension;
+ ULONG cc, to_read;
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS Status;
+ superblock* sb;
+ UINT32 crc32;
+ UINT64 i;
+
+ if (Vcb->removing)
+ return STATUS_WRONG_VOLUME;
+
+ Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return Status;
+ }
+
+ to_read = sector_align(sizeof(superblock), device->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+ if (!sb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = sync_read_phys(Vcb->Vpb->RealDevice, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("Failed to read superblock: %08x\n", Status);
+ ExFreePool(sb);
+ return Status;
+ }
+
+ if (sb->magic != BTRFS_MAGIC) {
+ ERR("not a BTRFS volume\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
+ }
+
+ if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
+ ERR("different UUIDs\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
+ }
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+ TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+
+ if (crc32 != *((UINT32*)sb->checksum)) {
+ ERR("different UUIDs\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
+ }
+
+ ExFreePool(sb);
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ if (Vcb->devices[i].removable) {
+ NTSTATUS Status;
+ ULONG cc;
+ IO_STATUS_BLOCK iosb;
+
+ Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return Status;
+ }
+
+ if (iosb.Information < sizeof(ULONG)) {
+ ERR("iosb.Information was too short\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ Vcb->devices[i].change_count = cc;
+ }
+
+ Vcb->devices[i].devobj->Flags &= ~DO_VERIFY_VOLUME;
+ }
+
+ Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
+ return STATUS_SUCCESS;
+}
+
static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
break;
case IRP_MN_VERIFY_VOLUME:
- FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
+ TRACE("IRP_MN_VERIFY_VOLUME\n");
+
+ Status = verify_volume(DeviceObject);
+
+ if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
+ uninit(Vcb, FALSE);
+// Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+ }
+
break;
default:
Status = STATUS_SUCCESS;
while (!IsListEmpty(&VcbList)) {
- LIST_ENTRY* le = RemoveHeadList(&VcbList);
- Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+ Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
TRACE("shutting down Vcb %p\n", Vcb);
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
VALUE "OriginalFilename", "btrfs.sys"
VALUE "ProductName", "WinBtrfs"
- VALUE "ProductVersion", "0.5"
+ VALUE "ProductVersion", "0.6"
END
END
BLOCK "VarFileInfo"
#define BTRFS_NODE_TYPE_FCB 0x2296
#define ALLOC_TAG 0x7442484D //'MHBt'
+#define ALLOC_TAG_ZLIB 0x7A42484D //'MHBz'
#define STDCALL __stdcall
#define EA_REPARSE "system.reparse"
#define EA_REPARSE_HASH 0x786f6167
-#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
-
#define MAX_EXTENT_SIZE 0x8000000 // 128 MB
+#define COMPRESSED_EXTENT_SIZE 0x20000 // 128 KB
+
+#define READ_AHEAD_GRANULARITY COMPRESSED_EXTENT_SIZE // really ought to be a multiple of COMPRESSED_EXTENT_SIZE
#ifdef _MSC_VER
#define try __try
// #pragma pack(push, 1)
-struct device_extension;
+struct _device_extension;
typedef struct {
BTRFS_UUID fsuuid;
BOOL ads;
UINT32 adshash;
+ ULONG adsmaxlen;
ANSI_STRING adsxattr;
ANSI_STRING adsdata;
LIST_ENTRY space;
} device;
-typedef struct {
- ERESOURCE lock;
- ERESOURCE changed_extents_lock;
-} chunk_nonpaged;
-
typedef struct {
CHUNK_ITEM* chunk_item;
UINT32 size;
LIST_ENTRY space_size;
LIST_ENTRY deleting;
LIST_ENTRY changed_extents;
- chunk_nonpaged* nonpaged;
+ ERESOURCE lock;
+ ERESOURCE changed_extents_lock;
BOOL created;
LIST_ENTRY list_entry;
ULONG num_threads;
LONG next_thread;
drv_thread* threads;
+ LONG pending_jobs;
} drv_threads;
typedef struct {
BOOL ignore;
+ BOOL compress;
+ BOOL compress_force;
+ UINT8 compress_type;
+ BOOL readonly;
+ UINT32 zlib_level;
+ UINT32 flush_interval;
+ UINT32 max_inline;
+ UINT64 subvol_id;
} mount_options;
#define VCB_TYPE_VOLUME 1
root* dev_root;
root* uuid_root;
BOOL log_to_phys_loaded;
- UINT32 max_inline;
LIST_ENTRY sys_chunks;
LIST_ENTRY chunks;
LIST_ENTRY chunks_changed;
WriteDataStatus_Ignore
};
-struct write_data_context;
+struct _write_data_context;
typedef struct {
- struct write_data_context* context;
+ struct _write_data_context* context;
UINT8* buf;
BOOL need_free;
device* device;
LIST_ENTRY list_entry;
} write_data_stripe;
-typedef struct {
+typedef struct _write_data_context {
KEVENT Event;
LIST_ENTRY stripes;
LONG stripes_left;
startoff = off % (num_stripes * stripe_length);
initoff = (off / (num_stripes * stripe_length)) * stripe_length;
- *stripe = startoff / stripe_length;
+ *stripe = (UINT16)(startoff / stripe_length);
*stripeoff = initoff + startoff - (*stripe * stripe_length);
}
device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
int keycmp(const KEY* key1, const KEY* key2);
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp);
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp);
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line);
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback);
-fcb* create_fcb();
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp);
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+fcb* create_fcb(POOL_TYPE pool_type);
file_ref* create_fileref();
void protect_superblocks(device_extension* Vcb, chunk* c);
BOOL is_top_level(PIRP Irp);
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback);
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback);
void STDCALL uninit(device_extension* Vcb, BOOL flush);
NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb);
NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
void mark_fcb_dirty(fcb* fcb);
void mark_fileref_dirty(file_ref* fileref);
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback);
#ifdef _MSC_VER
#define funcname __FUNCTION__
#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
#define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
+extern UINT32 mount_compress;
+extern UINT32 mount_compress_force;
+extern UINT32 mount_compress_type;
+extern UINT32 mount_zlib_level;
+extern UINT32 mount_flush_interval;
+extern UINT32 mount_max_inline;
+
#ifdef _DEBUG
extern BOOL log_started;
// in crc32c.c
UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
+typedef struct {
+ LIST_ENTRY* list;
+ LIST_ENTRY* list_size;
+ UINT64 address;
+ UINT64 length;
+ chunk* chunk;
+} rollback_space;
+
+typedef struct {
+ fcb* fcb;
+ extent* ext;
+} rollback_extent;
+
enum rollback_type {
ROLLBACK_INSERT_ITEM,
ROLLBACK_DELETE_ITEM,
ROLLBACK_INSERT_EXTENT,
- ROLLBACK_DELETE_EXTENT
+ ROLLBACK_DELETE_EXTENT,
+ ROLLBACK_ADD_SPACE,
+ ROLLBACK_SUBTRACT_SPACE
};
// in treefuncs.c
-NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
void STDCALL free_trees(device_extension* Vcb);
-BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, PIRP Irp, LIST_ENTRY* rollback);
void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback);
tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp,
+ const char* func, const char* file, unsigned int line);
void clear_rollback(LIST_ENTRY* rollback);
void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
void free_trees_root(device_extension* Vcb, root* r);
void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
-#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
-#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
-#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
+#define find_item(Vcb, r, tp, searchkey, ignore, Irp) _find_item(Vcb, r, tp, searchkey, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_next_item(Vcb, tp, next_tp, ignore, Irp) _find_next_item(Vcb, tp, next_tp, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_prev_item(Vcb, tp, prev_tp, ignore, Irp) _find_prev_item(Vcb, tp, prev_tp, ignore, Irp, funcname, __FILE__, __LINE__)
#define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
-#define load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__)
-#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)
+#define load_tree(t, addr, r, pt, parent, Irp) _load_tree(t, addr, r, pt, parent, Irp, funcname, __FILE__, __LINE__)
+#define do_load_tree(Vcb, th, r, t, td, loaded, Irp) _do_load_tree(Vcb, th, r, t, td, loaded, Irp, funcname, __FILE__, __LINE__)
// in search.c
void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes);
extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
// in write.c
-NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write);
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback);
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback);
void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
-NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
-chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp);
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp);
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c);
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c);
void free_write_data_stripes(write_data_context* wtc);
-NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback);
+void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback);
BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list,
- PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+ PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size);
NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
-void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback);
NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
- signed long long count, BOOL no_csum, UINT64 new_size);
+ signed long long count, BOOL no_csum, UINT64 new_size, PIRP Irp);
+NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
// in dirctrl.c
NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp);
// in security.c
NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void fcb_get_sd(fcb* fcb, struct _fcb* parent);
+void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp);
// UINT32 STDCALL get_uid();
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
UINT32 sid_to_uid(PSID sid);
+void uid_to_sid(UINT32 uid, PSID* sid);
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as);
// in fileinfo.c
NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
BOOL has_open_children(file_ref* fileref);
NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback);
NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset);
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr);
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
// in reparse.c
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
// in create.c
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol,
- UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
- root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb);
-NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb);
+ root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp);
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp);
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp);
+NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp);
void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock);
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index);
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp);
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp);
// in fsctl.c
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// in free-space.c
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c);
-NTSTATUS clear_free_space_cache(device_extension* Vcb);
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback);
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp);
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp);
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size);
void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
#define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname)
+#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, NULL, rollback, funcname)
#define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname)
+#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, NULL, rollback, funcname)
// in extent-tree.c
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback);
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback);
void decrease_chunk_usage(chunk* c, UINT64 delta);
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset);
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback);
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp);
// in worker-thread.c
void STDCALL worker_thread(void* context);
void STDCALL read_registry(PUNICODE_STRING regpath);
NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid);
NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid);
-NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options);
+NTSTATUS registry_load_volume_options(device_extension* Vcb);
+
+// in compress.c
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen);
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
#define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
head->Blink = item;
}
+static __inline BOOL write_fcb_compressed(fcb* fcb) {
+ // make sure we don't accidentally write the cache inodes or pagefile compressed
+ if (fcb->subvol->id == BTRFS_ROOT_ROOT || fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
+ return FALSE;
+
+ if (fcb->Vcb->options.compress_force)
+ return TRUE;
+
+ if (fcb->inode_item.flags & BTRFS_INODE_NOCOMPRESS)
+ return FALSE;
+
+ if (fcb->inode_item.flags & BTRFS_INODE_COMPRESS || fcb->Vcb->options.compress)
+ return TRUE;
+
+ return FALSE;
+}
+
#ifdef DEBUG_FCB_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
#define increase_fileref_refcount(fileref) {\
+// No copyright claimed in this file - do what you want with it.
+
#ifndef BTRFSIOCTL_H_DEFINED
#define BTRFSIOCTL_H_DEFINED
#define FSCTL_BTRFS_GET_FILE_IDS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define FSCTL_BTRFS_CREATE_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define FSCTL_BTRFS_CREATE_SNAPSHOT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82b, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_GET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82c, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_SET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82d, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
typedef struct {
UINT64 subvol;
WCHAR name[1];
} btrfs_create_snapshot;
+typedef struct {
+ UINT64 subvol;
+ UINT64 inode;
+ BOOL top;
+ UINT8 type;
+ UINT32 st_uid;
+ UINT32 st_gid;
+ UINT32 st_mode;
+ UINT64 st_rdev;
+ UINT64 flags;
+ UINT32 inline_length;
+ UINT64 disk_size[3];
+} btrfs_inode_info;
+
+typedef struct {
+ UINT64 flags;
+ BOOL flags_changed;
+ UINT32 st_uid;
+ BOOL uid_changed;
+ UINT32 st_gid;
+ BOOL gid_changed;
+ UINT32 st_mode;
+ BOOL mode_changed;
+} btrfs_set_inode_info;
+
#endif
--- /dev/null
+/* Copyright (c) Mark Harmstone 2016
+ * Copyright (c) Reimar Doeffinger 2006
+ * Copyright (c) Markus Oberhumer 1996
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
+// Portion of the LZO decompression code here were cribbed from code in
+// libavcodec, also under the LGPL. Thank you, Reimar Doeffinger.
+
+// The LZO compression code comes from v0.22 of lzo, written way back in
+// 1996, and available here:
+// https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/libs/lzo-0.22.tar.gz
+// Modern versions of lzo are licensed under the GPL, but the very oldest
+// versions are under the LGPL and hence okay to use here.
+
+#include "btrfs_drv.h"
+
+#define Z_SOLO
+#define ZLIB_INTERNAL
+
+#ifndef __REACTOS__
+#include "zlib/zlib.h"
+#include "zlib/inftrees.h"
+#include "zlib/inflate.h"
+#else
+#include <zlib.h>
+#endif
+
+#define LINUX_PAGE_SIZE 4096
+
+typedef struct {
+ UINT8* in;
+ UINT32 inlen;
+ UINT32 inpos;
+ UINT8* out;
+ UINT32 outlen;
+ UINT32 outpos;
+ BOOL error;
+ void* wrkmem;
+} lzo_stream;
+
+#define LZO1X_MEM_COMPRESS ((UINT32) (16384L * sizeof(UINT8*)))
+
+#define M1_MAX_OFFSET 0x0400
+#define M2_MAX_OFFSET 0x0800
+#define M3_MAX_OFFSET 0x4000
+#define M4_MAX_OFFSET 0xbfff
+
+#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MARKER 0
+#define M2_MARKER 64
+#define M3_MARKER 32
+#define M4_MARKER 16
+
+#define _DV2(p, shift1, shift2) (((( (UINT32)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0])
+#define DVAL_NEXT(dv, p) dv ^= p[-1]; dv = (((dv) >> 5) ^ ((UINT32)(p[2]) << (2*5)))
+#define _DV(p, shift) _DV2(p, shift, shift)
+#define DVAL_FIRST(dv, p) dv = _DV((p), 5)
+#define _DINDEX(dv, p) ((40799u * (dv)) >> 5)
+#define DINDEX(dv, p) (((_DINDEX(dv, p)) & 0x3fff) << 0)
+#define UPDATE_D(dict, cycle, dv, p) dict[DINDEX(dv, p)] = (p)
+#define UPDATE_I(dict, cycle, index, p) dict[index] = (p)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, max_offset) \
+ ((void*) m_pos < (void*) in || \
+ (m_off = (UINT8*) ip - (UINT8*) m_pos) <= 0 || \
+ m_off > max_offset)
+
+#define LZO_BYTE(x) ((unsigned char) (x))
+
+static UINT8 lzo_nextbyte(lzo_stream* stream) {
+ UINT8 c;
+
+ if (stream->inpos >= stream->inlen) {
+ stream->error = TRUE;
+ return 0;
+ }
+
+ c = stream->in[stream->inpos];
+ stream->inpos++;
+
+ return c;
+}
+
+static int lzo_len(lzo_stream* stream, int byte, int mask) {
+ int len = byte & mask;
+
+ if (len == 0) {
+ while (!(byte = lzo_nextbyte(stream))) {
+ if (stream->error) return 0;
+
+ len += 255;
+ }
+
+ len += mask + byte;
+ }
+
+ return len;
+}
+
+static void lzo_copy(lzo_stream* stream, int len) {
+ if (stream->inpos + len > stream->inlen) {
+ stream->error = TRUE;
+ return;
+ }
+
+ if (stream->outpos + len > stream->outlen) {
+ stream->error = TRUE;
+ return;
+ }
+
+ do {
+ stream->out[stream->outpos] = stream->in[stream->inpos];
+ stream->inpos++;
+ stream->outpos++;
+ len--;
+ } while (len > 0);
+}
+
+static void lzo_copyback(lzo_stream* stream, int back, int len) {
+ if (stream->outpos < back) {
+ stream->error = TRUE;
+ return;
+ }
+
+ if (stream->outpos + len > stream->outlen) {
+ stream->error = TRUE;
+ return;
+ }
+
+ do {
+ stream->out[stream->outpos] = stream->out[stream->outpos - back];
+ stream->outpos++;
+ len--;
+ } while (len > 0);
+}
+
+static NTSTATUS do_lzo_decompress(lzo_stream* stream) {
+ UINT8 byte;
+ UINT32 len, back;
+ BOOL backcopy = FALSE;
+
+ stream->error = FALSE;
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ if (byte > 17) {
+ lzo_copy(stream, byte - 17);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ if (byte < 16) return STATUS_INTERNAL_ERROR;
+ }
+
+ while (1) {
+ if (byte >> 4) {
+ backcopy = TRUE;
+ if (byte >> 6) {
+ len = (byte >> 5) - 1;
+ back = (lzo_nextbyte(stream) << 3) + ((byte >> 2) & 7) + 1;
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+ } else if (byte >> 5) {
+ len = lzo_len(stream, byte, 31);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ back = (lzo_nextbyte(stream) << 6) + (byte >> 2) + 1;
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+ } else {
+ len = lzo_len(stream, byte, 7);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ back = (1 << 14) + ((byte & 8) << 11);
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ back += (lzo_nextbyte(stream) << 6) + (byte >> 2);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ if (back == (1 << 14)) {
+ if (len != 1)
+ return STATUS_INTERNAL_ERROR;
+ break;
+ }
+ }
+ } else if (backcopy) {
+ len = 0;
+ back = (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+ } else {
+ len = lzo_len(stream, byte, 15);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ lzo_copy(stream, len + 3);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ if (byte >> 4)
+ continue;
+
+ len = 1;
+ back = (1 << 11) + (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ break;
+ }
+
+ lzo_copyback(stream, back, len + 2);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+
+ len = byte & 3;
+
+ if (len) {
+ lzo_copy(stream, len);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+ } else
+ backcopy = !backcopy;
+
+ byte = lzo_nextbyte(stream);
+ if (stream->error) return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+ NTSTATUS Status;
+ UINT32 extlen, partlen, inoff, outoff;
+ lzo_stream stream;
+
+ extlen = *((UINT32*)inbuf);
+ if (inlen < extlen) {
+ ERR("compressed extent was %llx, should have been at least %x\n", inlen, extlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ inoff = sizeof(UINT32);
+ outoff = 0;
+
+ do {
+ partlen = *(UINT32*)&inbuf[inoff];
+
+ if (partlen + inoff > inlen) {
+ ERR("overflow: %x + %x > %llx\n", partlen, inoff, inlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ inoff += sizeof(UINT32);
+
+ stream.in = &inbuf[inoff];
+ stream.inlen = partlen;
+ stream.inpos = 0;
+ stream.out = &outbuf[outoff];
+ stream.outlen = LINUX_PAGE_SIZE;
+ stream.outpos = 0;
+
+ Status = do_lzo_decompress(&stream);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_lzo_decompress returned %08x\n", Status);
+ return Status;
+ }
+
+ if (stream.outpos < stream.outlen)
+ RtlZeroMemory(&stream.out[stream.outpos], stream.outlen - stream.outpos);
+
+ inoff += partlen;
+ outoff += stream.outlen;
+
+ if (LINUX_PAGE_SIZE - (inoff % LINUX_PAGE_SIZE) < sizeof(UINT32))
+ inoff = ((inoff / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE;
+ } while (inoff < extlen);
+
+ return STATUS_SUCCESS;
+}
+
+static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) {
+ return ExAllocatePoolWithTag(PagedPool, items * size, ALLOC_TAG_ZLIB);
+}
+
+static void zlib_free(void* opaque, void* ptr) {
+ ExFreePool(ptr);
+}
+
+static NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+ z_stream c_stream;
+ int ret;
+
+ c_stream.zalloc = zlib_alloc;
+ c_stream.zfree = zlib_free;
+ c_stream.opaque = (voidpf)0;
+
+ ret = inflateInit(&c_stream);
+
+ if (ret != Z_OK) {
+ ERR("inflateInit returned %08x\n", ret);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ c_stream.next_in = inbuf;
+ c_stream.avail_in = inlen;
+
+ c_stream.next_out = outbuf;
+ c_stream.avail_out = outlen;
+
+ do {
+ ret = inflate(&c_stream, Z_NO_FLUSH);
+
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ ERR("inflate returned %08x\n", ret);
+ inflateEnd(&c_stream);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (ret != Z_STREAM_END);
+
+ ret = inflateEnd(&c_stream);
+
+ if (ret != Z_OK) {
+ ERR("inflateEnd returned %08x\n", ret);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace?
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+ if (type == BTRFS_COMPRESSION_ZLIB)
+ return zlib_decompress(inbuf, inlen, outbuf, outlen);
+ else if (type == BTRFS_COMPRESSION_LZO)
+ return lzo_decompress(inbuf, inlen, outbuf, outlen);
+ else {
+ ERR("unsupported compression type %x\n", type);
+ return STATUS_NOT_SUPPORTED;
+ }
+}
+
+static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ UINT8 compression;
+ UINT64 comp_length;
+ UINT8* comp_data;
+ UINT32 out_left;
+ LIST_ENTRY* le;
+ chunk* c;
+ z_stream c_stream;
+ int ret;
+
+ comp_data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
+ if (!comp_data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ ExFreePool(comp_data);
+ return Status;
+ }
+
+ c_stream.zalloc = zlib_alloc;
+ c_stream.zfree = zlib_free;
+ c_stream.opaque = (voidpf)0;
+
+ ret = deflateInit(&c_stream, fcb->Vcb->options.zlib_level);
+
+ if (ret != Z_OK) {
+ ERR("deflateInit returned %08x\n", ret);
+ ExFreePool(comp_data);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ c_stream.avail_in = end_data - start_data;
+ c_stream.next_in = data;
+ c_stream.avail_out = end_data - start_data;
+ c_stream.next_out = comp_data;
+
+ do {
+ ret = deflate(&c_stream, Z_FINISH);
+
+ if (ret == Z_STREAM_ERROR) {
+ ERR("deflate returned %x\n", ret);
+ ExFreePool(comp_data);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } while (c_stream.avail_in > 0 && c_stream.avail_out > 0);
+
+ out_left = c_stream.avail_out;
+
+ ret = deflateEnd(&c_stream);
+
+ if (ret != Z_OK) {
+ ERR("deflateEnd returned %08x\n", ret);
+ ExFreePool(comp_data);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+ ExFreePool(comp_data);
+
+ comp_length = end_data - start_data;
+ comp_data = data;
+ compression = BTRFS_COMPRESSION_NONE;
+
+ *compressed = FALSE;
+ } else {
+ UINT32 cl;
+
+ compression = BTRFS_COMPRESSION_ZLIB;
+ cl = end_data - start_data - out_left;
+ comp_length = sector_align(cl, fcb->Vcb->superblock.sector_size);
+
+ RtlZeroMemory(comp_data + cl, comp_length - cl);
+
+ *compressed = TRUE;
+ }
+
+ ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ le = fcb->Vcb->chunks.Flink;
+ while (le != &fcb->Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+
+ if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+ ExReleaseResourceLite(&c->lock);
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ if (compression != BTRFS_COMPRESSION_NONE)
+ ExFreePool(comp_data);
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ExReleaseResourceLite(&c->lock);
+
+ le = le->Flink;
+ }
+
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+
+ if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+ ExReleaseResourceLite(&c->lock);
+
+ if (compression != BTRFS_COMPRESSION_NONE)
+ ExFreePool(comp_data);
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ExReleaseResourceLite(&c->lock);
+ } else
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+ return STATUS_DISK_FULL;
+}
+
+static NTSTATUS lzo_do_compress(const UINT8* in, UINT32 in_len, UINT8* out, UINT32* out_len, void* wrkmem) {
+ const UINT8* ip;
+ UINT32 dv;
+ UINT8* op;
+ const UINT8* in_end = in + in_len;
+ const UINT8* ip_end = in + in_len - 9 - 4;
+ const UINT8* ii;
+ const UINT8** dict = (const UINT8**)wrkmem;
+
+ op = out;
+ ip = in;
+ ii = ip;
+
+ DVAL_FIRST(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+ DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+ DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+ DVAL_NEXT(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+
+ while (1) {
+ const UINT8* m_pos;
+ UINT32 m_len;
+ ptrdiff_t m_off;
+ UINT32 lit, dindex;
+
+ dindex = DINDEX(dv, ip);
+ m_pos = dict[dindex];
+ UPDATE_I(dict, cycle, dindex, ip);
+
+ if (!LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, M4_MAX_OFFSET) && m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2]) {
+ lit = ip - ii;
+ m_pos += 3;
+ if (m_off <= M2_MAX_OFFSET)
+ goto match;
+
+ if (lit == 3) { /* better compression, but slower */
+ if (op - 2 <= out)
+ return STATUS_INTERNAL_ERROR;
+
+ op[-2] |= LZO_BYTE(3);
+ *op++ = *ii++; *op++ = *ii++; *op++ = *ii++;
+ goto code_match;
+ }
+
+ if (*m_pos == ip[3])
+ goto match;
+ }
+
+ /* a literal */
+ ++ip;
+ if (ip >= ip_end)
+ break;
+ DVAL_NEXT(dv, ip);
+ continue;
+
+ /* a match */
+match:
+ /* store current literal run */
+ if (lit > 0) {
+ UINT32 t = lit;
+
+ if (t <= 3) {
+ if (op - 2 <= out)
+ return STATUS_INTERNAL_ERROR;
+
+ op[-2] |= LZO_BYTE(t);
+ } else if (t <= 18)
+ *op++ = LZO_BYTE(t - 3);
+ else {
+ UINT32 tt = t - 18;
+
+ *op++ = 0;
+ while (tt > 255) {
+ tt -= 255;
+ *op++ = 0;
+ }
+
+ if (tt <= 0)
+ return STATUS_INTERNAL_ERROR;
+
+ *op++ = LZO_BYTE(tt);
+ }
+
+ do {
+ *op++ = *ii++;
+ } while (--t > 0);
+ }
+
+
+ /* code the match */
+code_match:
+ if (ii != ip)
+ return STATUS_INTERNAL_ERROR;
+
+ ip += 3;
+ if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ ||
+ *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) {
+ --ip;
+ m_len = ip - ii;
+
+ if (m_len < 3 || m_len > 8)
+ return STATUS_INTERNAL_ERROR;
+
+ if (m_off <= M2_MAX_OFFSET) {
+ m_off -= 1;
+ *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2));
+ *op++ = LZO_BYTE(m_off >> 3);
+ } else if (m_off <= M3_MAX_OFFSET) {
+ m_off -= 1;
+ *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+ goto m3_m4_offset;
+ } else {
+ m_off -= 0x4000;
+
+ if (m_off <= 0 || m_off > 0x7fff)
+ return STATUS_INTERNAL_ERROR;
+
+ *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+ goto m3_m4_offset;
+ }
+ } else {
+ const UINT8* end;
+ end = in_end;
+ while (ip < end && *m_pos == *ip)
+ m_pos++, ip++;
+ m_len = (ip - ii);
+
+ if (m_len < 3)
+ return STATUS_INTERNAL_ERROR;
+
+ if (m_off <= M3_MAX_OFFSET) {
+ m_off -= 1;
+ if (m_len <= 33)
+ *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+ else {
+ m_len -= 33;
+ *op++ = M3_MARKER | 0;
+ goto m3_m4_len;
+ }
+ } else {
+ m_off -= 0x4000;
+
+ if (m_off <= 0 || m_off > 0x7fff)
+ return STATUS_INTERNAL_ERROR;
+
+ if (m_len <= 9)
+ *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+ else {
+ m_len -= 9;
+ *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11));
+m3_m4_len:
+ while (m_len > 255) {
+ m_len -= 255;
+ *op++ = 0;
+ }
+
+ if (m_len <= 0)
+ return STATUS_INTERNAL_ERROR;
+
+ *op++ = LZO_BYTE(m_len);
+ }
+ }
+
+m3_m4_offset:
+ *op++ = LZO_BYTE((m_off & 63) << 2);
+ *op++ = LZO_BYTE(m_off >> 6);
+ }
+
+ ii = ip;
+ if (ip >= ip_end)
+ break;
+ DVAL_FIRST(dv, ip);
+ }
+
+ /* store final literal run */
+ if (in_end - ii > 0) {
+ UINT32 t = in_end - ii;
+
+ if (op == out && t <= 238)
+ *op++ = LZO_BYTE(17 + t);
+ else if (t <= 3)
+ op[-2] |= LZO_BYTE(t);
+ else if (t <= 18)
+ *op++ = LZO_BYTE(t - 3);
+ else {
+ UINT32 tt = t - 18;
+
+ *op++ = 0;
+ while (tt > 255) {
+ tt -= 255;
+ *op++ = 0;
+ }
+
+ if (tt <= 0)
+ return STATUS_INTERNAL_ERROR;
+
+ *op++ = LZO_BYTE(tt);
+ }
+
+ do {
+ *op++ = *ii++;
+ } while (--t > 0);
+ }
+
+ *out_len = op - out;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo1x_1_compress(lzo_stream* stream) {
+ UINT8 *op = stream->out;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (stream->inlen <= 0)
+ stream->outlen = 0;
+ else if (stream->inlen <= 9 + 4) {
+ *op++ = LZO_BYTE(17 + stream->inlen);
+
+ stream->inpos = 0;
+ do {
+ *op++ = stream->in[stream->inpos];
+ stream->inpos++;
+ } while (stream->inlen < stream->inpos);
+ stream->outlen = op - stream->out;
+ } else
+ Status = lzo_do_compress(stream->in, stream->inlen, stream->out, &stream->outlen, stream->wrkmem);
+
+ if (Status == STATUS_SUCCESS) {
+ op = stream->out + stream->outlen;
+ *op++ = M4_MARKER | 1;
+ *op++ = 0;
+ *op++ = 0;
+ stream->outlen += 3;
+ }
+
+ return Status;
+}
+
+static __inline UINT32 lzo_max_outlen(UINT32 inlen) {
+ return inlen + (inlen / 16) + 64 + 3; // formula comes from LZO.FAQ
+}
+
+static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ UINT8 compression;
+ UINT64 comp_length;
+ ULONG comp_data_len, num_pages, i;
+ UINT8* comp_data;
+ BOOL skip_compression = FALSE;
+ lzo_stream stream;
+ UINT32* out_size;
+ LIST_ENTRY* le;
+ chunk* c;
+
+ num_pages = (sector_align(end_data - start_data, LINUX_PAGE_SIZE)) / LINUX_PAGE_SIZE;
+
+ // Four-byte overall header
+ // Another four-byte header page
+ // Each page has a maximum size of lzo_max_outlen(LINUX_PAGE_SIZE)
+ // Plus another four bytes for possible padding
+ comp_data_len = sizeof(UINT32) + ((lzo_max_outlen(LINUX_PAGE_SIZE) + (2 * sizeof(UINT32))) * num_pages);
+
+ comp_data = ExAllocatePoolWithTag(PagedPool, comp_data_len, ALLOC_TAG);
+ if (!comp_data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ stream.wrkmem = ExAllocatePoolWithTag(PagedPool, LZO1X_MEM_COMPRESS, ALLOC_TAG);
+ if (!stream.wrkmem) {
+ ERR("out of memory\n");
+ ExFreePool(comp_data);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ ExFreePool(comp_data);
+ ExFreePool(stream.wrkmem);
+ return Status;
+ }
+
+ out_size = (UINT32*)comp_data;
+ *out_size = sizeof(UINT32);
+
+ stream.in = data;
+ stream.out = comp_data + (2 * sizeof(UINT32));
+
+ for (i = 0; i < num_pages; i++) {
+ UINT32* pagelen = (UINT32*)(stream.out - sizeof(UINT32));
+
+ stream.inlen = min(LINUX_PAGE_SIZE, end_data - start_data - (i * LINUX_PAGE_SIZE));
+
+ Status = lzo1x_1_compress(&stream);
+ if (!NT_SUCCESS(Status)) {
+ ERR("lzo1x_1_compress returned %08x\n", Status);
+ skip_compression = TRUE;
+ break;
+ }
+
+ *pagelen = stream.outlen;
+ *out_size += stream.outlen + sizeof(UINT32);
+
+ stream.in += LINUX_PAGE_SIZE;
+ stream.out += stream.outlen + sizeof(UINT32);
+
+ if (LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE) < sizeof(UINT32)) {
+ RtlZeroMemory(stream.out, LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE));
+ stream.out += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+ *out_size += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+ }
+ }
+
+ ExFreePool(stream.wrkmem);
+
+ if (skip_compression || *out_size >= end_data - start_data - fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+ ExFreePool(comp_data);
+
+ comp_length = end_data - start_data;
+ comp_data = data;
+ compression = BTRFS_COMPRESSION_NONE;
+
+ *compressed = FALSE;
+ } else {
+ compression = BTRFS_COMPRESSION_LZO;
+ comp_length = sector_align(*out_size, fcb->Vcb->superblock.sector_size);
+
+ RtlZeroMemory(comp_data + *out_size, comp_length - *out_size);
+
+ *compressed = TRUE;
+ }
+
+ ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ le = fcb->Vcb->chunks.Flink;
+ while (le != &fcb->Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+
+ if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+ ExReleaseResourceLite(&c->lock);
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ if (compression != BTRFS_COMPRESSION_NONE)
+ ExFreePool(comp_data);
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ExReleaseResourceLite(&c->lock);
+
+ le = le->Flink;
+ }
+
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+
+ if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+ ExReleaseResourceLite(&c->lock);
+
+ if (compression != BTRFS_COMPRESSION_NONE)
+ ExFreePool(comp_data);
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ ExReleaseResourceLite(&c->lock);
+ } else
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+ return STATUS_DISK_FULL;
+}
+
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ UINT8 type;
+
+ if (fcb->Vcb->options.compress_type != 0)
+ type = fcb->Vcb->options.compress_type;
+ else {
+ if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO)
+ type = BTRFS_COMPRESSION_LZO;
+ else
+ type = BTRFS_COMPRESSION_ZLIB;
+ }
+
+ if (type == BTRFS_COMPRESSION_LZO) {
+ fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+ return lzo_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+ } else
+ return zlib_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+}
extern PDEVICE_OBJECT devobj;
-static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = parinode;
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
return STATUS_NOT_FOUND;
}
-static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_INODE_EXTREF;
searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
return STATUS_NOT_FOUND;
}
-static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_ROOT_REF;
searchkey.offset = subvolid;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
return STATUS_NOT_FOUND;
}
-static NTSTATUS load_index_list(fcb* fcb) {
+static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = 2;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (keycmp(&tp.item->key, &searchkey) == -1) {
- if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) {
+ if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
}
nextitem:
- b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
}
static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
- UINT64* pindex, PANSI_STRING utf8) {
+ UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
LIST_ENTRY* le;
NTSTATUS Status;
UNICODE_STRING us;
ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
if (!fr->fcb->index_loaded) {
- Status = load_index_list(fr->fcb);
+ Status = load_index_list(fr->fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_index_list returned %08x\n", Status);
goto end;
return Status;
}
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
- root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) {
+static NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
+ root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
DIR_ITEM* di;
KEY searchkey;
traverse_ptr tp;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
} else {
+ ANSI_STRING nutf8;
+
us.Buffer = utf16;
us.Length = us.MaximumLength = (USHORT)stringlen;
index = 0;
if (fr->fcb->subvol != Vcb->root_root) {
+ nutf8.Buffer = di->name;
+ nutf8.Length = nutf8.MaximumLength = di->n;
+
if (di->key.obj_type == TYPE_ROOT_ITEM) {
- Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+ Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_subvol_dir_index returned %08x\n", Status);
return Status;
}
} else {
- Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+ Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
if (!NT_SUCCESS(Status)) {
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
- Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+ Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_file_dir_index_extref returned %08x\n", Status);
}
byindex:
- Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8);
+ Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8, Irp);
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
ERR("find_file_in_dir_index returned %08x\n", Status);
return Status;
return Status;
}
-fcb* create_fcb() {
+fcb* create_fcb(POOL_TYPE pool_type) {
fcb* fcb;
fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
}
NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
- root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) {
+ root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp) {
char* fn;
UINT32 crc32;
ULONG utf8len;
crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
- return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8);
+ return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, Irp);
}
-static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) {
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
NTSTATUS Status;
ULONG utf8len;
char* utf8;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
searchkey.offset = 0;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
return deleted;
}
-static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) {
+static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
return ei->refcount;
}
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
}
}
- fcb = create_fcb();
+ fcb = create_fcb(PagedPool);
if (!fcb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
free_fcb(fcb);
fcb->type = BTRFS_TYPE_FILE;
}
- fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE);
+ fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp);
- fcb_get_sd(fcb, parent);
+ fcb_get_sd(fcb, parent, Irp);
if (fcb->type == BTRFS_TYPE_DIRECTORY) {
UINT8* xattrdata;
UINT16 xattrlen;
- if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) {
+ if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen, Irp)) {
fcb->reparse_xattr.Buffer = (char*)xattrdata;
fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
}
searchkey.obj_type = TYPE_EXTENT_DATA;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
free_fcb(fcb);
return STATUS_INTERNAL_ERROR;
}
+ if (ed2->address == 0 && ed2->size == 0) // sparse
+ goto nextitem;
+
if (ed2->size != 0)
- unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1;
+ unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size, Irp) == 1;
}
ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
InsertTailList(&fcb->extents, &ext->list_entry);
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+nextitem:
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
free_fcb(fcb);
}
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
}
NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
- UINT32 streamhash, fcb* parent, fcb** pfcb) {
+ UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) {
fcb* fcb;
UINT8* xattrdata;
- UINT16 xattrlen;
+ UINT16 xattrlen, overhead;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
if (!IsListEmpty(&subvol->fcbs)) {
LIST_ENTRY* le = subvol->fcbs.Flink;
}
}
- fcb = create_fcb();
+ fcb = create_fcb(PagedPool);
if (!fcb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
- if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) {
+ if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) {
ERR("get_xattr failed\n");
free_fcb(fcb);
return STATUS_INTERNAL_ERROR;
fcb->adshash = streamhash;
fcb->adsxattr = *xattr;
+ // find XATTR_ITEM overhead and hence calculate maximum length
+
+ searchkey.obj_id = parent->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = streamhash;
+
+ Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ ERR("error - could not find key for xattr\n");
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < xattrlen) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ overhead = tp.item->size - xattrlen;
+
+ fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
+
fcb->adsdata.Buffer = (char*)xattrdata;
fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
ExReleaseResourceLite(&parent->nonpaged->children_lock);
}
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp) {
UNICODE_STRING fnus2;
file_ref *dir, *sf, *sf2;
ULONG i, num_parts;
NTSTATUS Status;
TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
+
+#ifdef DEBUG
+ if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+ ERR("fcb_lock not acquired exclusively\n");
+ int3;
+ }
+#endif
if (Vcb->removing || Vcb->locked)
return STATUS_ACCESS_DENIED;
// FIXME - check if already opened
- if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) {
+ if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr, Irp)) {
TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
- Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb);
+ Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb_stream returned %08x\n", Status);
goto end;
UINT8 type;
ANSI_STRING utf8;
- Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8);
+ Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, Irp);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
fcb* fcb;
ULONG strlen;
- if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
- WARN("passed path including file as subdirectory\n");
-
- Status = STATUS_OBJECT_PATH_NOT_FOUND;
- goto end;
- }
-
- Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb);
+ Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
goto end;
}
+ if (type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ WARN("passed path including file as subdirectory\n");
+ free_fcb(fcb);
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ goto end;
+ }
+
sf2 = create_fileref();
if (!sf2) {
ERR("out of memory\n");
}
}
- if (i == num_parts - 1)
+ if (i == num_parts - 1) {
+ if (fn_offset)
+ *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
+
break;
+ }
if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
Status = STATUS_REPARSE;
*pfr = sf2;
end:
- ExReleaseResourceLite(&Vcb->fcb_lock);
free_fileref(sf);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
end2:
if (parts)
return Status;
}
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, prev_tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_DIR_INDEX + 1;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) {
- if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE))
+ if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE, Irp))
tp = prev_tp;
}
LARGE_INTEGER time;
BTRFS_TIME now;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
ULONG defda;
file_ref* fileref;
hardlink* hl;
if (!NT_SUCCESS(Status))
return Status;
- utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+ utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
if (!utf8) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
utf8[utf8len] = 0;
- Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos);
+ Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
ExFreePool(utf8);
mark_fcb_dirty(parfileref->fcb);
if (parfileref->fcb->subvol->lastinode == 0)
- get_last_inode(Vcb, parfileref->fcb->subvol);
+ get_last_inode(Vcb, parfileref->fcb->subvol, Irp);
inode = parfileref->fcb->subvol->lastinode + 1;
parfileref->fcb->subvol->lastinode++;
- fcb = create_fcb();
+ fcb = create_fcb(pool_type);
if (!fcb) {
ERR("out of memory\n");
ExFreePool(utf8);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
fcb->Vcb = Vcb;
-
+
+ if (IrpSp->Flags & SL_OPEN_PAGING_FILE)
+ fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
+
fcb->inode_item.generation = Vcb->superblock.generation;
fcb->inode_item.transid = Vcb->superblock.generation;
fcb->inode_item.st_size = 0;
fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
}
- // inherit nodatacow flag from parent directory
- if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
- fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+ if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
+ fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
+ } else {
+ // inherit nodatacow flag from parent directory
+ if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+ fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+
+ if (type != BTRFS_TYPE_DIRECTORY)
+ fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+ }
- if (type != BTRFS_TYPE_DIRECTORY)
- fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+ if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
+ fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
}
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_new_sd returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
return Status;
}
fcb->sd_dirty = TRUE;
- hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
+ hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG);
if (!hl) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
hl->index = dirpos;
hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
- hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
+ hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG);
if (!hl->utf8.Buffer) {
ERR("out of memory\n");
ExFreePool(hl);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
hl->name.Length = hl->name.MaximumLength = fpus->Length;
- hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG);
+ hl->name.Buffer = ExAllocatePoolWithTag(pool_type, fpus->Length, ALLOC_TAG);
if (!hl->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl->utf8.Buffer);
ExFreePool(hl);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fileref = create_fileref();
if (!fileref) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
fileref->utf8.Buffer = utf8;
- fileref->filepart = *fpus;
+ fileref->filepart.Length = fileref->filepart.MaximumLength = fpus->Length;
+
+ if (fileref->filepart.Length == 0)
+ fileref->filepart.Buffer = NULL;
+ else {
+ fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
+
+ if (!fileref->filepart.Buffer) {
+ ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fileref->filepart.Buffer, fpus->Buffer, fpus->Length);
+ }
Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
return Status;
}
- if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+ if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
if (!NT_SUCCESS(Status)) {
ERR("extend_file returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
return Status;
}
}
fcb->subvol->root_item.ctime = now;
fileref->parent = parfileref;
-
- ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-
- if (IsListEmpty(&parfileref->children))
- InsertTailList(&parfileref->children, &fileref->list_entry);
- else {
- LIST_ENTRY* le = parfileref->children.Flink;
- file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
-
- while (le != &parfileref->children) {
- file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
-
- if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) {
- InsertHeadList(&fr1->list_entry, &fileref->list_entry);
- break;
- }
-
- fr1 = fr2;
- le = le->Flink;
- }
- }
-
- ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+
+ insert_fileref_child(parfileref, fileref, TRUE);
increase_fileref_refcount(parfileref);
return STATUS_SUCCESS;
}
+static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream,
+ PIRP Irp, ULONG options, POOL_TYPE pool_type, LIST_ENTRY* rollback) {
+ file_ref *fileref, *newpar, *parfileref;
+ fcb* fcb;
+ static char xapref[] = "user.";
+ ULONG xapreflen = strlen(xapref), overhead;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ ULONG utf8len;
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc;
+#endif
+
+ TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
+ TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
+
+ parfileref = *pparfileref;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &newpar, fpus, parfileref, FALSE, NULL, NULL, Irp);
+ ExReleaseResource(&Vcb->fcb_lock);
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ UNICODE_STRING fpus2;
+
+ if (!is_file_name_valid(fpus))
+ return STATUS_OBJECT_NAME_INVALID;
+
+ fpus2.Length = fpus2.MaximumLength = fpus->Length;
+ fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
+
+ if (!fpus2.Buffer) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
+
+ Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("file_create2 returned %08x\n", Status);
+ ExFreePool(fpus2.Buffer);
+ return Status;
+ }
+
+ send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+ send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("open_fileref returned %08x\n", Status);
+ return Status;
+ }
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(parfileref);
+ ExReleaseResource(&Vcb->fcb_lock);
+
+ parfileref = newpar;
+ *pparfileref = parfileref;
+
+ if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
+ WARN("parent not file or symlink\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (options & FILE_DIRECTORY_FILE) {
+ WARN("tried to create directory as stream\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fcb = create_fcb(pool_type);
+ if (!fcb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fcb->Vcb = Vcb;
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&parfileref->fcb->refcount);
+ WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
+#else
+ InterlockedIncrement(&parfileref->fcb->refcount);
+#endif
+ fcb->subvol = parfileref->fcb->subvol;
+ fcb->inode = parfileref->fcb->inode;
+ fcb->type = parfileref->fcb->type;
+
+ fcb->ads = TRUE;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return Status;
+ }
+
+ fcb->adsxattr.Length = utf8len + xapreflen;
+ fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
+ fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
+ if (!fcb->adsxattr.Buffer) {
+ ERR("out of memory\n");
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+
+ Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return Status;
+ }
+
+ fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
+
+ TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
+
+ fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+ TRACE("adshash = %08x\n", fcb->adshash);
+
+ searchkey.obj_id = parfileref->fcb->inode;
+ searchkey.obj_type = TYPE_XATTR_ITEM;
+ searchkey.offset = fcb->adshash;
+
+ Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return Status;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey))
+ overhead = tp.item->size;
+ else
+ overhead = 0;
+
+ fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
+
+ if (utf8len + xapreflen + overhead > fcb->adsmaxlen) {
+ WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + xapreflen, overhead, fcb->adsmaxlen);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return STATUS_DISK_FULL;
+ } else
+ fcb->adsmaxlen -= overhead + utf8len + xapreflen;
+
+ fileref = create_fileref();
+ if (!fileref) {
+ ERR("out of memory\n");
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fcb(fcb);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fileref->fcb = fcb;
+
+ fileref->filepart.MaximumLength = fileref->filepart.Length = stream->Length;
+ fileref->filepart.Buffer = ExAllocatePoolWithTag(pool_type, fileref->filepart.MaximumLength, ALLOC_TAG);
+ if (!fileref->filepart.Buffer) {
+ ERR("out of memory\n");
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length);
+
+ Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
+ return Status;
+ }
+
+ mark_fcb_dirty(fcb);
+ mark_fileref_dirty(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ ExReleaseResource(&Vcb->fcb_lock);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
+ parfileref->fcb->inode_item.sequence++;
+ parfileref->fcb->inode_item.st_ctime = now;
+
+ mark_fcb_dirty(parfileref->fcb);
+
+ parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ parfileref->fcb->subvol->root_item.ctime = now;
+
+ fileref->parent = (struct _file_ref*)parfileref;
+
+ insert_fileref_child(parfileref, fileref, TRUE);
+
+ increase_fileref_refcount(parfileref);
+
+ *pfileref = fileref;
+
+ return STATUS_SUCCESS;
+}
+
static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
NTSTATUS Status;
// fcb *fcb, *parfcb = NULL;
file_ref *fileref, *parfileref = NULL, *related;
- ULONG i, j;
+ ULONG i, j, fn_offset;
// ULONG utf8len;
ccb* ccb;
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
UNICODE_STRING dsus, fpus, stream;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
#endif
-
+
TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
if (Vcb->readonly)
} else
related = NULL;
- Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, Irp);
+ ExReleaseResource(&Vcb->fcb_lock);
+
if (!NT_SUCCESS(Status))
goto end;
while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
- fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
+ fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
if (!fpus.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
fpus.Buffer[j - i + 1] = 0;
+ fn_offset = i;
+
if (fpus.Length > dsus.Length) { // check for :$DATA suffix
UNICODE_STRING lb;
}
if (stream.Length > 0) {
- file_ref* newpar;
- fcb* fcb;
- static char xapref[] = "user.";
- ULONG xapreflen = strlen(xapref);
- LARGE_INTEGER time;
- BTRFS_TIME now;
- ULONG utf8len;
-#ifdef DEBUG_FCB_REFCOUNTS
- LONG rc;
-#endif
-
- TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
- TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
-
- Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
-
- if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- UNICODE_STRING fpus2;
-
- if (!is_file_name_valid(&fpus))
- return STATUS_OBJECT_NAME_INVALID;
-
- fpus2.Length = fpus2.MaximumLength = fpus.Length;
- fpus2.Buffer = ExAllocatePoolWithTag(PagedPool, fpus2.MaximumLength, ALLOC_TAG);
-
- if (!fpus2.Buffer) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- RtlCopyMemory(fpus2.Buffer, fpus.Buffer, fpus2.Length);
-
- Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("file_create2 returned %08x\n", Status);
- ExFreePool(fpus2.Buffer);
- goto end;
- }
-
- send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
- send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
- } else if (!NT_SUCCESS(Status)) {
- ERR("open_fileref returned %08x\n", Status);
- goto end;
- }
-
- free_fileref(parfileref);
- parfileref = newpar;
-
- if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
- WARN("parent not file or symlink\n");
- Status = STATUS_INVALID_PARAMETER;
- goto end;
- }
-
- if (options & FILE_DIRECTORY_FILE) {
- WARN("tried to create directory as stream\n");
- Status = STATUS_INVALID_PARAMETER;
- goto end;
- }
-
- fcb = create_fcb();
- if (!fcb) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- fcb->Vcb = Vcb;
-
- fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
- fcb->Header.AllocationSize.QuadPart = 0;
- fcb->Header.FileSize.QuadPart = 0;
- fcb->Header.ValidDataLength.QuadPart = 0;
-
-#ifdef DEBUG_FCB_REFCOUNTS
- rc = InterlockedIncrement(&parfileref->fcb->refcount);
- WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
-#else
- InterlockedIncrement(&parfileref->fcb->refcount);
-#endif
- fcb->subvol = parfileref->fcb->subvol;
- fcb->inode = parfileref->fcb->inode;
- fcb->type = parfileref->fcb->type;
-
- fcb->ads = TRUE;
-
- Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
+ Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
- free_fcb(fcb);
+ ERR("create_stream returned %08x\n", Status);
goto end;
}
-
- fcb->adsxattr.Length = utf8len + xapreflen;
- fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
- fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
- if (!fcb->adsxattr.Buffer) {
- ERR("out of memory\n");
- free_fcb(fcb);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
-
- Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
- free_fcb(fcb);
- goto end;
- }
-
- fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
-
- TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
-
- fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
- TRACE("adshash = %08x\n", fcb->adshash);
-
- fileref = create_fileref();
- if (!fileref) {
- ERR("out of memory\n");
- free_fcb(fcb);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- fileref->fcb = fcb;
-
- fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length;
- fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
- if (!fileref->filepart.Buffer) {
- ERR("out of memory\n");
- free_fileref(fileref);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
-
- Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(fileref);
- goto end;
- }
-
- mark_fcb_dirty(fcb);
-
- InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
- KeQuerySystemTime(&time);
- win_time_to_unix(time, &now);
-
- parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
- parfileref->fcb->inode_item.sequence++;
- parfileref->fcb->inode_item.st_ctime = now;
-
- mark_fcb_dirty(parfileref->fcb);
-
- parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
- parfileref->fcb->subvol->root_item.ctime = now;
-
- fileref->parent = (struct _file_ref*)parfileref;
-
- ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
- InsertTailList(&parfileref->children, &fileref->list_entry);
- ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
-
- increase_fileref_refcount(parfileref);
-
- ExFreePool(fpus.Buffer);
- fpus.Buffer = NULL;
} else {
if (!is_file_name_valid(&fpus)) {
Status = STATUS_OBJECT_NAME_INVALID;
if (!ccb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto end;
}
#endif
FileObject->FsContext2 = ccb;
+
+ if (fn_offset > 0) {
+ FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+ RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+ }
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
ExFreePool(fpus.Buffer);
end2:
- if (parfileref)
+ if (parfileref) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(parfileref);
+ ExReleaseResource(&Vcb->fcb_lock);
+ }
return Status;
}
if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
ULONG size, bytes_read, i;
- if (fcb->inode_item.st_size < sizeof(ULONG)) {
+ if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
WARN("file was too short to be a reparse point\n");
return STATUS_INVALID_PARAMETER;
}
device_extension* Vcb = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
USHORT unparsed;
+ ULONG fn_offset = 0;
file_ref *related, *fileref;
#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
goto exit;
}
- // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
-
+ if (Vcb->readonly && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+ (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto exit;
+ }
+
if (options & FILE_OPEN_BY_FILE_ID) {
if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
UINT64 inode;
fileref = related->parent;
increase_fileref_refcount(fileref);
Status = STATUS_SUCCESS;
- } else
- Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref);
+ } else {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
+ ExReleaseResource(&Vcb->fcb_lock);
+ }
} else {
WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
- Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset, Irp);
+ ExReleaseResource(&Vcb->fcb_lock);
}
if (Status == STATUS_REPARSE) {
if (RequestedDisposition == FILE_CREATE) {
TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
Status = STATUS_OBJECT_NAME_COLLISION;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
goto exit;
}
- } else {
+ } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
TRACE("open_fileref returned %08x\n", Status);
goto exit;
+ } else {
+ ERR("open_fileref returned %08x\n", Status);
+ goto exit;
}
if (NT_SUCCESS(Status)) { // file already exists
if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
if (Vcb->readonly) {
Status = STATUS_MEDIA_WRITE_PROTECTED;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
}
+ if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+ (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+ Status = STATUS_ACCESS_DENIED;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
+ goto exit;
+ }
+
TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
sf = fileref;
if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) {
Status = STATUS_ACCESS_DENIED;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
}
- if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
- ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- free_fileref(fileref);
- ExReleaseResourceLite(&Vcb->fcb_lock);
-
- Status = STATUS_FILE_IS_A_DIRECTORY;
- goto exit;
- } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
- TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
-
- ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- free_fileref(fileref);
- ExReleaseResourceLite(&Vcb->fcb_lock);
-
- Status = STATUS_NOT_A_DIRECTORY;
- goto exit;
- }
-
if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly ||
fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) {
Status = STATUS_CANNOT_DELETE;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
-
if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) {
- UINT8* data;
+ REPARSE_DATA_BUFFER* data;
/* How reparse points work from the point of view of the filesystem appears to
* undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
* a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
* IopSymlinkProcessReparse will do the translation for us. */
- Status = get_reparse_block(fileref->fcb, &data);
+ Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
if (!NT_SUCCESS(Status)) {
ERR("get_reparse_block returned %08x\n", Status);
}
Status = STATUS_REPARSE;
- RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+ Irp->IoStatus.Information = data->ReparseTag;
+
+ if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\')
+ data->Reserved = sizeof(WCHAR);
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
goto exit;
}
+ if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+ if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ Status = STATUS_FILE_IS_A_DIRECTORY;
+ goto exit;
+ }
+ } else if (options & FILE_DIRECTORY_FILE) {
+ TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ Status = STATUS_NOT_A_DIRECTORY;
+ goto exit;
+ }
+
if (fileref->fcb->open_count > 0) {
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
WARN("cannot overwrite readonly file\n");
Status = STATUS_ACCESS_DENIED;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
// }
// FIXME - make sure not ADS!
- Status = truncate_file(fileref->fcb, fileref->fcb->inode_item.st_size, rollback);
+ Status = truncate_file(fileref->fcb, 0, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
if (!NT_SUCCESS(Status)) {
ERR("extend_file returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fileref);
+ ExReleaseResource(&Vcb->fcb_lock);
goto exit;
}
}
oldatts = fileref->fcb->atts;
defda = get_file_attributes(Vcb, &fileref->fcb->inode_item, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
- fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+ fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
if (RequestedDisposition == FILE_SUPERSEDE)
fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
FileObject->FsContext2 = ccb;
+ if (fn_offset > 0) {
+ FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+ RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+ }
+
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
if (NT_SUCCESS(Status)) {
return Status;
}
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
+ UINT64 i;
+
+ for (i = 0; i < Vcb->superblock.num_devices; i++) {
+ if (Vcb->devices[i].removable) {
+ NTSTATUS Status;
+ ULONG cc;
+ IO_STATUS_BLOCK iosb;
+
+ Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return Status;
+ }
+
+ if (iosb.Information < sizeof(ULONG)) {
+ ERR("iosb.Information was too short\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (cc != Vcb->devices[i].change_count) {
+ PDEVICE_OBJECT dev;
+
+ Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+
+ dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+ IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+
+ if (!dev) {
+ dev = IoGetDeviceToVerify(PsGetCurrentThread());
+ IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+ }
+
+ dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+
+ if (dev)
+ IoVerifyVolume(dev, FALSE);
+
+ return STATUS_VERIFY_REQUIRED;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
}
Vcb = DeviceObject->DeviceExtension;
+
+ Status = verify_vcb(Vcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("verify_vcb returned %08x\n", Status);
+ goto exit;
+ }
+
ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
locked = TRUE;
enum DirEntryType dir_entry_type;
} dir_entry;
-static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) {
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) {
fcb* fcb;
ULONG tag = 0, br;
NTSTATUS Status;
return 0;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb);
+ Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
ExReleaseResourceLite(&Vcb->fcb_lock);
end:
ExReleaseResourceLite(fcb->Header.Resource);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
return tag;
}
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
BOOL dotfile = de->namelen > 1 && de->name[0] == '.';
- atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+ atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp);
}
}
fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
- fbdi->ChangeTime.QuadPart = 0;
+ fbdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fbdi->FileAttributes = atts;
fbdi->FileNameLength = stringlen;
- fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+ fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
fbdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
- fdi->ChangeTime.QuadPart = 0;
+ fdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fdi->FileAttributes = atts;
ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
- ffdi->ChangeTime.QuadPart = 0;
+ ffdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
ffdi->FileAttributes = atts;
ffdi->FileNameLength = stringlen;
- ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+ ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
- fibdi->ChangeTime.QuadPart = 0;
+ fibdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fibdi->FileAttributes = atts;
fibdi->FileNameLength = stringlen;
- fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+ fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
fibdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fibdi->FileId.QuadPart = inode;
return STATUS_NO_MORE_FILES;
}
-static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) {
+static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
DIR_ITEM* di;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = *offset;
- Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
if (keycmp(&tp.item->key, &searchkey) == -1) {
- if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE))
+ if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp))
tp = next_tp;
}
}
newoffset = ccb->query_dir_offset;
- Status = next_dir_entry(fileref, &newoffset, &de);
+ Status = next_dir_entry(fileref, &newoffset, &de, Irp);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_NO_MORE_FILES && initial)
ExFreePool(us.Buffer);
if (!found) {
- Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8);
+ Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8, Irp);
if (!NT_SUCCESS(Status)) {
Status = STATUS_NO_SUCH_FILE;
ExFreePool(de.name);
newoffset = ccb->query_dir_offset;
- Status = next_dir_entry(fileref, &newoffset, &de);
+ Status = next_dir_entry(fileref, &newoffset, &de, Irp);
ExFreePool(uni_fn);
if (NT_SUCCESS(Status)) {
UNICODE_STRING di_uni_fn;
newoffset = ccb->query_dir_offset;
- Status = next_dir_entry(fileref, &newoffset, &de);
+ Status = next_dir_entry(fileref, &newoffset, &de, Irp);
if (NT_SUCCESS(Status)) {
if (has_wildcard) {
ULONG stringlen;
}
}
-static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
*ptr = type;
RtlCopyMemory(ptr + 1, data, datalen);
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ei);
return STATUS_INTERNAL_ERROR;
}
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
searchkey.obj_type = type;
searchkey.offset = offset;
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp2, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
RtlCopyMemory(data2, data, datalen);
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
EXTENT_DATA_REF edr;
edr.root = root;
edr.offset = offset;
edr.count = refcount;
- return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, rollback);
+ return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
}
void decrease_chunk_usage(chunk* c, UINT64 delta) {
}
static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
- UINT8 level, UINT64 parent, LIST_ENTRY* rollback) {
+ UINT8 level, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
NTSTATUS Status;
traverse_ptr tp, tp2;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ei);
return STATUS_INTERNAL_ERROR;
}
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
searchkey.obj_type = type;
searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data);
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
newedr->count -= edr->count;
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
}
NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode,
- UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+ UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
EXTENT_DATA_REF edr;
edr.root = root;
edr.offset = offset;
edr.count = refcount;
- return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback);
+ return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, Irp, rollback);
}
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback) {
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
SHARED_DATA_REF sdr;
sdr.offset = treeaddr;
sdr.count = 1;
- return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, rollback);
+ return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, Irp, rollback);
}
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback) {
- return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback) {
+ return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, Irp, rollback);
}
typedef struct {
return STATUS_SUCCESS;
}
-static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, LIST_ENTRY* rollback) {
+static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY *le, *next_le;
UINT64 refcount;
ULONG inline_len;
le = le->Flink;
}
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(ei);
return STATUS_INTERNAL_ERROR;
while (le != extent_refs) {
extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_EXTENT_REF_V0;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
InitializeListHead(&extent_refs);
do {
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
Status = populate_extent_refs_from_tree(Vcb, tp.item->key.offset, address, &extent_refs);
}
} while (b);
- Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, rollback);
+ Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("construct_extent_item returned %08x\n", Status);
free_extent_refs(&extent_refs);
return STATUS_SUCCESS;
}
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset) {
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("could not find address %llx in extent tree\n", address);
+ TRACE("could not find address %llx in extent tree\n", address);
return 0;
}
searchkey.obj_type = TYPE_EXTENT_DATA_REF;
searchkey.offset = get_extent_data_ref_hash2(root, objid, offset);
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
searchkey.obj_type = TYPE_EXTENT_REF_V0;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
do {
traverse_ptr next_tp;
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
if (tp.item->size >= sizeof(EXTENT_REF_V0)) {
searchkey.obj_type = TYPE_SHARED_DATA_REF;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
do {
traverse_ptr next_tp;
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
if (tp.item->size >= sizeof(SHARED_DATA_REF)) {
fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime);
fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime);
fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime);
- fbi->ChangeTime.QuadPart = 0;
+ fbi->ChangeTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_ctime);
fbi->FileAttributes = fcb->atts;
IoStatus->Status = STATUS_SUCCESS;
return FALSE;
}
-static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
- TRACE("STUB: fast_io_acquire_for_mod_write\n");
- return STATUS_NOT_IMPLEMENTED;
+static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
+ fcb* fcb;
+
+ TRACE("(%p, %llx, %p, %p)\n", FileObject, EndingOffset->QuadPart, ResourceToRelease, DeviceObject);
+
+ fcb = FileObject->FsContext;
+
+ if (!fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ *ResourceToRelease = fcb->Header.PagingIoResource;
+
+ if (!ExAcquireResourceSharedLite(*ResourceToRelease, FALSE))
+ return STATUS_CANT_WAIT;
+
+ return STATUS_SUCCESS;
}
static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
#endif
#endif
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us);
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp);
static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
LARGE_INTEGER time;
BTRFS_TIME now;
- defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
if (fcb->type == BTRFS_TYPE_DIRECTORY)
fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
-// FIXME - CreationTime
-// FIXME - LastAccessTime
-// FIXME - LastWriteTime
-// FIXME - ChangeTime
+ if (fbi->CreationTime.QuadPart == -1) {
+ FIXME("FIXME - support CreationTime == -1\n"); // FIXME - set ccb flag
+ } else if (fbi->CreationTime.QuadPart != 0) {
+ win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
+ inode_item_changed = TRUE;
+ filter |= FILE_NOTIFY_CHANGE_CREATION;
+ }
+
+ if (fbi->LastAccessTime.QuadPart == -1) {
+ FIXME("FIXME - support LastAccessTime == -1\n"); // FIXME - set ccb flag
+ } else if (fbi->LastAccessTime.QuadPart != 0) {
+ win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
+ inode_item_changed = TRUE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ }
+
+ if (fbi->LastWriteTime.QuadPart == -1) {
+ FIXME("FIXME - support LastWriteTime == -1\n"); // FIXME - set ccb flag
+ } else if (fbi->LastWriteTime.QuadPart != 0) {
+ win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
+ inode_item_changed = TRUE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+
+ if (fbi->ChangeTime.QuadPart == -1) {
+ FIXME("FIXME - support ChangeTime == -1\n"); // FIXME - set ccb flag
+ } else if (fbi->ChangeTime.QuadPart != 0) {
+ win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
+ inode_item_changed = TRUE;
+ // no filter for this
+ }
if (inode_item_changed) {
fcb->inode_item.transid = Vcb->superblock.generation;
return Status;
}
-static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
INODE_EXTREF* ier;
searchkey.obj_type = TYPE_INODE_EXTREF;
searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
ier->n = utf8->Length;
RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
- if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
INODE_REF* ir;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = parinode;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (irsize > maxlen) {
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
TRACE("INODE_REF too long, creating INODE_EXTREF\n");
- return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback);
+ return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, Irp, rollback);
} else {
ERR("item would be too long (%u > %u)\n", irsize, maxlen);
return STATUS_INTERNAL_ERROR;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
ir->n = utf8->Length;
RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
- if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
}
static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
+ device_extension* Vcb = oldfcb->Vcb;
fcb* fcb;
LIST_ENTRY* le;
// FIXME - we can skip a lot of this if the inode is about to be deleted
- fcb = create_fcb();
+ fcb = create_fcb(PagedPool); // FIXME - what if we duplicate the paging file?
if (!fcb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
- fcb->Vcb = oldfcb->Vcb;
+ fcb->Vcb = Vcb;
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
if (oldfcb->ads) {
fcb->ads = TRUE;
fcb->adshash = oldfcb->adshash;
+ fcb->adsmaxlen = oldfcb->adsmaxlen;
if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
fcb->adsxattr.Length = oldfcb->adsxattr.Length;
if (!fcb->adsxattr.Buffer) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!fcb->adsdata.Buffer) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
if (!fcb->sd) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!ext2) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!ext2->data) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!hl2) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!hl2->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl2);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("out of memory\n");
ExFreePool(hl2->name.Buffer);
ExFreePool(hl2);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
if (!fcb->reparse_xattr.Buffer) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
}
-
- if (oldfcb->ads) {
- fcb->ads = TRUE;
- fcb->adshash = oldfcb->adshash;
-
- if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
- fcb->adsxattr.Length = fcb->adsxattr.MaximumLength = oldfcb->adsxattr.Length;
- fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
- if (!fcb->adsxattr.Buffer) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
- }
-
- if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
- fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
- fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
- if (!fcb->adsdata.Buffer) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
- }
- }
end:
*pfcb = fcb;
LIST_ENTRY list_entry;
} move_entry;
-static NTSTATUS add_children_to_move_list(move_entry* me) {
+static NTSTATUS add_children_to_move_list(move_entry* me, PIRP Irp) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = 0;
- Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
RtlCopyMemory(xattr.Buffer, xa->name, xa->n);
xattr.Buffer[xa->n] = 0;
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
Status = open_fcb_stream(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, &xattr,
- tp.item->key.offset, me->fileref->fcb, &fcb);
+ tp.item->key.offset, me->fileref->fcb, &fcb, Irp);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
if (!NT_SUCCESS(Status)) {
ERR("open_fcb_stream returned %08x\n", Status);
ExFreePool(xattr.Buffer);
fr = create_fileref();
if (!fr) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!fr->filepart.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!me) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
} while (len > 0);
}
- b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = 2;
- Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
inode = di->key.obj_id;
}
- Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb);
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
+ Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb, Irp);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(utf8.Buffer);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fcb(fcb);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!fr->filepart.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
if (!me) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+
goto end;
}
}
}
- b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
return Status;
}
-static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, LIST_ENTRY* rollback) {
+static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
LIST_ENTRY move_list, *le;
move_entry* me;
ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
- Status = add_children_to_move_list(me);
+ Status = add_children_to_move_list(me, Irp);
if (!NT_SUCCESS(Status)) {
ERR("add_children_to_move_list returned %08x\n", Status);
LIST_ENTRY* le2;
if (destdir->fcb->subvol->lastinode == 0)
- get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol);
+ get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol, Irp);
me->fileref->fcb->subvol = destdir->fcb->subvol;
me->fileref->fcb->inode = ++destdir->fcb->subvol->lastinode; // FIXME - do proper function for this
me->fileref->fcb->inode_item.st_nlink = 1;
defda = get_file_attributes(me->fileref->fcb->Vcb, &me->fileref->fcb->inode_item, me->fileref->fcb->subvol, me->fileref->fcb->inode,
- me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE);
+ me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE, Irp);
me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
} else {
Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
- ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+ ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
if (!NT_SUCCESS(Status)) {
ERR("update_changed_extent_ref returned %08x\n", Status);
if (!me->parent) {
RemoveEntryList(&me->fileref->list_entry);
+ ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
free_fileref(me->fileref->parent);
+ ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
me->fileref->parent = destdir;
increase_fileref_refcount(destdir);
- Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index);
+ Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
goto end;
me->fileref->fcb->subvol->root_item.num_references++;
if (!me->dummyfileref->fcb->ads) {
- Status = delete_fileref(me->dummyfileref, NULL, rollback);
+ Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_fileref returned %08x\n", Status);
goto end;
me = CONTAINING_RECORD(le, move_entry, list_entry);
if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
- Status = delete_fileref(me->dummyfileref, NULL, rollback);
+ Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_fileref returned %08x\n", Status);
goto end;
end:
while (!IsListEmpty(&move_list)) {
+ device_extension* Vcb;
+
le = RemoveHeadList(&move_list);
me = CONTAINING_RECORD(le, move_entry, list_entry);
+ Vcb = me->fileref->fcb->Vcb;
- if (me->dummyfcb)
+ if (me->dummyfcb) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(me->dummyfcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
- if (me->dummyfileref)
+ if (me->dummyfileref) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(me->dummyfileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(me->fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
ExFreePool(me);
}
return Status;
}
-static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) {
+static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
fcb *fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
InitializeListHead(&rollback);
- // FIXME - check fri length
- // FIXME - don't ignore fri->RootDirectory
-
TRACE("tfo = %p\n", tfo);
- TRACE("ReplaceIfExists = %u\n", ReplaceIfExists);
+ TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
TRACE("RootDirectory = %p\n", fri->RootDirectory);
TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
} else {
LONG i;
+ while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
+ fnlen--;
+
+ if (fnlen == 0)
+ return STATUS_INVALID_PARAMETER;
+
for (i = fnlen - 1; i >= 0; i--) {
if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
fn = &fri->FileName[i+1];
increase_fileref_refcount(related);
}
- Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
if (NT_SUCCESS(Status)) {
TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
if (fileref != oldfileref && !oldfileref->deleted) {
- if (!ReplaceIfExists) {
+ if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
} else if ((oldfileref->fcb->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
}
}
- if (fileref == oldfileref || !oldfileref->deleted) {
+ if (fileref == oldfileref || oldfileref->deleted) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(oldfileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
oldfileref = NULL;
}
}
if (!related) {
- Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
if (!NT_SUCCESS(Status)) {
ERR("open_fileref returned %08x\n", Status);
}
if (oldfileref) {
- // FIXME - check we have permissions for this
- Status = delete_fileref(oldfileref, NULL, &rollback);
+ ACCESS_MASK access;
+ SECURITY_SUBJECT_CONTEXT subjcont;
+
+ SeCaptureSubjectContext(&subjcont);
+
+ if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
+ IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
+ SeReleaseSubjectContext(&subjcont);
+ WARN("SeAccessCheck failed, returning %08x\n", Status);
+ goto end;
+ }
+
+ SeReleaseSubjectContext(&subjcont);
+
+ Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_fileref returned %08x\n", Status);
goto end;
}
if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) {
- Status = move_across_subvols(fileref, related, &utf8, &fnus, &rollback);
+ Status = move_across_subvols(fileref, related, &utf8, &fnus, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_across_subvols returned %08x\n", Status);
}
if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
fr2->fcb->fileref = fr2;
- Status = fcb_get_last_dir_index(related->fcb, &index);
+ Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
goto end;
fr2->parent->fcb->inode_item.st_ctime = now;
fr2->parent->fcb->inode_item.st_mtime = now;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr2);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
mark_fcb_dirty(fr2->parent->fcb);
Status = STATUS_SUCCESS;
end:
- if (oldfileref)
+ if (oldfileref) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(oldfileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
- if (!NT_SUCCESS(Status) && related)
+ if (!NT_SUCCESS(Status) && related) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(related);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
- if (!NT_SUCCESS(Status) && fr2)
+ if (!NT_SUCCESS(Status) && fr2) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr2);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
fcb->adsdata.Length = end;
} else if (end > fcb->adsdata.Length) {
-// UINT16 maxlen;
-
TRACE("extending stream to %llx bytes\n", end);
-//
-// // find maximum length of xattr
-// maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
-//
-// searchkey.obj_id = fcb->inode;
-// searchkey.obj_type = TYPE_XATTR_ITEM;
-// searchkey.offset = fcb->adshash;
-//
-// Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
-// if (!NT_SUCCESS(Status)) {
-// ERR("error - find_item returned %08x\n", Status);
-// return Status;
-// }
-//
-// if (keycmp(&tp.item->key, &searchkey)) {
-// ERR("error - could not find key for xattr\n");
-// return STATUS_INTERNAL_ERROR;
-// }
-//
-// if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
-// ERR("get_xattr failed\n");
-// return STATUS_INTERNAL_ERROR;
-// }
-//
-// maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
-//
-// if (end > maxlen) {
-// ERR("error - xattr too long (%llu > %u)\n", end, maxlen);
-// return STATUS_DISK_FULL;
-// }
+
+ if (end > fcb->adsmaxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", end, fcb->adsmaxlen);
+ return STATUS_DISK_FULL;
+ }
if (end > fcb->adsdata.MaximumLength) {
char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
- Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, &rollback);
+ Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - truncate_file failed\n");
goto end;
tfofcb = tfo->FsContext;
parfcb = tfofcb;
+ while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
+ fnlen--;
+
+ if (fnlen == 0)
+ return STATUS_INVALID_PARAMETER;
+
for (i = fnlen - 1; i >= 0; i--) {
if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
fn = &fli->FileName[i+1];
increase_fileref_refcount(related);
}
- Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
if (NT_SUCCESS(Status)) {
if (!oldfileref->deleted) {
goto end;
}
} else {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(oldfileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
oldfileref = NULL;
}
}
if (!related) {
- Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
if (!NT_SUCCESS(Status)) {
ERR("open_fileref returned %08x\n", Status);
SeReleaseSubjectContext(&subjcont);
- Status = delete_fileref(oldfileref, NULL, &rollback);
+ Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_fileref returned %08x\n", Status);
goto end;
}
}
- Status = fcb_get_last_dir_index(related->fcb, &index);
+ Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
goto end;
Status = STATUS_SUCCESS;
end:
- if (oldfileref)
+ if (oldfileref) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(oldfileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
- if (!NT_SUCCESS(Status) && related)
+ if (!NT_SUCCESS(Status) && related) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(related);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
- if (!NT_SUCCESS(Status) && fr2)
+ if (!NT_SUCCESS(Status) && fr2) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr2);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
goto exit;
}
- if (Vcb->readonly) {
+ if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
Status = STATUS_MEDIA_WRITE_PROTECTED;
goto end;
}
goto end;
}
- if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
case FileRenameInformation:
TRACE("FileRenameInformation\n");
// FIXME - make this work with streams
- Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists);
+ Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
break;
case FileValidDataLengthInformation:
fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
- fbi->ChangeTime.QuadPart = 0;
+ fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
if (fcb->ads) {
if (!fileref || !fileref->parent) {
fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
- fnoi->ChangeTime.QuadPart = 0;
+ fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
if (fcb->ads) {
fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, PIRP Irp, LONG* length) {
*length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
if (fcb->ads) {
} else
ati->FileAttributes = fcb->atts;
- ati->ReparseTag = 0; // FIXME
+ if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ ati->ReparseTag = 0;
+ else
+ ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, Irp);
return STATUS_SUCCESS;
}
LIST_ENTRY list_entry;
} stream_info;
-static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, PIRP Irp, LONG* length) {
ULONG reqsize;
LIST_ENTRY streamlist, *le;
FILE_STREAM_INFORMATION *entry, *lastentry;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = 0;
- Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
}
- b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
LIST_ENTRY list_entry;
} name_bit;
-static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
+static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_ROOT_BACKREF;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
// FIXME - recursion
- Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath);
+ Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath, Irp);
if (!NT_SUCCESS(Status)) {
ERR("get_inode_dir_path returned %08x\n", Status);
return Status;
return Status;
}
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us) {
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp) {
KEY searchkey;
NTSTATUS Status;
UINT64 in;
// FIXME - start with subvol prefix
if (!subvol->path.Buffer) {
- Status = get_subvol_path(Vcb, subvol);
+ Status = get_subvol_path(Vcb, subvol, Irp);
if (!NT_SUCCESS(Status)) {
ERR("get_subvol_path returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_INODE_EXTREF;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
return Status;
}
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr) {
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
NTSTATUS Status;
fcb* fcb;
hardlink* hl;
file_ref *parfr, *fr;
- Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb);
+ Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
return Status;
if (hl->parent == inode) // root of subvol
parfr = NULL;
else {
- Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr);
+ Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fileref_by_inode returned %08x\n", Status);
free_fcb(fcb);
}
#ifndef __REACTOS__
-static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
NTSTATUS Status;
LIST_ENTRY* le;
ULONG bytes_needed;
TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
- Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+ Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fileref_by_inode returned %08x\n", Status);
goto exit;
}
- Status = fill_in_file_attribute_information(ati, fcb, fileref, &length);
+ Status = fill_in_file_attribute_information(ati, fcb, fileref, Irp, &length);
break;
}
TRACE("FileStreamInformation\n");
- Status = fill_in_file_stream_information(fsi, fileref, &length);
+ Status = fill_in_file_stream_information(fsi, fileref, Irp, &length);
break;
}
TRACE("FileHardLinkInformation\n");
- Status = fill_in_hard_link_information(fli, fileref, &length);
+ Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
break;
}
#include "btrfs_drv.h"
-#define INTERVAL 15000 // in milliseconds
-
static void do_flush(device_extension* Vcb) {
LIST_ENTRY rollback;
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
- if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ if (Vcb->need_write && !Vcb->readonly)
+ do_write(Vcb, NULL, &rollback);
free_trees(Vcb);
KeInitializeTimer(&Vcb->flush_thread_timer);
- due_time.QuadPart = -INTERVAL * 10000;
+ due_time.QuadPart = (UINT64)Vcb->options.flush_interval * -10000000;
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
// #define DEBUG_SPACE_LISTS
-static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* rollback) {
+static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
fcb* fcb;
- Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb);
+ Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
return Status;
fcb->dirty = TRUE;
if (fcb->inode_item.st_size > 0) {
- Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback);
+ Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("excise_extents returned %08x\n", Status);
return Status;
fcb->deleted = TRUE;
- flush_fcb(fcb, FALSE, rollback);
+ flush_fcb(fcb, FALSE, Irp, rollback);
free_fcb(fcb);
return STATUS_SUCCESS;
}
-NTSTATUS clear_free_space_cache(device_extension* Vcb) {
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
NTSTATUS Status;
searchkey.obj_type = 0;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
else {
LIST_ENTRY* le;
- Status = remove_free_space_inode(Vcb, fsi->key.obj_id, &rollback);
+ Status = remove_free_space_inode(Vcb, fsi->key.obj_id, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("remove_free_space_inode for (%llx,%x,%llx) returned %08x\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset, Status);
WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b)
tp = next_tp;
} while (b);
InsertTailList(list_size, &s->list_entry_size);
}
-static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
FREE_SPACE_ITEM* fsi;
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
num_entries = fsi->num_entries;
num_bitmaps = fsi->num_bitmaps;
- Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache);
+ Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
- return Status;
+ return STATUS_NOT_FOUND;
}
if (c->cache->inode_item.st_size == 0) {
free_fcb(c->cache);
c->cache = NULL;
- return Status;
+ return STATUS_NOT_FOUND;
}
if (size > c->cache->inode_item.st_size)
InitializeListHead(&rollback);
- Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, &rollback);
+ Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("excise_extents returned %08x\n", Status);
do_rollback(Vcb, &rollback);
return STATUS_NOT_FOUND;
}
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
traverse_ptr tp, next_tp;
KEY searchkey;
UINT64 lastaddr;
// LIST_ENTRY* le;
if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) {
- Status = load_stored_free_space_cache(Vcb, c);
+ Status = load_stored_free_space_cache(Vcb, c, Irp);
if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
ERR("load_stored_free_space_cache returned %08x\n", Status);
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
lastaddr = tp.item->key.obj_id + tp.item->key.offset;
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b)
tp = next_tp;
} while (b);
flags = fcb->Vcb->data_flags;
- ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+ ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
while (le != &fcb->Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
- if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
return STATUS_SUCCESS;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le->Flink;
}
- if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ if ((c = alloc_chunk(fcb->Vcb, flags))) {
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
- if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
- ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+ ExReleaseResourceLite(&c->lock);
return STATUS_SUCCESS;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
- }
-
- ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+ ExReleaseResourceLite(&c->lock);
+ } else
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
WARN("couldn't find any data chunks with %llx bytes free\n", length);
return STATUS_DISK_FULL;
}
-static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, LIST_ENTRY* rollback) {
+static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le;
NTSTATUS Status;
UINT64 num_entries, new_cache_size, i;
// create new inode
- c->cache = create_fcb();
+ c->cache = create_fcb(PagedPool);
if (!c->cache) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
c->cache->subvol = Vcb->root_root;
if (Vcb->root_root->lastinode == 0)
- get_last_inode(Vcb, Vcb->root_root);
+ get_last_inode(Vcb, Vcb->root_root, Irp);
c->cache->inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
+ ExFreePool(fsi);
+ free_fcb(c->cache);
+ c->cache = NULL;
return Status;
}
fsi->key.obj_type = TYPE_INODE_ITEM;
fsi->key.offset = 0;
- if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
free_fcb(c->cache);
c->cache = NULL;
Vcb->root_root->lastinode = c->cache->inode;
- flush_fcb(c->cache, TRUE, rollback);
+ flush_fcb(c->cache, TRUE, Irp, rollback);
*changed = TRUE;
} else if (new_cache_size > c->cache->inode_item.st_size) {
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
c->cache->inode_item.st_size = new_cache_size;
c->cache->inode_item.st_blocks = new_cache_size;
- flush_fcb(c->cache, TRUE, rollback);
+ flush_fcb(c->cache, TRUE, Irp, rollback);
*changed = TRUE;
} else {
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
RtlCopyMemory(ii, &c->cache->inode_item, sizeof(INODE_ITEM));
- if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
+
+ *changed = TRUE;
} else {
if (tp.item->size < sizeof(INODE_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
return STATUS_SUCCESS;
}
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) {
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le = Vcb->chunks_changed.Flink;
NTSTATUS Status;
BOOL b;
chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
- Status = allocate_cache_chunk(Vcb, c, &b, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+ Status = allocate_cache_chunk(Vcb, c, &b, Irp, rollback);
+ ExReleaseResourceLite(&c->lock);
if (b)
*changed = TRUE;
return STATUS_SUCCESS;
}
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+static void add_rollback_space(LIST_ENTRY* rollback, BOOL add, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c) {
+ rollback_space* rs;
+
+ rs = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_space), ALLOC_TAG);
+ if (!rs) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ rs->list = list;
+ rs->list_size = list_size;
+ rs->address = address;
+ rs->length = length;
+ rs->chunk = c;
+
+ add_rollback(rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs);
+}
+
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
LIST_ENTRY* le;
space *s, *s2;
if (list_size)
InsertTailList(list_size, &s->list_entry_size);
- // FIXME - insert rollback entry
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
return;
}
// new entry envelops old one completely
if (address <= s2->address && address + length >= s2->address + s2->size) {
if (address < s2->address) {
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+
s2->size += s2->address - address;
s2->address = address;
- // FIXME - insert rollback
while (s2->list_entry.Blink != list) {
space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
}
if (length > s2->size) {
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c);
+
s2->size = length;
- // FIXME - insert rollback
while (s2->list_entry.Flink != list) {
space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
// new entry overlaps start of old one
if (address < s2->address && address + length >= s2->address) {
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+
s2->size += s2->address - address;
s2->address = address;
- // FIXME - insert rollback
while (s2->list_entry.Blink != list) {
space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
// new entry overlaps end of old one
if (address <= s2->address + s2->size && address + length > s2->address + s2->size) {
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c);
+
s2->size = address + length - s2->address;
- // FIXME - insert rollback
while (s2->list_entry.Flink != list) {
space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
return;
}
- // FIXME - insert rollback
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
+
s->address = address;
s->size = length;
InsertHeadList(s2->list_entry.Blink, &s->list_entry);
// 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);
if (list_size)
order_space_entry(s, list_size);
- // FIXME - insert rollback
+ if (rollback)
+ add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
}
static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) {
}
}
-static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
c->cache->inode_item.sequence++;
c->cache->inode_item.st_ctime = *now;
- flush_fcb(c->cache, TRUE, rollback);
+ flush_fcb(c->cache, TRUE, Irp, rollback);
// update free_space item
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
// write cache
- Status = do_nocow_write(Vcb, c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
+ Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("do_nocow_write returned %08x\n", Status);
+ ERR("do_write_file returned %08x\n", Status);
return Status;
}
return STATUS_SUCCESS;
}
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le = Vcb->chunks_changed.Flink;
NTSTATUS Status;
chunk* c;
while (le != &Vcb->chunks_changed) {
c = CONTAINING_RECORD(le, chunk, list_entry_changed);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
- Status = update_chunk_cache(Vcb, c, &now, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+ Status = update_chunk_cache(Vcb, c, &now, Irp, rollback);
+ ExReleaseResourceLite(&c->lock);
if (!NT_SUCCESS(Status)) {
ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status);
if (!c->list_entry_changed.Flink)
InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
- _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+ _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
}
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
LIST_ENTRY *le, *le2;
space *s, *s2;
return;
if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely
- // FIXME - insert rollback
+ if (rollback)
+ add_rollback_space(rollback, FALSE, list, list_size, s2->address, s2->size, c);
+
RemoveEntryList(&s2->list_entry);
if (list_size)
ExFreePool(s2);
} else if (address + length > s2->address && address + length < s2->address + s2->size) {
if (address > s2->address) { // cut out hole
- // FIXME - insert rollback
+ if (rollback)
+ add_rollback_space(rollback, FALSE, list, list_size, address, length, c);
s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
return;
} else { // remove start of entry
+ if (rollback)
+ add_rollback_space(rollback, FALSE, list, list_size, s2->address, address + length - s2->address, c);
+
s2->size -= address + length - s2->address;
s2->address = address + length;
- // FIXME - insert rollback
if (list_size) {
RemoveEntryList(&s2->list_entry_size);
}
}
} else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry
- // FIXME - insert rollback
+ if (rollback)
+ add_rollback_space(rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c);
+
s2->size = address - s2->address;
if (list_size) {
if (!c->list_entry_changed.Flink)
InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
- _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+ _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
}
#define DOTDOT ".."
+#define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
+
extern LIST_ENTRY VcbList;
extern ERESOURCE global_loading_lock;
}
}
-static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) {
+static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
UINT8* buf;
NTSTATUS Status;
write_data_context* wtc;
return STATUS_INSUFFICIENT_RESOURCES;
}
- Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL);
+ Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, Irp);
if (!NT_SUCCESS(Status)) {
ERR("read_data returned %08x\n", Status);
goto end;
t.header.level = th->level;
t.header.tree_id = t.root->id;
- Status = get_tree_new_address(Vcb, &t, rollback);
+ Status = get_tree_new_address(Vcb, &t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("get_tree_new_address returned %08x\n", Status);
goto end;
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
if (ed2->size != 0) { // not sparse
- Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback);
+ Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("increase_extent_refcount_data returned %08x\n", Status);
internal_node* in = (internal_node*)&th[1];
for (i = 0; i < th->num_items; i++) {
- Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, rollback);
+ Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("snapshot_tree_copy returned %08x\n", Status);
wtc->tree = TRUE;
wtc->stripes_left = 0;
- Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL);
+ Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL);
if (!NT_SUCCESS(Status)) {
ERR("write_data returned %08x\n", Status);
goto end;
}
}
-static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8, PUNICODE_STRING name) {
+static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, PIRP Irp) {
LIST_ENTRY rollback;
UINT64 id;
NTSTATUS Status;
InitializeListHead(&rollback);
- ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-
// flush open files on this subvol
flush_subvol_fcbs(subvol, &rollback);
// flush metadata
if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ do_write(Vcb, Irp, &rollback);
free_trees(Vcb);
// create new root
if (Vcb->root_root->lastinode == 0)
- get_last_inode(Vcb, Vcb->root_root);
+ get_last_inode(Vcb, Vcb->root_root, Irp);
id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
- Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback);
+ Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_root returned %08x\n", Status);
TRACE("uuid root doesn't exist, creating it\n");
- Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+ Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_root returned %08x\n", Status);
searchkey.obj_type = TYPE_SUBVOL_UUID;
RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
- Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
} while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
*root_num = r->id;
- if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(root_num);
Status = STATUS_INTERNAL_ERROR;
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
- Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback);
+ Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("snapshot_tree_copy returned %08x\n", Status);
goto end;
searchkey.obj_type = 0;
searchkey.offset = 0;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length);
- Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb);
+ Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
free_fileref(fr);
goto end;
}
- Status = fcb_get_last_dir_index(fcb, &dirpos);
+ Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
free_fileref(fr);
le = le->Flink;
}
- do_write(Vcb, &rollback);
+ do_write(Vcb, Irp, &rollback);
free_trees(Vcb);
else
do_rollback(Vcb, &rollback);
- ExReleaseResourceLite(&Vcb->tree_lock);
-
return Status;
}
-static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
+static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
PFILE_OBJECT subvol_obj;
NTSTATUS Status;
btrfs_create_snapshot* bcs = data;
ANSI_STRING utf8;
UNICODE_STRING nameus;
ULONG len;
- UINT32 crc32;
fcb* fcb;
ccb* ccb;
- file_ref* fileref;
+ file_ref *fileref, *fr2;
if (length < offsetof(btrfs_create_snapshot, name))
return STATUS_INVALID_PARAMETER;
ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
goto end2;
}
+
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
+ // no need for fcb_lock as we have tree_lock exclusively
+ Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp);
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
-
- Status = find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL);
-
if (NT_SUCCESS(Status)) {
- WARN("file already exists\n");
- Status = STATUS_OBJECT_NAME_COLLISION;
- goto end2;
+ if (!fr2->deleted) {
+ WARN("file already exists\n");
+ free_fileref(fr2);
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end3;
+ } else
+ free_fileref(fr2);
} else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
- ERR("find_file_in_dir_with_crc32 returned %08x\n", Status);
- goto end2;
+ ERR("open_fileref returned %08x\n", Status);
+ goto end3;
}
Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL);
if (!NT_SUCCESS(Status)) {
ERR("ObReferenceObjectByHandle returned %08x\n", Status);
- goto end2;
+ goto end3;
}
subvol_fcb = subvol_obj->FsContext;
goto end;
}
- Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8, &nameus);
+ Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, Irp);
if (NT_SUCCESS(Status)) {
file_ref* fr;
- Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL);
+ Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL, NULL, Irp);
+
if (!NT_SUCCESS(Status)) {
ERR("open_fileref returned %08x\n", Status);
Status = STATUS_SUCCESS;
end:
ObDereferenceObject(subvol_obj);
+end3:
+ ExReleaseResourceLite(&Vcb->tree_lock);
+
end2:
ExFreePool(utf8.Buffer);
return Status;
}
-static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) {
+static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length, PIRP Irp) {
fcb *fcb, *rootfcb;
ccb* ccb;
file_ref* fileref;
UNICODE_STRING nameus;
ANSI_STRING utf8;
UINT64 dirpos;
- UINT32 crc32;
INODE_REF* ir;
KEY searchkey;
traverse_ptr tp;
PSID owner;
BOOLEAN defaulted;
UINT64* root_num;
- file_ref* fr;
+ file_ref *fr = NULL, *fr2;
fcb = FileObject->FsContext;
if (!fcb) {
InitializeListHead(&rollback);
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
-
- Status = find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL);
+ // no need for fcb_lock as we have tree_lock exclusively
+ Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp);
if (NT_SUCCESS(Status)) {
- WARN("file already exists\n");
- Status = STATUS_OBJECT_NAME_COLLISION;
- goto end;
+ if (!fr2->deleted) {
+ WARN("file already exists\n");
+ free_fileref(fr2);
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end;
+ } else
+ free_fileref(fr2);
} else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
- ERR("find_file_in_dir_with_crc32 returned %08x\n", Status);
+ ERR("open_fileref returned %08x\n", Status);
goto end;
}
if (Vcb->root_root->lastinode == 0)
- get_last_inode(Vcb, Vcb->root_root);
+ get_last_inode(Vcb, Vcb->root_root, Irp);
// FIXME - make sure rollback removes new roots from internal structures
id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
- Status = create_root(Vcb, id, &r, FALSE, 0, &rollback);
+ Status = create_root(Vcb, id, &r, FALSE, 0, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_root returned %08x\n", Status);
TRACE("uuid root doesn't exist, creating it\n");
- Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+ Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_root returned %08x\n", Status);
searchkey.obj_type = TYPE_SUBVOL_UUID;
RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
- Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
} while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
*root_num = r->id;
- if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
ERR("insert_tree_item failed\n");
Status = STATUS_INTERNAL_ERROR;
goto end;
// add .. inode to new subvol
- rootfcb = create_fcb();
+ rootfcb = create_fcb(PagedPool);
if (!rootfcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
rootfcb->inode_item.st_gid = GID_NOBODY; // FIXME?
- rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE);
+ rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE, Irp);
SeCaptureSubjectContext(&subjcont);
ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
rootfcb->inode_item.st_uid = UID_NOBODY;
} else {
- rootfcb->inode_item.st_uid = sid_to_uid(&owner);
+ rootfcb->inode_item.st_uid = sid_to_uid(owner);
}
rootfcb->sd_dirty = TRUE;
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
InsertTailList(&r->fcbs, &rootfcb->list_entry);
InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
rootfcb->Header.AllocationSize.QuadPart = 0;
ir->n = strlen(DOTDOT);
RtlCopyMemory(ir->name, DOTDOT, ir->n);
- if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, &rollback)) {
+ if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp, &rollback)) {
ERR("insert_tree_item failed\n");
Status = STATUS_INTERNAL_ERROR;
goto end;
fr = create_fileref();
if (!fr) {
ERR("out of memory\n");
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fcb(rootfcb);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
mark_fcb_dirty(rootfcb);
- Status = fcb_get_last_dir_index(fcb, &dirpos);
+ Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_last_dir_index returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto end;
}
fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
if (!fr->filepart.Buffer) {
ERR("out of memory\n");
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto end;
}
}
end2:
- if (fr)
+ if (fr) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
free_fileref(fr);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
+
+ return Status;
+}
+
+static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
+ btrfs_inode_info* bii = data;
+ fcb* fcb;
+ ccb* ccb;
+
+ if (length < sizeof(btrfs_inode_info))
+ return STATUS_BUFFER_OVERFLOW;
+
+ if (!FileObject)
+ return STATUS_INVALID_PARAMETER;
+
+ fcb = FileObject->FsContext;
+
+ if (!fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ ccb = FileObject->FsContext2;
+
+ if (!ccb)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
+
+ bii->subvol = fcb->subvol->id;
+ bii->inode = fcb->inode;
+ bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
+ bii->type = fcb->type;
+ bii->st_uid = fcb->inode_item.st_uid;
+ bii->st_gid = fcb->inode_item.st_gid;
+ bii->st_mode = fcb->inode_item.st_mode;
+ bii->st_rdev = fcb->inode_item.st_rdev;
+ bii->flags = fcb->inode_item.flags;
+
+ bii->inline_length = 0;
+ bii->disk_size[0] = 0;
+ bii->disk_size[1] = 0;
+ bii->disk_size[2] = 0;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ LIST_ENTRY* le;
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+ if (!ext->ignore) {
+ if (ext->data->type == EXTENT_TYPE_INLINE) {
+ bii->inline_length += ext->data->decoded_size;
+ } else {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+
+ // FIXME - compressed extents with a hole in them are counted more than once
+ if (ed2->size != 0) {
+ if (ext->data->compression == BTRFS_COMPRESSION_NONE) {
+ bii->disk_size[0] += ed2->num_bytes;
+ } else if (ext->data->compression == BTRFS_COMPRESSION_ZLIB) {
+ bii->disk_size[1] += ed2->size;
+ } else if (ext->data->compression == BTRFS_COMPRESSION_LZO) {
+ bii->disk_size[2] += ed2->size;
+ }
+ }
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ ExReleaseResourceLite(fcb->Header.Resource);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
+ btrfs_set_inode_info* bsii = data;
+ NTSTATUS Status;
+ fcb* fcb;
+ ccb* ccb;
+
+ if (length < sizeof(btrfs_set_inode_info))
+ return STATUS_BUFFER_OVERFLOW;
+
+ if (!FileObject)
+ return STATUS_INVALID_PARAMETER;
+
+ fcb = FileObject->FsContext;
+
+ if (!fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ ccb = FileObject->FsContext2;
+
+ if (!ccb)
+ return STATUS_INVALID_PARAMETER;
+
+ if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (bsii->mode_changed && !(ccb->access & WRITE_DAC)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if ((bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_OWNER)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+
+ if (bsii->flags_changed) {
+ if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
+ (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
+ WARN("trying to change nocow flag on non-empty file\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ fcb->inode_item.flags = bsii->flags;
+
+ if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
+ fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+ else
+ fcb->inode_item.flags &= ~(UINT64)BTRFS_INODE_NODATASUM;
+ }
+
+ if (bsii->mode_changed) {
+ UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
+
+ fcb->inode_item.st_mode &= ~allowed;
+ fcb->inode_item.st_mode |= bsii->st_mode & allowed;
+ }
+
+ if (bsii->uid_changed) {
+ PSID sid;
+ SECURITY_INFORMATION secinfo;
+ SECURITY_DESCRIPTOR sd;
+ void* oldsd;
+
+ fcb->inode_item.st_uid = bsii->st_uid;
+
+ uid_to_sid(bsii->st_uid, &sid);
+
+ Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = RtlSetOwnerSecurityDescriptor(&sd, sid, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+ goto end;
+ }
+
+ oldsd = fcb->sd;
+
+ secinfo = OWNER_SECURITY_INFORMATION;
+ Status = SeSetSecurityDescriptorInfoEx(NULL, &secinfo, &sd, (void**)&fcb->sd, SEF_AVOID_PRIVILEGE_CHECK, PagedPool, IoGetFileObjectGenericMapping());
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
+ goto end;
+ }
+
+ ExFreePool(oldsd);
+
+ fcb->sd_dirty = TRUE;
+
+ send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED);
+ }
+
+ if (bsii->gid_changed)
+ fcb->inode_item.st_gid = bsii->st_gid;
+
+ if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed)
+ mark_fcb_dirty(fcb);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ ExReleaseResourceLite(fcb->Header.Resource);
return Status;
}
return STATUS_SUCCESS;
}
-static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
+static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
FILE_SET_SPARSE_BUFFER* fssb = data;
NTSTATUS Status;
BOOL set;
fcb->atts_changed = TRUE;
defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type,
- fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+ fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
fcb->atts_deleted = defda == fcb->atts;
}
}
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;
+ NTSTATUS Status;
+ BOOL compress = write_fcb_compressed(fcb);
+ UINT64 start_data, end_data;
+ UINT8* data;
+
+ if (compress) {
+ start_data = start & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
+ end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE),
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size));
+ } else {
+ start_data = start & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
+ end_data = sector_align(start + length, fcb->Vcb->superblock.sector_size);
+ }
+
+ data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
+ if (!data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- le = fcb->extents.Flink;
+ RtlZeroMemory(data, end_data - start_data);
- while (le != &fcb->extents) {
- LIST_ENTRY* le2 = le->Flink;
- extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+ if (start > start_data || start + length < end_data) {
+ Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp);
- 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;
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ }
+
+ RtlZeroMemory(data + start - start_data, length);
+
+ if (compress) {
+ Status = write_compressed(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
+
+ ExFreePool(data);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_compressed returned %08x\n", Status);
+ return Status;
}
+ } else {
+ Status = do_write_file(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
- le = le2;
+ ExFreePool(data);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_file returned %08x\n", Status);
+ return Status;
+ }
}
return STATUS_SUCCESS;
BOOL nocsum;
IO_STATUS_BLOCK iosb;
- // FIXME - check permissions
-
if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
return STATUS_INVALID_PARAMETER;
}
ccb = FileObject->FsContext2;
- fileref = ccb ? ccb->fileref : NULL;
+
+ if (!ccb) {
+ ERR("ccb was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(ccb->access & FILE_WRITE_DATA)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ fileref = ccb->fileref;
if (!fileref) {
ERR("fileref was NULL\n");
}
if (end < fzdi->BeyondFinalZero.QuadPart) {
- Status = zero_data(Vcb, fcb, fzdi->BeyondFinalZero.QuadPart, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback);
+ Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("zero_data returned %08x\n", Status);
goto end;
}
if (end > start) {
- Status = excise_extents(Vcb, fcb, start, end, &rollback);
+ Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("excise_extents returned %08x\n", Status);
goto end;
}
-
- Status = insert_sparse_extent(fcb, start, end - start, &rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- goto end;
- }
}
}
}
LIST_ENTRY* le;
FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
ULONG i = 0;
- BOOL sparse, finish_off;
- UINT64 nonsparse_run_start;
+ UINT64 last_start, last_end;
TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
le = fcb->extents.Flink;
- sparse = FALSE;
- nonsparse_run_start = 0xffffffffffffffff;
- finish_off = TRUE;
+ last_start = 0;
+ last_end = 0;
while (le != &fcb->extents) {
extent* ext = CONTAINING_RECORD(le, extent, list_entry);
if (!ext->ignore) {
- EXTENT_DATA2* ed2 = ext->data->type == EXTENT_TYPE_REGULAR ? (EXTENT_DATA2*)ext->data->data : NULL;
+ EXTENT_DATA2* ed2 = (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->data->data : NULL;
+ UINT64 len = ed2 ? ed2->num_bytes : ext->data->decoded_size;
- if (ed2 && ed2->size == 0) { // sparse
- if (!sparse) {
- if (nonsparse_run_start < ext->offset) {
- if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
- ranges[i].FileOffset.QuadPart = nonsparse_run_start;
- ranges[i].Length.QuadPart = ext->offset - nonsparse_run_start;
- i++;
- } else {
- Status = STATUS_BUFFER_TOO_SMALL;
- goto end;
- }
- }
-
- if (ext->offset > inbuf->FileOffset.QuadPart + inbuf->Length.QuadPart) {
- finish_off = FALSE;
- break;
+ if (ext->offset > last_end) { // first extent after a hole
+ if (last_end > last_start) {
+ if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
+ ranges[i].FileOffset.QuadPart = last_start;
+ ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
+ i++;
+ } else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto end;
}
-
- sparse = TRUE;
- }
- } else { // not sparse
- if (sparse) {
- sparse = FALSE;
- nonsparse_run_start = ext->offset;
}
+
+ last_start = ext->offset;
}
+
+ last_end = ext->offset + len;
}
le = le->Flink;
}
- if (finish_off && nonsparse_run_start < fcb->inode_item.st_size) {
+ if (last_end > last_start) {
if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
- ranges[i].FileOffset.QuadPart = nonsparse_run_start;
- ranges[i].Length.QuadPart = fcb->inode_item.st_size - nonsparse_run_start;
+ ranges[i].FileOffset.QuadPart = last_start;
+ ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
i++;
} else {
Status = STATUS_BUFFER_TOO_SMALL;
flush_fcb_caches(Vcb);
- if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ if (Vcb->need_write && !Vcb->readonly)
+ do_write(Vcb, Irp, &rollback);
free_trees(Vcb);
while (le != &VcbList) {
device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
- if (Vcb->Vpb->RealDevice == devobj) {
+ if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
if (Vcb->Vpb == devobj->Vpb) {
KIRQL irql;
PVPB newvpb;
flush_fcb_caches(Vcb);
- if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ if (Vcb->need_write && !Vcb->readonly)
+ do_write(Vcb, Irp, &rollback);
free_trees(Vcb);
return Status;
}
+static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG* volstate;
+
+ if (Irp->AssociatedIrp.SystemBuffer) {
+ volstate = Irp->AssociatedIrp.SystemBuffer;
+ } else if (Irp->MdlAddress != NULL) {
+ volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
+
+ if (!volstate)
+ return STATUS_INSUFFICIENT_RESOURCES;
+ } else
+ return STATUS_INVALID_USER_BUFFER;
+
+ if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
+ return STATUS_INVALID_PARAMETER;
+
+ *volstate = 0;
+
+ if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
+ return STATUS_INVALID_PARAMETER;
+
+ Irp->IoStatus.Information = sizeof(ULONG);
+
+ return STATUS_SUCCESS;
+}
+
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status;
break;
case FSCTL_IS_VOLUME_DIRTY:
- WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp);
break;
case FSCTL_ALLOW_EXTENDED_DASD_IO:
case FSCTL_SET_SPARSE:
Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
- IrpSp->Parameters.FileSystemControl.InputBufferLength);
+ IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
break;
case FSCTL_SET_ZERO_DATA:
break;
case FSCTL_BTRFS_CREATE_SUBVOL:
- Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+ Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
break;
case FSCTL_BTRFS_CREATE_SNAPSHOT:
- Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+ Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
+ break;
+
+ case FSCTL_BTRFS_GET_INODE_INFO:
+ Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+ break;
+
+ case FSCTL_BTRFS_SET_INODE_INFO:
+ Status = set_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
break;
default:
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
#include "btrfs_drv.h"
struct pnp_context;
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
- if (Vcb->need_write)
- do_write(Vcb, &rollback);
+ if (Vcb->need_write && !Vcb->readonly)
+ do_write(Vcb, Irp, &rollback);
clear_rollback(&rollback);
}
static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
- FIXME("STUB\n");
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
+ uninit(Vcb, FALSE);
+ Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+ }
return STATUS_SUCCESS;
}
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
#include "btrfs_drv.h"
enum read_data_status {
UINT64 *stripestart = NULL, *stripeend = NULL;
UINT16 startoffstripe;
+ Status = verify_vcb(Vcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("verify_vcb returned %08x\n", Status);
+ return Status;
+ }
+
if (Vcb->log_to_phys_loaded) {
chunk* c = get_chunk_from_address(Vcb, addr);
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
- IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
+ if (Irp && context->stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) {
+ PDEVICE_OBJECT dev;
+
+ dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+ IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+
+ if (!dev) {
+ dev = IoGetDeviceToVerify(PsGetCurrentThread());
+ IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+ }
+
+ dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+
+ if (dev)
+ IoVerifyVolume(dev, FALSE);
+ }
+// IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
Status = context->stripes[i].iosb.Status;
goto exit;
return Status;
}
-static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length) {
+static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp, next_tp;
searchkey.obj_type = TYPE_EXTENT_CSUM;
searchkey.offset = start;
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
break;
}
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
if (b)
tp = next_tp;
return STATUS_SUCCESS;
}
-static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum) {
+static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum, PIRP Irp) {
UINT32* csum = NULL;
NTSTATUS Status;
UINT64 end;
runlength = RtlFindFirstRunClear(&bmp, &index);
while (runlength != 0) {
- Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength);
+ Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_csum_from_disk returned %08x\n", Status);
goto end;
NTSTATUS Status;
EXTENT_DATA* ed;
UINT64 bytes_read = 0;
+ UINT64 last_end;
LIST_ENTRY* le;
TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
if (pbr)
*pbr = 0;
-
- le = fcb->extents.Flink;
- while (le != &fcb->extents) {
- extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!ext->ignore) {
- if (ext->offset == start)
- break;
- else if (ext->offset > start) {
- LIST_ENTRY* le2 = le->Blink;
-
- ext = NULL;
-
- while (le2 != &fcb->extents) {
- extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-
- if (!ext2->ignore) {
- le = le2;
- ext = ext2;
- break;
- }
-
- le2 = le2->Blink;
- }
-
- if (!ext) {
- ERR("first extent was after offset\n");
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
- } else
- break;
- }
- }
-
- le = le->Flink;
+ if (start >= fcb->inode_item.st_size) {
+ WARN("Tried to read beyond end of file\n");
+ Status = STATUS_END_OF_FILE;
+ goto exit;
}
-
- if (le == &fcb->extents) {
- LIST_ENTRY* le2 = le->Blink;
- extent* ext = NULL;
- while (le2 != &fcb->extents) {
- extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-
- if (!ext2->ignore) {
- le = le2;
- ext = ext2;
- break;
- }
-
- le2 = le2->Blink;
- }
-
- if (!ext) {
- ERR("could not find extent\n");
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
- }
- }
+ le = fcb->extents.Flink;
+
+ last_end = start;
while (le != &fcb->extents) {
UINT64 len;
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;
+ ed2 = (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ed->data : NULL;
- len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+ len = ed2 ? ed2->num_bytes : ed->decoded_size;
- if (ext->offset + len < start) {
- ERR("Tried to read beyond end of file\n");
- Status = STATUS_END_OF_FILE;
- goto exit;
+ if (ext->offset + len <= start) {
+ last_end = ext->offset + len;
+ goto nextitem;
}
- if (ed->compression != BTRFS_COMPRESSION_NONE) {
- FIXME("FIXME - compression not yet supported\n");
- Status = STATUS_NOT_IMPLEMENTED;
- goto exit;
+ if (ext->offset > last_end && ext->offset > start + bytes_read) {
+ UINT32 read = min(length, ext->offset - max(start, last_end));
+
+ RtlZeroMemory(data + bytes_read, read);
+ bytes_read += read;
+ length -= read;
}
+ if (length == 0 || ext->offset > start + bytes_read + length)
+ break;
+
if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
WARN("Encryption not supported\n");
Status = STATUS_NOT_IMPLEMENTED;
RtlCopyMemory(data + bytes_read, &ed->data[off], read);
+ // FIXME - can we have compressed inline extents?
+
bytes_read += read;
length -= read;
break;
UINT64 off = start + bytes_read - ext->offset;
UINT32 to_read, read;
UINT8* buf;
+ UINT32 *csum, bumpoff = 0;
+ UINT64 addr;
read = len - off;
if (read > length) read = length;
- if (ed2->address == 0) {
- RtlZeroMemory(data + bytes_read, read);
- } else {
- UINT32 *csum, bumpoff = 0;
- UINT64 addr;
-
+ if (ed->compression == BTRFS_COMPRESSION_NONE) {
addr = ed2->address + ed2->offset + off;
to_read = sector_align(read, fcb->Vcb->superblock.sector_size);
addr -= bumpoff;
to_read = sector_align(read + bumpoff, fcb->Vcb->superblock.sector_size);
}
+ } else {
+ addr = ed2->address;
+ to_read = sector_align(ed2->size, fcb->Vcb->superblock.sector_size);
+ }
+
+ buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+
+ if (!buf) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+ Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum, Irp);
- buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_csum returned %08x\n", Status);
+ ExFreePool(buf);
+ goto exit;
+ }
+ } else
+ csum = NULL;
+
+ Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_data returned %08x\n", Status);
+ ExFreePool(buf);
+ goto exit;
+ }
+
+ if (ed->compression == BTRFS_COMPRESSION_NONE) {
+ RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+ } else {
+ UINT8* decomp = NULL;
+
+ // FIXME - don't mess around with decomp if we're reading the whole extent
- if (!buf) {
+ decomp = ExAllocatePoolWithTag(PagedPool, ed->decoded_size, ALLOC_TAG);
+ if (!decomp) {
ERR("out of memory\n");
+ ExFreePool(buf);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
- if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
- Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum);
-
- if (!NT_SUCCESS(Status)) {
- ERR("load_csum returned %08x\n", Status);
- ExFreePool(buf);
- goto exit;
- }
- } else
- csum = NULL;
+ Status = decompress(ed->compression, buf, ed2->size, decomp, ed->decoded_size);
- Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
if (!NT_SUCCESS(Status)) {
- ERR("read_data returned %08x\n", Status);
+ ERR("decompress returned %08x\n", Status);
ExFreePool(buf);
+ ExFreePool(decomp);
goto exit;
}
- RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+ RtlCopyMemory(data + bytes_read, decomp + ed2->offset + off, min(read, ed2->num_bytes - off));
- ExFreePool(buf);
-
- if (csum)
- ExFreePool(csum);
+ ExFreePool(decomp);
}
+ ExFreePool(buf);
+
+ if (csum)
+ ExFreePool(csum);
+
bytes_read += read;
length -= read;
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
+
+ last_end = ext->offset + len;
if (length == 0)
break;
}
+nextitem:
le = le->Flink;
}
+ if (length > 0 && start + bytes_read < fcb->inode_item.st_size) {
+ UINT32 read = min(fcb->inode_item.st_size - start - bytes_read, length);
+
+ RtlZeroMemory(data + bytes_read, read);
+
+ bytes_read += read;
+ length -= read;
+ }
+
Status = STATUS_SUCCESS;
if (pbr)
*pbr = bytes_read;
}
if (start >= fcb->Header.ValidDataLength.QuadPart) {
- length = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart;
+ length = min(length, min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart);
RtlZeroMemory(data, length);
Irp->IoStatus.Information = *bytes_read = length;
return STATUS_SUCCESS;
BOOL top_level;
fcb* fcb;
ccb* ccb;
- BOOL tree_lock = FALSE, fcb_lock = FALSE;
+ BOOL tree_lock = FALSE, fcb_lock = FALSE, pagefile;
FsRtlEnterFileSystem();
goto exit;
}
+ if (fcb == Vcb->volume_fcb) {
+ TRACE("not allowing read of volume FCB\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
ccb = FileObject->FsContext2;
if (!ccb) {
goto exit;
}
+ pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && Irp->Flags & IRP_PAGING_IO;
+
if (Irp->Flags & IRP_NOCACHE) {
- if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
- Status = STATUS_PENDING;
- IoMarkIrpPending(Irp);
- goto exit;
+ if (!pagefile) {
+ if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
+ Status = STATUS_PENDING;
+ IoMarkIrpPending(Irp);
+ goto exit;
+ }
+
+ tree_lock = TRUE;
}
-
- tree_lock = TRUE;
if (!ExAcquireResourceSharedLite(fcb->Header.Resource, IoIsOperationSynchronous(Irp))) {
Status = STATUS_PENDING;
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
#include "btrfs_drv.h"
extern UNICODE_STRING log_device, log_file, registry_path;
static WCHAR option_mounted[] = L"Mounted";
-static WCHAR option_ignore[] = L"Ignore";
#define hex_digit(c) ((c) >= 0 && (c) <= 9) ? ((c) + '0') : ((c) - 10 + 'a')
-NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) {
- UNICODE_STRING path, ignoreus;
+NTSTATUS registry_load_volume_options(device_extension* Vcb) {
+ BTRFS_UUID* uuid = &Vcb->superblock.uuid;
+ mount_options* options = &Vcb->options;
+ UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
+ maxinlineus, subvolidus;
OBJECT_ATTRIBUTES oa;
NTSTATUS Status;
ULONG i, j, kvfilen, index, retlen;
KEY_VALUE_FULL_INFORMATION* kvfi = NULL;
HANDLE h;
+ options->compress = mount_compress;
+ options->compress_force = mount_compress_force;
+ options->compress_type = mount_compress_type > BTRFS_COMPRESSION_LZO ? 0 : mount_compress_type;
+ options->readonly = FALSE;
+ options->zlib_level = mount_zlib_level;
+ options->flush_interval = mount_flush_interval;
+ options->max_inline = min(mount_max_inline, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
+ options->subvol_id = 0;
+
path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
index = 0;
- ignoreus.Buffer = option_ignore;
- ignoreus.Length = ignoreus.MaximumLength = wcslen(option_ignore) * sizeof(WCHAR);
+ RtlInitUnicodeString(&ignoreus, L"Ignore");
+ RtlInitUnicodeString(&compressus, L"Compress");
+ RtlInitUnicodeString(&compressforceus, L"CompressForce");
+ RtlInitUnicodeString(&compresstypeus, L"CompressType");
+ RtlInitUnicodeString(&readonlyus, L"Readonly");
+ RtlInitUnicodeString(&zliblevelus, L"ZlibLevel");
+ RtlInitUnicodeString(&flushintervalus, L"FlushInterval");
+ RtlInitUnicodeString(&maxinlineus, L"MaxInline");
+ RtlInitUnicodeString(&subvolidus, L"SubvolId");
do {
Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
options->ignore = *val != 0 ? TRUE : FALSE;
+ } else if (FsRtlAreNamesEqual(&compressus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->compress = *val != 0 ? TRUE : FALSE;
+ } else if (FsRtlAreNamesEqual(&compressforceus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->compress_force = *val != 0 ? TRUE : FALSE;
+ } else if (FsRtlAreNamesEqual(&compresstypeus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->compress_type = *val > BTRFS_COMPRESSION_LZO ? 0 : *val;
+ } else if (FsRtlAreNamesEqual(&readonlyus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->readonly = *val != 0 ? TRUE : FALSE;
+ } else if (FsRtlAreNamesEqual(&zliblevelus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->zlib_level = *val;
+ } else if (FsRtlAreNamesEqual(&flushintervalus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->flush_interval = *val;
+ } else if (FsRtlAreNamesEqual(&maxinlineus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+ DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->max_inline = min(*val, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
+ } else if (FsRtlAreNamesEqual(&subvolidus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_QWORD) {
+ UINT64* val = (UINT64*)((UINT8*)kvfi + kvfi->DataOffset);
+
+ options->subvol_id = *val;
}
} else if (Status != STATUS_NO_MORE_ENTRIES) {
ERR("ZwEnumerateValueKey returned %08x\n", Status);
goto end2;
}
} while (NT_SUCCESS(Status));
+
+ if (!options->compress && options->compress_force)
+ options->compress = TRUE;
+
+ if (options->zlib_level > 9)
+ options->zlib_level = 9;
+
+ if (options->flush_interval == 0)
+ options->flush_interval = mount_flush_interval;
Status = STATUS_SUCCESS;
ExFreePool(path);
}
-void STDCALL read_registry(PUNICODE_STRING regpath) {
-#ifndef __REACTOS__
- UNICODE_STRING us;
-#endif
- OBJECT_ATTRIBUTES oa;
- NTSTATUS Status;
- HANDLE h;
- ULONG dispos;
-#ifndef __REACTOS__
+static void get_registry_value(HANDLE h, WCHAR* string, ULONG type, void* val, ULONG size) {
ULONG kvfilen;
KEY_VALUE_FULL_INFORMATION* kvfi;
-#endif
-
-#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;
- }
+ UNICODE_STRING us;
+ NTSTATUS Status;
- reset_subkeys(h, regpath);
-
-#ifdef _DEBUG
- RtlInitUnicodeString(&us, L"DebugLogLevel");
+ RtlInitUnicodeString(&us, string);
kvfi = NULL;
kvfilen = 0;
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if (NT_SUCCESS(Status)) {
- if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) {
- RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32));
+ if (kvfi->Type == type && kvfi->DataLength >= size) {
+ RtlCopyMemory(val, ((UINT8*)kvfi) + kvfi->DataOffset, size);
} else {
Status = ZwDeleteValueKey(h, &us);
if (!NT_SUCCESS(Status)) {
ERR("ZwDeleteValueKey returned %08x\n", Status);
}
- Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
+ Status = ZwSetValueKey(h, &us, 0, type, val, size);
if (!NT_SUCCESS(Status)) {
- ERR("ZwSetValueKey reutrned %08x\n", Status);
+ ERR("ZwSetValueKey returned %08x\n", Status);
}
}
}
ExFreePool(kvfi);
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
+ Status = ZwSetValueKey(h, &us, 0, type, val, size);
if (!NT_SUCCESS(Status)) {
- ERR("ZwSetValueKey reutrned %08x\n", Status);
+ ERR("ZwSetValueKey returned %08x\n", Status);
}
} else {
ERR("ZwQueryValueKey returned %08x\n", Status);
}
+}
+
+void STDCALL read_registry(PUNICODE_STRING regpath) {
+#ifndef __REACTOS__
+ UNICODE_STRING us;
+#endif
+ OBJECT_ATTRIBUTES oa;
+ NTSTATUS Status;
+ HANDLE h;
+ ULONG dispos;
+#ifndef __REACTOS__
+ ULONG kvfilen;
+ KEY_VALUE_FULL_INFORMATION* kvfi;
+#endif
+
+#ifndef __REACTOS__
+ static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
+#endif
+
+ read_mappings(regpath);
+
+ InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwCreateKey returned %08x\n", Status);
+ return;
+ }
+
+ reset_subkeys(h, regpath);
+
+ get_registry_value(h, L"Compress", REG_DWORD, &mount_compress, sizeof(mount_compress));
+ get_registry_value(h, L"CompressForce", REG_DWORD, &mount_compress_force, sizeof(mount_compress_force));
+ get_registry_value(h, L"CompressType", REG_DWORD, &mount_compress_type, sizeof(mount_compress_type));
+ get_registry_value(h, L"ZlibLevel", REG_DWORD, &mount_zlib_level, sizeof(mount_zlib_level));
+ get_registry_value(h, L"FlushInterval", REG_DWORD, &mount_flush_interval, sizeof(mount_flush_interval));
+ get_registry_value(h, L"MaxInline", REG_DWORD, &mount_max_inline, sizeof(mount_max_inline));
+
+ if (mount_flush_interval == 0)
+ mount_flush_interval = 1;
+
+#ifdef _DEBUG
+ get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level));
RtlInitUnicodeString(&us, L"LogDevice");
char* data;
NTSTATUS Status;
- // FIXME - check permissions
-
TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
NTSTATUS Status;
ULONG minlen;
+ ULONG tlength;
UNICODE_STRING subname;
ANSI_STRING target;
LARGE_INTEGER offset, time;
fileref->fcb->inode_item.st_mode |= __S_IFLNK;
- Status = truncate_file(fileref->fcb, 0, rollback);
+ Status = truncate_file(fileref->fcb, 0, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
return Status;
}
offset.QuadPart = 0;
- Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, FALSE, TRUE,
+ tlength = target.Length;
+ Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, &tlength, FALSE, TRUE,
TRUE, FALSE, rollback);
ExFreePool(target.Buffer);
ULONG tag;
LIST_ENTRY rollback;
- // FIXME - send notification if this succeeds? The attributes will have changed.
- // FIXME - check permissions
-
TRACE("(%p, %p)\n", DeviceObject, Irp);
InitializeListHead(&rollback);
return STATUS_INVALID_PARAMETER;
}
+ // It isn't documented what permissions FSCTL_SET_REPARSE_POINT needs, but CreateSymbolicLinkW
+ // creates a file with FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE.
+ if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
fileref = ccb->fileref;
if (!fileref) {
Status = STATUS_SUCCESS;
} else { // otherwise, store as file data
- Status = truncate_file(fcb, 0, &rollback);
+ Status = truncate_file(fcb, 0, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
file_ref* fileref;
LIST_ENTRY rollback;
- // FIXME - check permissions
-
TRACE("(%p, %p)\n", DeviceObject, Irp);
InitializeListHead(&rollback);
}
fcb = FileObject->FsContext;
+
+ if (!fcb) {
+ ERR("fcb was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
ccb = FileObject->FsContext2;
- fileref = ccb ? ccb->fileref : NULL;
- ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
- ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+ if (!ccb) {
+ ERR("ccb was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
- TRACE("%S\n", file_desc(FileObject));
+ if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ fileref = ccb->fileref;
if (!fileref) {
ERR("fileref was NULL\n");
goto end;
}
+ ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+ ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+
+ TRACE("%S\n", file_desc(FileObject));
+
if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
ERR("buffer was too short\n");
Status = STATUS_INVALID_PARAMETER;
// FIXME - do we need to check that the reparse tags match?
- Status = truncate_file(fcb, 0, &rollback);
+ Status = truncate_file(fcb, 0, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
if (!um) {
ERR("out of memory\n");
+ ExFreePool(sid);
return;
}
InsertTailList(&uid_map_list, &um->listentry);
}
-static void uid_to_sid(UINT32 uid, PSID* sid) {
+void uid_to_sid(UINT32 uid, PSID* sid) {
LIST_ENTRY* le;
uid_map* um;
sid_header* sh;
// }
// }
-static BOOL get_sd_from_xattr(fcb* fcb) {
+static BOOL get_sd_from_xattr(fcb* fcb, PIRP Irp) {
ULONG buflen;
NTSTATUS Status;
PSID sid, usersid;
- if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen))
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen, Irp))
return FALSE;
TRACE("using xattr " EA_NTACL " for security descriptor\n");
ExFreePool(groupsid);
}
-void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
+void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp) {
NTSTATUS Status;
PSID usersid = NULL, groupsid = NULL;
SECURITY_SUBJECT_CONTEXT subjcont;
- if (get_sd_from_xattr(fcb))
+ if (get_sd_from_xattr(fcb, Irp))
return;
if (!parent) {
fcb = fileref->parent->fcb;
else {
ERR("could not find parent fcb for stream\n");
- Status = STATUS_INTERNAL_ERROR;
- goto end;
+ return STATUS_INTERNAL_ERROR;
}
}
ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
fcb->inode_item.st_uid = UID_NOBODY;
} else {
- fcb->inode_item.st_uid = sid_to_uid(&owner);
+ fcb->inode_item.st_uid = sid_to_uid(owner);
}
return STATUS_SUCCESS;
LIST_ENTRY list_entry;
} rollback_item;
-NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line) {
UINT8* buf;
NTSTATUS Status;
tree_header* th;
return STATUS_INSUFFICIENT_RESOURCES;
}
- Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, NULL);
+ Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, Irp);
if (!NT_SUCCESS(Status)) {
ERR("read_data returned 0x%08x\n", Status);
ExFreePool(buf);
return NULL;
}
-NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp,
+ const char* func, const char* file, unsigned int line) {
// KIRQL irql;
// tree_holder_nonpaged* thnp = th->nonpaged;
BOOL ret;
if (!th->tree) {
NTSTATUS Status;
- Status = _load_tree(Vcb, th->address, r, &th->tree, t, func, file, line);
+ Status = _load_tree(Vcb, th->address, r, &th->tree, t, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("load_tree returned %08x\n", Status);
ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
return CONTAINING_RECORD(le, tree_data, list_entry);
}
-static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp,
+ const char* func, const char* file, unsigned int line) {
int cmp;
tree_data *td, *lasttd;
td = first_item(t);
lasttd = NULL;
- if (!td) return STATUS_INTERNAL_ERROR;
+ if (!td) return STATUS_NOT_FOUND;
do {
cmp = keycmp(searchkey, &td->key);
oldtp.tree = t;
oldtp.item = td;
- while (_find_prev_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
+ while (_find_prev_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
oldtp.tree = t;
oldtp.item = td;
- while (_find_next_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
+ while (_find_next_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
oldtp = *tp;
}
- return STATUS_INTERNAL_ERROR;
+ return STATUS_NOT_FOUND;
} else {
tp->tree = t;
tp->item = td;
}
if (!td)
- return STATUS_INTERNAL_ERROR;
+ return STATUS_NOT_FOUND;
// if (i > 0)
// TRACE("entering tree from (%x,%x,%x) to (%x,%x,%x) (%p)\n", (UINT32)t->items[i].key.obj_id, t->items[i].key.obj_type, (UINT32)t->items[i].key.offset, (UINT32)t->items[i+1].key.obj_id, t->items[i+1].key.obj_type, (UINT32)t->items[i+1].key.offset, t->items[i].tree);
- Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return Status;
}
- Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
+ Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line);
return Status;
}
}
-NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line) {
NTSTATUS Status;
BOOL loaded;
// KIRQL irql;
TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
if (!r->treeholder.tree) {
- Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return Status;
}
}
- Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
- if (!NT_SUCCESS(Status)) {
+ Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line);
+ if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
ERR("find_item_in_tree returned %08x\n", Status);
}
return Status;
}
-BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp,
+ const char* func, const char* file, unsigned int line) {
tree* t;
tree_data *td, *next;
NTSTATUS Status;
if (!t)
return FALSE;
- Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return FALSE;
fi = first_item(t);
- Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return FALSE;
traverse_ptr ntp2;
BOOL b;
- while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
+ while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, Irp, func, file, line))) {
*next_tp = ntp2;
if (!next_tp->item->ignore)
return CONTAINING_RECORD(le, tree_data, list_entry);
}
-BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp,
+ const char* func, const char* file, unsigned int line) {
tree* t;
tree_data* td;
NTSTATUS Status;
td = prev_item(t->parent, t->paritem);
- Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return FALSE;
li = last_item(t);
- Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, func, file, line);
+ Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return FALSE;
InsertTailList(rollback, &ri->list_entry);
}
-BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback) {
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, PIRP Irp, LIST_ENTRY* rollback) {
traverse_ptr tp;
KEY searchkey;
int cmp;
searchkey.obj_type = obj_type;
searchkey.offset = offset;
- Status = find_item(Vcb, r, &tp, &searchkey, TRUE);
- if (!NT_SUCCESS(Status)) {
+ Status = find_item(Vcb, r, &tp, &searchkey, TRUE, Irp);
+ if (Status == STATUS_NOT_FOUND) {
if (r) {
if (!r->treeholder.tree) {
BOOL loaded;
- Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded);
+ Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
ERR("error: find_item returned %08x\n", Status);
goto end;
}
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ goto end;
}
TRACE("tp.item = %p\n", tp.item);
if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
+ int3;
goto end;
}
} else
TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->ignore ? "TRUE" : "FALSE");
#ifdef DEBUG_PARANOID
- if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
- ERR("ERROR - tree_lock not held exclusively\n");
- int3;
- }
+// if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+// ERR("ERROR - tree_lock not held exclusively\n");
+// int3;
+// }
if (tp->item->ignore) {
ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
switch (ri->type) {
case ROLLBACK_INSERT_ITEM:
case ROLLBACK_DELETE_ITEM:
+ case ROLLBACK_ADD_SPACE:
+ case ROLLBACK_SUBTRACT_SPACE:
+ case ROLLBACK_INSERT_EXTENT:
+ case ROLLBACK_DELETE_EXTENT:
ExFreePool(ri->ptr);
break;
}
void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
rollback_item* ri;
while (!IsListEmpty(rollback)) {
case ROLLBACK_INSERT_EXTENT:
{
- extent* ext = ri->ptr;
+ rollback_extent* re = ri->ptr;
- ext->ignore = TRUE;
+ re->ext->ignore = TRUE;
+
+ if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data;
+
+ if (ed2->size != 0) {
+ chunk* c = get_chunk_from_address(Vcb, ed2->address);
+
+ if (c) {
+ Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id,
+ re->fcb->inode, re->ext->offset - ed2->offset, -1,
+ re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL);
+
+ if (!NT_SUCCESS(Status))
+ ERR("update_changed_extent_ref returned %08x\n", Status);
+ }
+
+ re->fcb->inode_item.st_blocks -= ed2->num_bytes;
+ }
+ }
+
+ ExFreePool(re);
break;
}
case ROLLBACK_DELETE_EXTENT:
{
- extent* ext = ri->ptr;
+ rollback_extent* re = ri->ptr;
+
+ re->ext->ignore = FALSE;
+
+ if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data;
+
+ if (ed2->size != 0) {
+ chunk* c = get_chunk_from_address(Vcb, ed2->address);
+
+ if (c) {
+ Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id,
+ re->fcb->inode, re->ext->offset - ed2->offset, 1,
+ re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL);
+
+ if (!NT_SUCCESS(Status))
+ ERR("update_changed_extent_ref returned %08x\n", Status);
+ }
+
+ re->fcb->inode_item.st_blocks += ed2->num_bytes;
+ }
+ }
+
+ ExFreePool(re);
+ break;
+ }
+
+ case ROLLBACK_ADD_SPACE:
+ case ROLLBACK_SUBTRACT_SPACE:
+ {
+ rollback_space* rs = ri->ptr;
+
+ if (rs->chunk)
+ ExAcquireResourceExclusiveLite(&rs->chunk->lock, TRUE);
+
+ if (ri->type == ROLLBACK_ADD_SPACE)
+ space_list_subtract2(rs->list, rs->list_size, rs->address, rs->length, NULL);
+ else
+ space_list_add2(rs->list, rs->list_size, rs->address, rs->length, NULL);
+
+ if (rs->chunk) {
+ LIST_ENTRY* le2 = le->Blink;
+
+ while (le2 != rollback) {
+ LIST_ENTRY* le3 = le2->Blink;
+ rollback_item* ri2 = CONTAINING_RECORD(le2, rollback_item, list_entry);
+
+ if (ri2->type == ROLLBACK_ADD_SPACE || ri2->type == ROLLBACK_SUBTRACT_SPACE) {
+ rollback_space* rs2 = ri2->ptr;
+
+ if (rs2->chunk == rs->chunk) {
+ if (ri2->type == ROLLBACK_ADD_SPACE)
+ space_list_subtract2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+ else
+ space_list_add2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+
+ ExFreePool(rs2);
+ RemoveEntryList(&ri2->list_entry);
+ ExFreePool(ri2);
+ }
+ }
+
+ le2 = le3;
+ }
+
+ ExReleaseResourceLite(&rs->chunk->lock);
+ }
+
+ ExFreePool(rs);
- ext->ignore = FALSE;
break;
}
}
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
#include "btrfs_drv.h"
void do_read_job(PIRP Irp) {
while (TRUE) {
LIST_ENTRY* le;
+ device_extension* Vcb = thread->DeviceObject->DeviceExtension;
KeAcquireSpinLock(&thread->spin_lock, &irql);
KeReleaseSpinLock(&thread->spin_lock, irql);
+ InterlockedDecrement(&Vcb->threads.pending_jobs);
do_job(thread, le);
}
// static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr);
-static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback);
+static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
+static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback);
static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
write_context* context = conptr;
return lastaddr;
}
-static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTRY* rollback) {
+static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
DEV_ITEM* di;
searchkey.obj_type = TYPE_DEV_ITEM;
searchkey.offset = device->devitem.dev_id;
- Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM));
- if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return TRUE;
}
-chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) {
UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor;
UINT64 total_size = 0, i, logaddr;
UINT16 type, num_stripes, sub_stripes, max_stripes, min_stripes;
}
TRACE("total_size = %llx\n", total_size);
+ // We purposely check for DATA first - mixed blocks have the same size
+ // as DATA ones.
if (flags & BLOCK_FLAG_DATA) {
max_stripe_size = 0x40000000; // 1 GB
max_chunk_size = 10 * max_stripe_size;
if (flags & BLOCK_FLAG_DUPLICATE) {
min_stripes = 2;
max_stripes = 2;
- sub_stripes = 1;
+ sub_stripes = 0;
type = BLOCK_FLAG_DUPLICATE;
} else if (flags & BLOCK_FLAG_RAID0) {
min_stripes = 2;
max_stripes = Vcb->superblock.num_devices;
- sub_stripes = 1;
+ sub_stripes = 0;
type = BLOCK_FLAG_RAID0;
} else if (flags & BLOCK_FLAG_RAID1) {
min_stripes = 2;
goto end;
}
- c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+ c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
if (!c) {
ERR("out of memory\n");
goto end;
}
- c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
- if (!c->nonpaged) {
- ERR("out of memory\n");
- goto end;
- }
-
- // add CHUNK_ITEM to tree 3
-
cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
- c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+ c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG);
if (!c->chunk_item) {
ERR("out of memory\n");
goto end;
c->chunk_item->num_stripes = num_stripes;
c->chunk_item->sub_stripes = sub_stripes;
- c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
+ c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
if (!c->devices) {
ERR("out of memory\n");
goto end;
InitializeListHead(&c->deleting);
InitializeListHead(&c->changed_extents);
- ExInitializeResourceLite(&c->nonpaged->lock);
- ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+ ExInitializeResourceLite(&c->lock);
+ ExInitializeResourceLite(&c->changed_extents_lock);
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+ s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG);
if (!s) {
ERR("out of memory\n");
goto end;
for (i = 0; i < num_stripes; i++) {
stripes[i].device->devitem.bytes_used += stripe_size;
- space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, rollback);
+ space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL);
}
success = TRUE;
return success ? c : NULL;
}
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp) {
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c) {
NTSTATUS Status;
UINT32 i;
- chunk* c;
CHUNK_ITEM_STRIPE* cis;
write_data_stripe* stripe;
UINT64 *stripestart = NULL, *stripeend = NULL;
TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
- c = get_chunk_from_address(Vcb, address);
if (!c) {
- ERR("could not get chunk for address %llx\n", address);
- return STATUS_INTERNAL_ERROR;
+ c = get_chunk_from_address(Vcb, address);
+ if (!c) {
+ ERR("could not get chunk for address %llx\n", address);
+ return STATUS_INTERNAL_ERROR;
+ }
}
if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
stripe->Irp = NULL;
stripe->buf = NULL;
} else {
- stripe->context = (struct write_data_context*)wtc;
+ stripe->context = (struct _write_data_context*)wtc;
stripe->buf = stripedata[i];
stripe->need_free = need_free2;
stripe->device = c->devices[i];
return Status;
}
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp) {
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c) {
write_data_context* wtc;
NTSTATUS Status;
wtc->tree = FALSE;
wtc->stripes_left = 0;
- Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp);
+ Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp, c);
if (!NT_SUCCESS(Status)) {
ERR("write_data returned %08x\n", Status);
free_write_data_stripes(wtc);
while (!IsListEmpty(&Vcb->chunks_changed)) {
c = CONTAINING_RECORD(Vcb->chunks_changed.Flink, chunk, list_entry_changed);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
clean_space_cache_chunk(Vcb, c);
RemoveEntryList(&c->list_entry_changed);
c->list_entry_changed.Flink = NULL;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
}
}
static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) {
UINT8 level;
LIST_ENTRY* le;
- NTSTATUS Status;
for (level = 0; level <= 255; level++) {
BOOL nothing_found = TRUE;
TRACE("adding tree %p (level %x)\n", t->parent, t->header.level);
t->parent->write = TRUE;
- } else if (t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
- KEY searchkey;
- traverse_ptr tp;
-
- searchkey.obj_id = t->root->id;
- searchkey.obj_type = TYPE_ROOT_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
- ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
- if (!ri) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- if (tp.item->size > 0)
- RtlCopyMemory(ri, tp.item->data, tp.item->size);
-
- RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
- ERR("insert_tree_item failed\n");
- return STATUS_INTERNAL_ERROR;
- }
- } else {
- tp.tree->write = TRUE;
- }
}
}
}
static void add_parents_to_cache(device_extension* Vcb, tree* t) {
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
-
while (t->parent) {
t = t->parent;
t->write = TRUE;
}
-
- if (t->root == Vcb->root_root || t->root == Vcb->chunk_root)
- return;
-
- searchkey.obj_id = t->root->id;
- searchkey.obj_type = TYPE_ROOT_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return;
- }
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
- return;
- }
-
- tp.tree->write = TRUE;
}
-static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, PIRP Irp, LIST_ENTRY* rollback) {
EXTENT_ITEM_SKINNY_METADATA* eism;
traverse_ptr insert_tp;
eism->type = TYPE_TREE_BLOCK_REF;
eism->tbr.offset = root_id;
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eism);
return FALSE;
}
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
add_parents_to_cache(Vcb, insert_tp.tree);
return TRUE;
}
-static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, PIRP Irp, LIST_ENTRY* rollback) {
UINT64 address;
EXTENT_ITEM_TREE2* eit2;
traverse_ptr insert_tp;
return FALSE;
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
- BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, rollback);
+ BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, Irp, rollback);
if (b)
*new_address = address;
// }
// #endif
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eit2);
return FALSE;
}
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
add_parents_to_cache(Vcb, insert_tp.tree);
return TRUE;
}
-NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
chunk *origchunk = NULL, *c;
LIST_ENTRY* le;
UINT64 flags = t->flags, addr;
- if (flags == 0)
- flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE;
+ if (flags == 0) {
+ if (t->root->id == BTRFS_ROOT_CHUNK)
+ flags = BLOCK_FLAG_SYSTEM | BLOCK_FLAG_DUPLICATE;
+ else if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS)
+ flags = BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA;
+ else
+ flags = BLOCK_FLAG_METADATA | BLOCK_FLAG_DUPLICATE;
+ }
// TRACE("flags = %x\n", (UINT32)wt->flags);
if (t->has_address) {
origchunk = get_chunk_from_address(Vcb, t->header.address);
- if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, rollback)) {
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, Irp, rollback)) {
t->new_address = addr;
t->has_new_address = TRUE;
return STATUS_SUCCESS;
while (le != &Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
- if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&Vcb->chunk_lock);
t->new_address = addr;
t->has_new_address = TRUE;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le->Flink;
}
// allocate new chunk if necessary
- if ((c = alloc_chunk(Vcb, flags, rollback))) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ if ((c = alloc_chunk(Vcb, flags))) {
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
- if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&Vcb->chunk_lock);
t->new_address = addr;
t->has_new_address = TRUE;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
}
ExReleaseResourceLite(&Vcb->chunk_lock);
return STATUS_DISK_FULL;
}
-static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) {
+static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
chunk* c;
searchkey.obj_type = TYPE_METADATA_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
c = get_chunk_from_address(Vcb, address);
if (c) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
decrease_chunk_usage(c, Vcb->superblock.node_size);
space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
} else
ERR("could not find chunk for address %llx\n", address);
// }
// }
-static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, LIST_ENTRY* rollback) {
+static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp, tp2, insert_tp;
EXTENT_REF_V0* erv0;
searchkey.obj_type = TYPE_EXTENT_REF_V0;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = Vcb->superblock.node_size;
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return;
eism->type = TYPE_TREE_BLOCK_REF;
eism->tbr.offset = t->header.tree_id;
- if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return;
}
eit2->type = TYPE_TREE_BLOCK_REF;
eit2->tbr.offset = t->header.tree_id;
- if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return;
}
add_parents_to_cache(Vcb, tp2.tree);
}
-static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) {
+static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
EXTENT_ITEM* ei;
TRACE("(%p, %llx, %p)\n", Vcb, address, t);
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
- if (reduce_tree_extent_skinny(Vcb, address, t, rollback)) {
+ if (reduce_tree_extent_skinny(Vcb, address, t, Irp, rollback)) {
return STATUS_SUCCESS;
}
}
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = Vcb->superblock.node_size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_EXTENT_REF_V0;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (!td->ignore && !td->inserted) {
if (t->header.level > 0) {
- convert_old_tree_extent(Vcb, td, t, rollback);
+ convert_old_tree_extent(Vcb, td, t, Irp, rollback);
} else if (td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA)) {
EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
if (ed2->address != 0) {
TRACE("trying to convert old data extent %llx,%llx\n", ed2->address, ed2->size);
- convert_old_data_extent(Vcb, ed2->address, ed2->size, rollback);
+ convert_old_data_extent(Vcb, ed2->address, ed2->size, Irp, rollback);
}
}
}
c = get_chunk_from_address(Vcb, address);
if (c) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
decrease_chunk_usage(c, tp.item->key.offset);
space_list_add(Vcb, c, TRUE, address, tp.item->key.offset, rollback);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
} else
ERR("could not find chunk for address %llx\n", address);
return STATUS_SUCCESS;
}
-static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le;
NTSTATUS Status;
if (t->write && !t->has_new_address) {
chunk* c;
- Status = get_tree_new_address(Vcb, t, rollback);
+ Status = get_tree_new_address(Vcb, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("get_tree_new_address returned %08x\n", Status);
return Status;
TRACE("allocated extent %llx\n", t->new_address);
if (t->has_address) {
- Status = reduce_tree_extent(Vcb, t->header.address, t, rollback);
+ Status = reduce_tree_extent(Vcb, t->header.address, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
return STATUS_SUCCESS;
}
-static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_root_root(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le;
NTSTATUS Status;
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ int3;
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
le = le->Flink;
}
- Status = update_chunk_caches(Vcb, rollback);
+ Status = update_chunk_caches(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_chunk_caches returned %08x\n", Status);
return Status;
}
}
-static NTSTATUS write_trees(device_extension* Vcb) {
+static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) {
UINT8 level;
UINT8 *data, *body;
UINT32 crc32;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = Vcb->superblock.node_size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
*((UINT32*)data) = crc32;
TRACE("setting crc32 to %08x\n", crc32);
- Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL);
+ Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL, NULL);
if (!NT_SUCCESS(Status)) {
ERR("write_data returned %08x\n", Status);
goto end;
return Status;
}
-static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb) {
+static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb, PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
searchkey.obj_id = BTRFS_ROOT_FSTREE;
- if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
searchkey.obj_id = BTRFS_ROOT_DEVTREE;
- if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
searchkey.obj_id = BTRFS_ROOT_CHECKSUM;
- if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
sb->num_devices = Vcb->superblock.num_devices;
}
-static NTSTATUS write_superblocks(device_extension* Vcb) {
+static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) {
UINT64 i;
NTSTATUS Status;
LIST_ENTRY* le;
RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup));
}
- update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1]);
+ update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp);
for (i = 0; i < Vcb->superblock.num_devices; i++) {
if (Vcb->devices[i].devobj) {
return STATUS_SUCCESS;
}
-static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, LIST_ENTRY* rollback) {
+static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY *le, *le2;
NTSTATUS Status;
UINT64 old_size;
old_size = ce->old_count > 0 ? ce->old_size : ce->size;
if (cer->edr.count > old_count) {
- Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, rollback);
+ Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("increase_extent_refcount_data returned %08x\n", Status);
}
} else if (cer->edr.count < old_count) {
Status = decrease_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset,
- old_count - cer->edr.count, rollback);
+ old_count - cer->edr.count, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("decrease_extent_refcount_data returned %08x\n", Status);
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = ce->old_size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
} else
data = NULL;
- if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
-static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY *le = Vcb->chunks.Flink, *le2;
chunk* c;
KEY searchkey;
while (le != &Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
le2 = c->changed_extents.Flink;
while (le2 != &c->changed_extents) {
LIST_ENTRY* le3 = le2->Flink;
changed_extent* ce = CONTAINING_RECORD(le2, changed_extent, list_entry);
- Status = flush_changed_extent(Vcb, c, ce, rollback);
+ Status = flush_changed_extent(Vcb, c, ce, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("flush_changed_extent returned %08x\n", Status);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
searchkey.offset = c->chunk_item->size;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
int3;
Status = STATUS_INTERNAL_ERROR;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
Status = STATUS_INTERNAL_ERROR;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
if (!bgi) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(bgi);
Status = STATUS_INTERNAL_ERROR;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
}
FIXME("RAID5 not yet supported\n");
ExFreePool(bgi);
Status = STATUS_INTERNAL_ERROR;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
} else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
FIXME("RAID6 not yet supported\n");
ExFreePool(bgi);
Status = STATUS_INTERNAL_ERROR;
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
goto end;
} else { // SINGLE
Vcb->superblock.bytes_used += c->used - c->oldused;
c->oldused = c->used;
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le->Flink;
}
if (flushed_extents) {
ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
if (!IsListEmpty(&Vcb->sector_checksums)) {
- update_checksum_tree(Vcb, rollback);
+ update_checksum_tree(Vcb, Irp, rollback);
}
ExReleaseResourceLite(&Vcb->checksum_lock);
}
return STATUS_SUCCESS;
}
-static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
+static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le;
tree_data* nextparitem = NULL;
NTSTATUS Status;
// ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
- Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded);
+ Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded, NULL);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return Status;
next_tree->size = 0;
if (next_tree->has_new_address) { // delete associated EXTENT_ITEM
- Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, rollback);
+ Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
return Status;
}
} else if (next_tree->has_address) {
- Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, rollback);
+ Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
return STATUS_SUCCESS;
}
-static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_METADATA_ITEM;
searchkey.offset = t->header.level;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eism);
return STATUS_INTERNAL_ERROR;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
eit->level = level;
- if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eit);
return STATUS_INTERNAL_ERROR;
return STATUS_INTERNAL_ERROR;
}
-static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL do_splits(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
// LIST_ENTRY *le, *le2;
// write_tree* wt;
// tree_data* td;
t->root->root_item.bytes_used -= Vcb->superblock.node_size;
if (t->has_new_address) { // delete associated EXTENT_ITEM
- Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
+ Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
t->has_new_address = FALSE;
} else if (t->has_address) {
- Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
+ Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
free_tree(t);
} else if (t->header.level != 0) {
if (t->has_new_address) {
- Status = update_extent_level(Vcb, t->new_address, t, 0, rollback);
+ Status = update_extent_level(Vcb, t->new_address, t, 0, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_extent_level returned %08x\n", Status);
t = CONTAINING_RECORD(le, tree, list_entry);
if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size) {
- Status = try_tree_amalgamate(Vcb, t, rollback);
+ Status = try_tree_amalgamate(Vcb, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("try_tree_amalgamate returned %08x\n", Status);
return Status;
TRACE("deleting top-level tree in root %llx with one item\n", t->root->id);
if (t->has_new_address) { // delete associated EXTENT_ITEM
- Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
+ Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
t->has_new_address = FALSE;
} else if (t->has_address) {
- Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
+ Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
KEY searchkey = {0,0,0};
traverse_ptr tp;
- Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
return STATUS_SUCCESS;
}
-static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
if (level > 0) {
if (!th->tree) {
- Status = load_tree(Vcb, th->address, r, &th->tree, NULL);
+ Status = load_tree(Vcb, th->address, r, &th->tree, NULL, NULL);
if (!NT_SUCCESS(Status)) {
ERR("load_tree(%llx) returned %08x\n", th->address, Status);
tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
if (!td->ignore) {
- Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, rollback);
+ Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("remove_root_extents returned %08x\n", Status);
}
if (!th->tree || th->tree->has_address) {
- Status = reduce_tree_extent(Vcb, th->address, NULL, rollback);
+ Status = reduce_tree_extent(Vcb, th->address, NULL, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent(%llx) returned %08x\n", th->address, Status);
return STATUS_SUCCESS;
}
-static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) {
+static NTSTATUS drop_root(device_extension* Vcb, root* r, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
- Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, rollback);
+ Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("remove_root_extents returned %08x\n", Status);
return Status;
RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
if (searchkey.obj_id != 0 || searchkey.offset != 0) {
- Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
WARN("find_item returned %08x\n", Status);
} else {
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
return Status;
return STATUS_SUCCESS;
}
-static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS drop_roots(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2;
NTSTATUS Status;
le2 = le->Flink;
- Status = drop_root(Vcb, r, rollback);
+ Status = drop_root(Vcb, r, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("drop_root(%llx) returned %08x\n", r->id, Status);
return Status;
return STATUS_SUCCESS;
}
-static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) {
+static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
CHUNK_ITEM* ci;
CHUNK_ITEM_STRIPE* cis;
BLOCK_GROUP_ITEM* bgi;
RtlCopyMemory(ci, c->chunk_item, c->size);
- if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ci);
return STATUS_INTERNAL_ERROR;
bgi->chunk_tree = 0x100;
bgi->flags = c->chunk_item->type;
- if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(bgi);
return STATUS_INSUFFICIENT_RESOURCES;
de->length = c->chunk_item->size / factor;
de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid;
- if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(de);
return STATUS_INTERNAL_ERROR;
}
// FIXME - no point in calling this twice for the same device
- Status = update_dev_item(Vcb, c->devices[i], rollback);
+ Status = update_dev_item(Vcb, c->devices[i], Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_dev_item returned %08x\n", Status);
return Status;
}
}
-static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) {
+static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
if (c->cache) {
c->cache->deleted = TRUE;
- flush_fcb(c->cache, TRUE, rollback);
+ flush_fcb(c->cache, TRUE, Irp, rollback);
free_fcb(c->cache);
searchkey.obj_type = 0;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_DEV_EXTENT;
searchkey.offset = cis[i].offset;
- Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_DEV_ITEM;
searchkey.offset = c->devices[i]->devitem.dev_id;
- Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM));
- if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
searchkey.obj_type = TYPE_CHUNK_ITEM;
searchkey.offset = c->offset;
- Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
ExFreePool(s);
}
- ExDeleteResourceLite(&c->nonpaged->lock);
- ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
- ExFreePool(c->nonpaged);
+ ExDeleteResourceLite(&c->lock);
+ ExDeleteResourceLite(&c->changed_extents_lock);
ExFreePool(c);
return STATUS_SUCCESS;
}
-static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunks(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY *le = Vcb->chunks_changed.Flink, *le2;
NTSTATUS Status;
UINT64 used_minus_cache;
le2 = le->Flink;
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
used_minus_cache = c->used;
}
if (used_minus_cache == 0) {
- Status = drop_chunk(Vcb, c, rollback);
+ Status = drop_chunk(Vcb, c, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("drop_chunk returned %08x\n", Status);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&Vcb->chunk_lock);
return Status;
}
} else if (c->created) {
- Status = create_chunk(Vcb, c, rollback);
+ Status = create_chunk(Vcb, c, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_chunk returned %08x\n", Status);
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&Vcb->chunk_lock);
return Status;
}
}
if (used_minus_cache > 0)
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le2;
}
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
- ULONG xasize;
+ ULONG xasize, maxlen;
DIR_ITEM* xa;
NTSTATUS Status;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
-
- // FIXME - make sure xasize not too big
+ maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
if (!keycmp(&tp.item->key, &searchkey)) { // key exists
UINT8* newdata;
UINT64 pos;
// replace
+
+ if (tp.item->size + xasize - oldxasize > maxlen) {
+ ERR("DIR_ITEM would be over maximum size (%u + %u - %u > %u)\n", tp.item->size, xasize, oldxasize, maxlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
if (!newdata) {
ERR("out of memory\n");
RtlCopyMemory(xa->name + strlen(name), data, datalen);
delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, Irp, rollback);
break;
}
- if (xa->m + xa->n >= size) { // FIXME - test this works
+ if ((UINT8*)xa - (UINT8*)tp.item->data + oldxasize >= size) {
// not found, add to end of data
+
+ if (tp.item->size + xasize > maxlen) {
+ ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", tp.item->size, xasize, maxlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
if (!newdata) {
ERR("out of memory\n");
RtlCopyMemory(xa->name + strlen(name), data, datalen);
delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, Irp, rollback);
break;
} else {
}
}
} else {
- // add new DIR_ITEM struct
+ if (xasize > maxlen) {
+ ERR("DIR_ITEM would be over maximum size (%u > %u)\n", xasize, maxlen);
+ return STATUS_INTERNAL_ERROR;
+ }
xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
if (!xa) {
RtlCopyMemory(xa->name, name, strlen(name));
RtlCopyMemory(xa->name + strlen(name), data, datalen);
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, Irp, rollback);
}
return STATUS_SUCCESS;
}
-static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) {
+static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
DIR_ITEM* xa;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = crc32;
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, Irp, rollback);
return TRUE;
}
}
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
+static NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+
+ TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length);
+
+ ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ if (!ed) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ed->generation = fcb->Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = 0;
+ ed2->size = 0;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, Irp, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback) {
traverse_ptr tp;
KEY searchkey;
NTSTATUS Status;
if (fcb->ads) {
if (fcb->deleted)
- delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, rollback);
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, Irp, rollback);
else {
- Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, rollback);
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
BOOL b;
traverse_ptr next_tp;
LIST_ENTRY* le;
- BOOL prealloc = FALSE;
+ BOOL prealloc = FALSE, extents_inline = FALSE;
+ UINT64 last_end;
// delete ignored extent items
le = fcb->extents.Flink;
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->data->data;
- if (ed2->address == 0 && ned2->address == 0 && ed2->size == 0 && ned2->size == 0) {
- // FIXME - merge together adjacent sparse extents
- } else if (ed2->address == ned2->address && ed2->size == ned2->size && nextext->offset == ext->offset + ed2->num_bytes &&
- ned2->offset == ed2->offset + ed2->num_bytes) {
+ if (ed2->size != 0 && ed2->address == ned2->address && ed2->size == ned2->size &&
+ nextext->offset == ext->offset + ed2->num_bytes && ned2->offset == ed2->offset + ed2->num_bytes) {
chunk* c;
ext->data->generation = fcb->Vcb->superblock.generation;
ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
} else {
Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
if (!NT_SUCCESS(Status)) {
ERR("update_changed_extent_ref returned %08x\n", Status);
goto end;
searchkey.obj_type = TYPE_EXTENT_DATA;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
delete_tree_item(fcb->Vcb, &tp, rollback);
- b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
if (b) {
tp = next_tp;
}
} while (b);
- // add new EXTENT_DATAs
-
- le = fcb->extents.Flink;
- while (le != &fcb->extents) {
- extent* ext = CONTAINING_RECORD(le, extent, list_entry);
- EXTENT_DATA* ed;
+ if (!fcb->deleted) {
+ // add new EXTENT_DATAs
+
+ last_end = 0;
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+ EXTENT_DATA* ed;
- ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!ed) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
+ if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && ext->offset > last_end) {
+ Status = insert_sparse_extent(fcb, last_end, ext->offset - last_end, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!ed) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(ed, ext->data, ext->datalen);
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, Irp, rollback)) {
+ ERR("insert_tree_item failed\n");
+ goto end;
+ }
+
+ if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
+ prealloc = TRUE;
+
+ if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_INLINE)
+ extents_inline = TRUE;
+
+ if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) {
+ if (ed->type == EXTENT_TYPE_INLINE)
+ last_end = ext->offset + ed->decoded_size;
+ else {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ last_end = ext->offset + ed2->num_bytes;
+ }
+ }
+
+ le = le->Flink;
}
- RtlCopyMemory(ed, ext->data, ext->datalen);
-
- if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, rollback)) {
- ERR("insert_tree_item failed\n");
- goto end;
+ if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && !extents_inline &&
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) {
+ Status = insert_sparse_extent(fcb, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ goto end;
+ }
}
- if (!prealloc && ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
- prealloc = TRUE;
+ // update prealloc flag in INODE_ITEM
- le = le->Flink;
+ if (!prealloc)
+ fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
+ else
+ fcb->inode_item.flags |= BTRFS_INODE_PREALLOC;
}
- // update prealloc flag in INODE_ITEM
-
- if (!prealloc)
- fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
- else
- fcb->inode_item.flags |= BTRFS_INODE_PREALLOC;
-
fcb->extents_changed = FALSE;
}
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
- if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
goto end;
}
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = ii_offset;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
- while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
+ while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE, Irp)) {
tp = tp2;
if (tp.item->key.obj_id == fcb->inode) {
RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
- if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
goto end;
}
}
if (fcb->sd_dirty) {
- Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
}
TRACE("inserting new DOSATTRIB xattr\n");
sprintf(val, "0x%lx", fcb->atts);
- Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
} else
- delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, Irp, rollback);
fcb->atts_changed = FALSE;
fcb->atts_deleted = FALSE;
if (fcb->reparse_xattr_changed) {
if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) {
- Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, rollback);
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
} else
- delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, rollback);
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, Irp, rollback);
fcb->reparse_xattr_changed = FALSE;
}
return;
}
-static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_ROOT_REF;
searchkey.offset = subvolid;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size)
RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data));
- insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, rollback);
+ insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, Irp, rollback);
}
break;
return STATUS_SUCCESS;
}
-static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
searchkey.obj_type = TYPE_ROOT_REF;
searchkey.offset = subvolid;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(rr2);
return STATUS_INTERNAL_ERROR;
}
} else {
- if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(rr);
return STATUS_INTERNAL_ERROR;
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
UINT8* data;
searchkey.obj_type = TYPE_ROOT_REF;
searchkey.offset = subvolid;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_ROOT_BACKREF;
searchkey.offset = parsubvolid;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
+
+ if (datalen > 0)
+ ExFreePool(data);
+
return Status;
}
delete_tree_item(Vcb, &tp, rollback);
if (datalen > 0) {
- if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(data);
return STATUS_INTERNAL_ERROR;
return STATUS_SUCCESS;
}
-static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
+static NTSTATUS flush_fileref(file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
// if fileref created and then immediately deleted, do nothing
RtlCopyMemory(di2, di, disize);
- if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, rollback)) {
+ if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
Status = STATUS_INTERNAL_ERROR;
return Status;
}
- Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, rollback);
+ Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_dir_item returned %08x\n", Status);
return Status;
}
if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
- Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback);
+ Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_inode_ref returned %08x\n", Status);
return Status;
rr->n = fileref->utf8.Length;
RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
- Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback);
+ Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_root_ref returned %08x\n", Status);
return Status;
}
- Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
+ Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_root_backref returned %08x\n", Status);
return Status;
// delete DIR_ITEM (0x54)
- Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, rollback);
+ Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_dir_item returned %08x\n", Status);
return Status;
if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
// delete INODE_REF (0xc)
- Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, rollback);
+ Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_inode_ref returned %08x\n", Status);
return Status;
}
} else { // subvolume
- Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, rollback);
+ Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_root_ref returned %08x\n", Status);
}
- Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
+ Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_root_backref returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = fileref->index;
- Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
Status = STATUS_INTERNAL_ERROR;
ExFreePool(fileref->oldutf8.Buffer);
fileref->oldutf8.Buffer = NULL;
}
- } else { // rename
- if (fileref->oldutf8.Buffer) {
- UINT32 crc32, oldcrc32;
- ULONG disize;
- DIR_ITEM *di, *di2;
- KEY searchkey;
- traverse_ptr tp;
-
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+ } else { // rename or change type
+ PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->utf8;
+ UINT32 crc32, oldcrc32;
+ ULONG disize;
+ DIR_ITEM *di, *di2;
+ KEY searchkey;
+ traverse_ptr tp;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+
+ if (!fileref->oldutf8.Buffer)
+ oldcrc32 = crc32;
+ else
oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->oldutf8.Buffer, fileref->oldutf8.Length);
- // delete DIR_ITEM (0x54)
+ // delete DIR_ITEM (0x54)
+
+ Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, oldutf8, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // add DIR_ITEM (0x54)
+
+ disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di2) {
+ ERR("out of memory\n");
+ ExFreePool(di);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+ di->key.obj_id = fileref->fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ } else { // subvolume
+ di->key.obj_id = fileref->fcb->subvol->id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0xffffffffffffffff;
+ }
+
+ di->transid = fileref->fcb->Vcb->superblock.generation;
+ di->m = 0;
+ di->n = (UINT16)fileref->utf8.Length;
+ di->type = fileref->fcb->type;
+ RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
+
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+ // delete INODE_REF (0xc)
- Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->oldutf8, rollback);
+ Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("delete_dir_item returned %08x\n", Status);
+ ERR("delete_inode_ref returned %08x\n", Status);
return Status;
}
- // add DIR_ITEM (0x54)
+ // add INODE_REF (0xc)
- disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
- di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
- if (!di) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ return Status;
}
+ } else { // subvolume
+ ULONG rrlen;
+ ROOT_REF* rr;
- di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
- if (!di2) {
- ERR("out of memory\n");
- ExFreePool(di);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
+ // FIXME - make sure this works with duff subvols within snapshots
- 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;
+ Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_root_ref returned %08x\n", Status);
}
- 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);
+ 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;
+ }
- RtlCopyMemory(di2, di, disize);
+ 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_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, rollback);
+ Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("add_dir_item returned %08x\n", Status);
+ ERR("add_root_ref returned %08x\n", Status);
return Status;
}
- if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
- // delete INODE_REF (0xc)
-
- Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, &fileref->oldutf8, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("delete_inode_ref returned %08x\n", Status);
- return Status;
- }
-
- // add INODE_REF (0xc)
-
- Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("add_inode_ref returned %08x\n", Status);
- return Status;
- }
- } else { // subvolume
- ULONG rrlen;
- ROOT_REF* rr;
-
- // FIXME - make sure this works with duff subvols within snapshots
-
- Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->oldutf8, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("delete_root_ref returned %08x\n", Status);
- }
-
- rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
-
- rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
- if (!rr) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- rr->dir = fileref->parent->fcb->inode;
- rr->index = fileref->index;
- rr->n = fileref->utf8.Length;
- RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
-
- Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("add_root_ref returned %08x\n", Status);
- return Status;
- }
-
- Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("update_root_backref returned %08x\n", Status);
- return Status;
- }
- }
-
- // delete DIR_INDEX (0x60)
-
- searchkey.obj_id = fileref->parent->fcb->inode;
- searchkey.obj_type = TYPE_DIR_INDEX;
- searchkey.offset = fileref->index;
-
- Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
+ Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- Status = STATUS_INTERNAL_ERROR;
- return Status;
- }
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- } else
- WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id);
-
- // add DIR_INDEX (0x60)
-
- if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, rollback)) {
- ERR("insert_tree_item failed\n");
- Status = STATUS_INTERNAL_ERROR;
+ ERR("update_root_backref returned %08x\n", Status);
return Status;
}
+ }
+
+ // delete DIR_INDEX (0x60)
+
+ searchkey.obj_id = fileref->parent->fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = fileref->index;
+
+ Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ Status = STATUS_INTERNAL_ERROR;
+ return Status;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ } else
+ WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id);
+
+ // add DIR_INDEX (0x60)
+
+ if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, Irp, rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ return Status;
+ }
+ if (fileref->oldutf8.Buffer) {
ExFreePool(fileref->oldutf8.Buffer);
fileref->oldutf8.Buffer = NULL;
}
return STATUS_SUCCESS;
}
-static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback) {
+static void convert_shared_data_refs(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le;
NTSTATUS Status;
TRACE("tree %llx; root %llx, objid %llx, offset %llx, count %x\n",
t->header.address, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count);
- Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, rollback);
+ Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, Irp, rollback);
if (!NT_SUCCESS(Status))
WARN("increase_extent_refcount_data returned %08x\n", Status);
if (old) {
- Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, rollback);
+ Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, Irp, rollback);
if (!NT_SUCCESS(Status))
WARN("decrease_extent_refcount_old returned %08x\n", Status);
} else {
- Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, rollback);
+ Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, Irp, rollback);
if (!NT_SUCCESS(Status))
WARN("decrease_extent_refcount_shared_data returned %08x\n", Status);
}
}
-NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ traverse_ptr tp;
+ NTSTATUS Status;
+
+ searchkey.obj_id = root;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+ int3;
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
+ ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+ if (!ri) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (tp.item->size > 0)
+ RtlCopyMemory(ri, tp.item->data, tp.item->size);
+
+ RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ tp.tree->write = TRUE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_root_items_to_cache(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
+
+ if (t->write && t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
+ Status = add_root_item_to_cache(Vcb, t->root->id, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_root_item_to_cache returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ // make sure we always update the extent tree
+ Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_root_item_to_cache returned %08x\n", Status);
+ return Status;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
LIST_ENTRY* le;
BOOL cache_changed = FALSE;
dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry);
- flush_fileref(dirt->fileref, rollback);
+ flush_fileref(dirt->fileref, Irp, rollback);
free_fileref(dirt->fileref);
ExFreePool(dirt);
}
+ // We process deleted streams first, so we don't run over our xattr
+ // limit unless we absolutely have to.
+
+ le = Vcb->dirty_fcbs.Flink;
+ while (le != &Vcb->dirty_fcbs) {
+ dirty_fcb* dirt;
+ LIST_ENTRY* le2 = le->Flink;
+
+ dirt = CONTAINING_RECORD(le, dirty_fcb, list_entry);
+
+ if (dirt->fcb->deleted && dirt->fcb->ads) {
+ RemoveEntryList(le);
+
+ flush_fcb(dirt->fcb, FALSE, Irp, rollback);
+ free_fcb(dirt->fcb);
+ ExFreePool(dirt);
+ }
+
+ le = le2;
+ }
+
le = Vcb->dirty_fcbs.Flink;
while (le != &Vcb->dirty_fcbs) {
dirty_fcb* dirt;
if (dirt->fcb->subvol != Vcb->root_root || dirt->fcb->deleted) {
RemoveEntryList(le);
- flush_fcb(dirt->fcb, FALSE, rollback);
+ flush_fcb(dirt->fcb, FALSE, Irp, rollback);
free_fcb(dirt->fcb);
ExFreePool(dirt);
}
le = le2;
}
- convert_shared_data_refs(Vcb, rollback);
+ convert_shared_data_refs(Vcb, Irp, rollback);
ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
if (!IsListEmpty(&Vcb->sector_checksums)) {
- update_checksum_tree(Vcb, rollback);
+ update_checksum_tree(Vcb, Irp, rollback);
}
ExReleaseResourceLite(&Vcb->checksum_lock);
if (!IsListEmpty(&Vcb->drop_roots)) {
- Status = drop_roots(Vcb, rollback);
+ Status = drop_roots(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("drop_roots returned %08x\n", Status);
}
if (!IsListEmpty(&Vcb->chunks_changed)) {
- Status = update_chunks(Vcb, rollback);
+ Status = update_chunks(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_chunks returned %08x\n", Status);
searchkey.obj_type = 0;
searchkey.offset = 0;
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
Vcb->root_root->treeholder.tree->write = TRUE;
}
+ Status = add_root_items_to_cache(Vcb, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_root_items_to_cache returned %08x\n", Status);
+ return Status;
+ }
+
do {
Status = add_parents(Vcb, rollback);
if (!NT_SUCCESS(Status)) {
goto end;
}
- Status = do_splits(Vcb, rollback);
+ Status = do_splits(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("do_splits returned %08x\n", Status);
goto end;
}
- Status = allocate_tree_extents(Vcb, rollback);
+ Status = allocate_tree_extents(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_parents returned %08x\n", Status);
goto end;
}
- Status = update_chunk_usage(Vcb, rollback);
+ Status = update_chunk_usage(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_chunk_usage returned %08x\n", Status);
goto end;
}
- Status = allocate_cache(Vcb, &cache_changed, rollback);
+ Status = allocate_cache(Vcb, &cache_changed, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("allocate_cache returned %08x\n", Status);
goto end;
TRACE("trees consistent\n");
- Status = update_root_root(Vcb, rollback);
+ Status = update_root_root(Vcb, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_root_root returned %08x\n", Status);
goto end;
}
- Status = write_trees(Vcb);
+ Status = write_trees(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("write_trees returned %08x\n", Status);
goto end;
Vcb->superblock.cache_generation = Vcb->superblock.generation;
- Status = write_superblocks(Vcb);
+ Status = write_superblocks(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("write_superblocks returned %08x\n", Status);
goto end;
searchkey.obj_type = TYPE_METADATA_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
int3;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
int3;
}
NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, signed long long count,
- BOOL no_csum, UINT64 new_size) {
+ BOOL no_csum, UINT64 new_size, PIRP Irp) {
LIST_ENTRY* le;
changed_extent* ce;
changed_extent_ref* cer;
traverse_ptr tp;
UINT64 old_count;
- ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
ce = get_changed_extent_item(c, address, size, no_csum);
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
le = le->Flink;
}
- old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset);
+ old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset, Irp);
if (old_count > 0) {
cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
Status = STATUS_SUCCESS;
end:
- ExReleaseResourceLite(&c->nonpaged->changed_extents_lock);
+ ExReleaseResourceLite(&c->changed_extents_lock);
return Status;
}
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) {
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
LIST_ENTRY* le;
len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
- if (ext->offset < end_data && ext->offset + len >= start_data) {
- if (ed->compression != BTRFS_COMPRESSION_NONE) {
- FIXME("FIXME - compression not supported at present\n");
- Status = STATUS_NOT_SUPPORTED;
- goto end;
- }
-
- if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
- WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb->subvol->id, fcb->inode, ext->offset, ed->encryption);
- Status = STATUS_NOT_SUPPORTED;
- goto end;
- }
-
- if (ed->encoding != BTRFS_ENCODING_NONE) {
- WARN("other encodings not supported\n");
- Status = STATUS_NOT_SUPPORTED;
- goto end;
- }
-
+ if (ext->offset < end_data && ext->offset + len > start_data) {
if (ed->type == EXTENT_TYPE_INLINE) {
if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
fcb->inode_item.st_blocks -= len;
} else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
newext->ignore = FALSE;
InsertHeadList(&ext->list_entry, &newext->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
fcb->inode_item.st_blocks -= end_data - ext->offset;
} else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
newext->ignore = FALSE;
InsertHeadList(&ext->list_entry, &newext->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
fcb->inode_item.st_blocks -= ext->offset + len - start_data;
} else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
InsertHeadList(&ext->list_entry, &newext1->list_entry);
InsertHeadList(&newext1->list_entry, &newext2->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
fcb->inode_item.st_blocks -= end_data - start_data;
}
ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
} else {
Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
if (!NT_SUCCESS(Status)) {
ERR("update_changed_extent_ref returned %08x\n", Status);
goto end;
}
}
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
} else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
EXTENT_DATA* ned;
EXTENT_DATA2* ned2;
newext->ignore = FALSE;
InsertHeadList(&ext->list_entry, &newext->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
} else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
EXTENT_DATA* ned;
EXTENT_DATA2* ned2;
newext->ignore = FALSE;
InsertHeadList(&ext->list_entry, &newext->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
} else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
EXTENT_DATA *neda, *nedb;
EXTENT_DATA2 *neda2, *nedb2;
ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
} else {
Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
if (!NT_SUCCESS(Status)) {
ERR("update_changed_extent_ref returned %08x\n", Status);
goto end;
InsertHeadList(&ext->list_entry, &newext1->list_entry);
InsertHeadList(&newext1->list_entry, &newext2->list_entry);
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
}
}
}
changed_sector* sc;
int i;
- Status = write_data_complete(Vcb, address, data, length, Irp);
+ Status = write_data_complete(Vcb, address, data, length, Irp, NULL);
if (!NT_SUCCESS(Status)) {
ERR("write_data returned %08x\n", Status);
return Status;
return STATUS_SUCCESS;
}
+static void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) {
+ rollback_extent* re;
+
+ re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
+ if (!re) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ re->fcb = fcb;
+ re->ext = ext;
+
+ add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
+}
+
static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG edsize, BOOL unique, LIST_ENTRY* rollback) {
extent* ext;
LIST_ENTRY* le;
InsertTailList(&fcb->extents, &ext->list_entry);
end:
- add_rollback(rollback, ROLLBACK_INSERT_EXTENT, ext);
+ add_insert_extent_rollback(rollback, fcb, ext);
return TRUE;
}
-void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback) {
+static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) {
if (!ext->ignore) {
+ rollback_extent* re;
+
ext->ignore = TRUE;
- add_rollback(rollback, ROLLBACK_DELETE_EXTENT, ext);
+
+ re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
+ if (!re) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ re->fcb = fcb;
+ re->ext = ext;
+
+ add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
}
}
}
BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data,
- LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size) {
UINT64 address;
NTSTATUS Status;
EXTENT_DATA* ed;
}
ed->generation = Vcb->superblock.generation;
- ed->decoded_size = length;
- ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->decoded_size = decoded_size;
+ ed->compression = compression;
ed->encryption = BTRFS_ENCRYPTION_NONE;
ed->encoding = BTRFS_ENCODING_NONE;
ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
ed2->address = address;
ed2->size = length;
ed2->offset = 0;
- ed2->num_bytes = length;
+ ed2->num_bytes = decoded_size;
if (!add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, rollback)) {
ERR("add_extent_to_fcb failed\n");
increase_chunk_usage(c, length);
space_list_subtract(Vcb, c, FALSE, address, length, rollback);
- fcb->inode_item.st_blocks += length;
+ fcb->inode_item.st_blocks += decoded_size;
fcb->extents_changed = TRUE;
mark_fcb_dirty(fcb);
- ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
- ExReleaseResourceLite(&c->nonpaged->changed_extents_lock);
+ ExReleaseResourceLite(&c->changed_extents_lock);
return TRUE;
}
static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
LIST_ENTRY* changed_sector_list, extent* ext, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
+ EXTENT_DATA2 *ed2, *ed2orig;
extent* newext;
- UINT64 addr;
+ UINT64 addr, origsize;
NTSTATUS Status;
+ LIST_ENTRY* le;
TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data,
length, data, changed_sector_list, ext, c, rollback);
+ ed2orig = (EXTENT_DATA2*)ext->data->data;
+
+ origsize = ed2orig->size;
+ addr = ed2orig->address + ed2orig->size;
+
+ Status = write_data_complete(Vcb, addr, data, length, Irp, c);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
+
+ if (!ext2->ignore && (ext2->data->type == EXTENT_TYPE_REGULAR || ext2->data->type == EXTENT_TYPE_PREALLOC)) {
+ EXTENT_DATA2* ed2b = (EXTENT_DATA2*)ext2->data->data;
+
+ if (ed2b->address == ed2orig->address) {
+ ed2b->size = origsize + length;
+ ext2->data->decoded_size = origsize + length;
+ }
+ }
+
+ le = le->Flink;
+ }
+
ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
if (!ed) {
ERR("out of memory\n");
}
RtlCopyMemory(ed, ext->data, ext->datalen);
-
- ed->decoded_size += length;
+
ed2 = (EXTENT_DATA2*)ed->data;
-
- addr = ed2->address + ed2->size;
-
- Status = write_data_complete(Vcb, addr, data, length, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("write_data returned %08x\n", Status);
- ExFreePool(newext);
- ExFreePool(ed);
- return FALSE;
- }
-
- ed2->size += length;
- ed2->num_bytes += length;
+ ed2->offset = ed2orig->offset + ed2orig->num_bytes;
+ ed2->num_bytes = length;
RtlCopyMemory(newext, ext, sizeof(extent));
+ newext->offset = ext->offset + ed2orig->num_bytes;
newext->data = ed;
InsertHeadList(&ext->list_entry, &newext->list_entry);
- remove_fcb_extent(ext, rollback);
+ add_insert_extent_rollback(rollback, fcb, newext);
- Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size - length, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 0,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+ Status = update_changed_extent_ref(Vcb, c, ed2orig->address, origsize, fcb->subvol->id, fcb->inode, newext->offset - ed2->offset,
+ 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
if (!NT_SUCCESS(Status)) {
ERR("update_changed_extent_ref returned %08x\n", Status);
increase_chunk_usage(c, length);
- space_list_subtract(Vcb, c, FALSE, addr, length, rollback);
+ space_list_subtract(Vcb, c, FALSE, addr, length, NULL); // no rollback as we don't reverse extending the extent
fcb->inode_item.st_blocks += length;
le = le->Flink;
}
- if (!ext) {
- WARN("previous EXTENT_DATA not found\n");
- goto end;
- }
-
+ if (!ext)
+ return FALSE;
+
if (!ext->unique) {
TRACE("extent was not unique\n");
- goto end;
+ return FALSE;
}
ed = ext->data;
if (ext->datalen < sizeof(EXTENT_DATA)) {
ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
- goto end;
+ return FALSE;
}
if (ed->type != EXTENT_TYPE_REGULAR) {
TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
- goto end;
+ return FALSE;
}
ed2 = (EXTENT_DATA2*)ed->data;
if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- goto end;
+ return FALSE;
}
if (ext->offset + ed2->num_bytes != start_data) {
TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data);
- goto end;
+ return FALSE;
}
if (ed->compression != BTRFS_COMPRESSION_NONE) {
- FIXME("FIXME: compression not yet supported\n");
- goto end;
+ TRACE("not extending a compressed extent\n");
+ return FALSE;
}
if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
WARN("encryption not supported\n");
- goto end;
+ return FALSE;
}
if (ed->encoding != BTRFS_ENCODING_NONE) {
WARN("other encodings not supported\n");
- goto end;
+ return FALSE;
}
if (ed2->size - ed2->offset != ed2->num_bytes) {
TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
- goto end;
+ return FALSE;
}
if (ed2->size >= MAX_EXTENT_SIZE) {
TRACE("extent size was too large to extend (%llx >= %llx)\n", ed2->size, (UINT64)MAX_EXTENT_SIZE);
- goto end;
+ return FALSE;
}
c = get_chunk_from_address(Vcb, ed2->address);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
le = c->space.Flink;
while (le != &c->space) {
le = le->Flink;
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
-end:
-
return success;
}
UINT64 flags, origlength = length;
#endif
NTSTATUS Status;
+ BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
flags = fcb->Vcb->data_flags;
// FIXME - try and maximize contiguous ranges first. If we can't do that,
// allocate all the free space we find until it's enough.
- ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
-
do {
UINT64 extlen = min(MAX_EXTENT_SIZE, length);
+ ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+
le = fcb->Vcb->chunks.Flink;
while (le != &fcb->Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
- if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
+ ExReleaseResourceLite(&c->lock);
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
goto cont;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le->Flink;
}
- if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ if ((c = alloc_chunk(fcb->Vcb, flags))) {
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
- if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
+ ExReleaseResourceLite(&c->lock);
goto cont;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
- }
+ ExReleaseResourceLite(&c->lock);
+ } else
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
WARN("couldn't find any data chunks with %llx bytes free\n", origlength);
Status = STATUS_DISK_FULL;
Status = STATUS_SUCCESS;
end:
- ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
-
return Status;
}
-NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
- EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
-
- TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length);
-
- ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
- if (!ed) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- ed->generation = fcb->Vcb->superblock.generation;
- ed->decoded_size = length;
- ed->compression = BTRFS_COMPRESSION_NONE;
- ed->encryption = BTRFS_ENCRYPTION_NONE;
- ed->encoding = BTRFS_ENCODING_NONE;
- ed->type = EXTENT_TYPE_REGULAR;
-
- ed2 = (EXTENT_DATA2*)ed->data;
- ed2->address = 0;
- ed2->size = 0;
- ed2->offset = 0;
- ed2->num_bytes = length;
-
- if (!add_extent_to_fcb(fcb, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), FALSE, rollback)) {
- ERR("add_extent_to_fcb failed\n");
- return STATUS_INTERNAL_ERROR;
- }
-
- fcb->extents_changed = TRUE;
- mark_fcb_dirty(fcb);
-
- return STATUS_SUCCESS;
-}
-
// static void print_tree(tree* t) {
// LIST_ENTRY* le = t->itemlist.Flink;
// while (le != &t->itemlist) {
}
}
- // if there is a gap before start_data, plug it with a sparse extent
- // FIXME - don't do this if no_holes set
- if (start_data > 0) {
- NTSTATUS Status;
- EXTENT_DATA* ed;
- extent* lastext = NULL;
- UINT64 len;
-
- le = fcb->extents.Flink;
- while (le != &fcb->extents) {
- extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!ext->ignore) {
- if (ext->offset == start_data) {
- lastext = ext;
- break;
- } else if (ext->offset > start_data)
- break;
-
- lastext = ext;
- }
-
- le = le->Flink;
- }
-
- if (lastext && lastext->datalen >= sizeof(EXTENT_DATA)) {
- EXTENT_DATA2* ed2;
-
- ed = lastext->data;
- ed2 = (EXTENT_DATA2*)ed->data;
-
- len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
- } else
- ed = NULL;
-
- if (!lastext || !ed || lastext->offset + len < start_data) {
- if (!lastext)
- Status = insert_sparse_extent(fcb, 0, start_data, rollback);
- else if (!ed) {
- ERR("extent at %llx was %u bytes, expected at least %u\n", lastext->offset, lastext->datalen, sizeof(EXTENT_DATA));
- return STATUS_INTERNAL_ERROR;
- } else
- Status = insert_sparse_extent(fcb, lastext->offset + len, start_data - lastext->offset - len, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- return Status;
- }
- }
- }
-
flags = Vcb->data_flags;
- ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
-
while (written < orig_length) {
UINT64 newlen = min(length, MAX_EXTENT_SIZE);
BOOL done = FALSE;
// Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
// First, see if we can write the extent part to an existing chunk.
+ ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
+
le = Vcb->chunks.Flink;
while (le != &Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
- if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
written += newlen;
if (written == orig_length) {
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
ExReleaseResourceLite(&Vcb->chunk_lock);
return STATUS_SUCCESS;
} else {
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
+ ExReleaseResourceLite(&c->lock);
le = le->Flink;
}
+ ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
if (done) continue;
// Otherwise, see if we can put it in a new chunk.
- if ((c = alloc_chunk(Vcb, flags, rollback))) {
- ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+ if ((c = alloc_chunk(Vcb, flags))) {
+ ExReleaseResourceLite(&Vcb->chunk_lock);
+
+ ExAcquireResourceExclusiveLite(&c->lock, TRUE);
if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
- if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
written += newlen;
if (written == orig_length) {
- ExReleaseResourceLite(&c->nonpaged->lock);
- ExReleaseResourceLite(&Vcb->chunk_lock);
+ ExReleaseResourceLite(&c->lock);
return STATUS_SUCCESS;
} else {
done = TRUE;
}
}
- ExReleaseResourceLite(&c->nonpaged->lock);
- }
+ ExReleaseResourceLite(&c->lock);
+ } else
+ ExReleaseResourceLite(&Vcb->chunk_lock);
if (!done) {
FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME
}
}
- ExReleaseResourceLite(&Vcb->chunk_lock);
-
WARN("couldn't find any data chunks with %llx bytes free\n", length);
return STATUS_DISK_FULL;
}
-static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
+static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
LIST_ENTRY* le = Vcb->sector_checksums.Flink;
changed_sector* cs;
traverse_ptr tp, next_tp;
// FIXME - create checksum_root if it doesn't exist at all
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) { // tree is completely empty
// FIXME - do proper check here that tree is empty
if (!cs->deleted) {
RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length);
- if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(checksums);
goto exit;
searchkey.obj_type = TYPE_EXTENT_CSUM;
searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
searchkey.obj_type = TYPE_EXTENT_CSUM;
searchkey.offset = cs->ol.key;
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
delete_tree_item(Vcb, &tp, rollback);
}
- if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
tp = next_tp;
} else
break;
RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
- if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, Irp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(data);
ExFreePool(bmparr);
}
}
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
// FIXME - convert into inline extent if short enough
Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
- sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback);
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - excise_extents failed\n");
return Status;
oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
cur_inline = ed->type == EXTENT_TYPE_INLINE;
- if (cur_inline && end > fcb->Vcb->max_inline) {
+ if (cur_inline && end > fcb->Vcb->options.max_inline) {
LIST_ENTRY changed_sector_list;
BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
UINT64 origlength, length;
fcb->inode_item.st_blocks -= origlength;
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
- Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("insert_extent returned %08x\n", Status);
- ExFreePool(data);
- return Status;
+ if (write_fcb_compressed(fcb)) {
+ Status = write_compressed(fcb, offset, offset + length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_compressed returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ } else {
+ Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_extent returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
}
oldalloc = ext->offset + length;
ed->decoded_size = end - ext->offset;
- remove_fcb_extent(ext, rollback);
+ remove_fcb_extent(fcb, ext, rollback);
if (!add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, rollback)) {
ERR("add_extent_to_fcb failed\n");
ERR("insert_prealloc_extent returned %08x\n", Status);
return Status;
}
- } else {
- Status = insert_sparse_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- return Status;
- }
}
fcb->extents_changed = TRUE;
fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
}
} else {
- if (end > fcb->Vcb->max_inline) {
+ if (end > fcb->Vcb->options.max_inline) {
newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
if (prealloc) {
ERR("insert_prealloc_extent returned %08x\n", Status);
return Status;
}
- } else {
- Status = insert_sparse_extent(fcb, 0, newalloc, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- return Status;
- }
}
fcb->extents_changed = TRUE;
return STATUS_SUCCESS;
}
-static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data) {
- LIST_ENTRY* le;
- extent* ext = NULL;
-
- le = fcb->extents.Flink;
-
- while (le != &fcb->extents) {
- extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!nextext->ignore) {
- if (nextext->offset == start_data) {
- ext = nextext;
- break;
- } else if (nextext->offset > start_data)
- break;
-
- ext = nextext;
- }
-
- le = le->Flink;
- }
-
- if (!ext)
- return FALSE;
-
- le = &ext->list_entry;
-
- while (le != &fcb->extents) {
- ext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!ext->ignore) {
- if (ext->datalen < sizeof(EXTENT_DATA)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
- return FALSE;
- }
-
- if (ext->offset < end_data && ext->data->type == EXTENT_TYPE_PREALLOC) {
- EXTENT_DATA2* ed2;
-
- if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- return FALSE;
- }
-
- ed2 = (EXTENT_DATA2*)ext->data->data;
-
- if (ext->offset + ed2->num_bytes >= start_data)
- return TRUE;
- }
- }
-
- le = le->Flink;
- }
-
- return FALSE;
-}
+// #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 NTSTATUS do_cow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
- NTSTATUS Status;
-
- Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("error - excise_extents returned %08x\n", Status);
- goto end;
- }
-
- Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - insert_extent returned %08x\n", Status);
- goto end;
- }
-
- Status = STATUS_SUCCESS;
-
-end:
- return Status;
-}
+// static 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);
+// // goto failure;
+// // }
+//
+// return;
+//
+// failure:
+// if (fcb->subvol->treeholder.tree)
+// print_loaded_trees(fcb->subvol->treeholder.tree, 0);
+//
+// int3;
+// }
-static NTSTATUS do_prealloc_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
- NTSTATUS Status;
- UINT64 last_written = start_data;
- extent* ext = NULL;
- LIST_ENTRY* le;
- chunk* c;
-
- le = fcb->extents.Flink;
-
- while (le != &fcb->extents) {
- extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!nextext->ignore) {
- if (nextext->offset == start_data) {
- ext = nextext;
- break;
- } else if (nextext->offset > start_data)
- break;
-
- ext = nextext;
- }
-
- le = le->Flink;
- }
-
- if (!ext)
- return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
-
- le = &ext->list_entry;
-
- while (le != &fcb->extents) {
- EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
- LIST_ENTRY* le2 = le->Flink;
-
- ext = CONTAINING_RECORD(le, extent, list_entry);
- ed = ext->data;
-
- if (!ext->ignore) {
- if (ext->offset >= end_data)
- break;
-
- if (ext->datalen < sizeof(EXTENT_DATA)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
- return STATUS_INTERNAL_ERROR;
- }
-
- if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
- if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- return STATUS_INTERNAL_ERROR;
- }
-
- ed2 = (EXTENT_DATA2*)ed->data;
- }
-
- if (ed->type == EXTENT_TYPE_PREALLOC) {
- if (ext->offset > last_written) {
- Status = do_cow_write(Vcb, fcb, last_written, ext->offset, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("do_cow_write returned %08x\n", Status);
- return Status;
- }
-
- last_written = ext->offset;
- }
-
- if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
- EXTENT_DATA* ned;
- extent* newext;
-
- ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!ned) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext) {
- ERR("out of memory\n");
- ExFreePool(ned);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ned, ext->data, ext->datalen);
-
- ned->type = EXTENT_TYPE_REGULAR;
-
- Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("do_write_data returned %08x\n", Status);
- return Status;
- }
-
- last_written = ext->offset + ed2->num_bytes;
-
- newext->offset = ext->offset;
- newext->data = ned;
- newext->datalen = ext->datalen;
- newext->unique = ext->unique;
- newext->ignore = FALSE;
- InsertHeadList(&ext->list_entry, &newext->list_entry);
-
- remove_fcb_extent(ext, rollback);
- } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
- EXTENT_DATA *ned, *nedb;
- EXTENT_DATA2* ned2;
- extent *newext1, *newext2;
-
- ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!ned) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!nedb) {
- ERR("out of memory\n");
- ExFreePool(ned);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext1) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext2) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- ExFreePool(newext1);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ned, ext->data, ext->datalen);
- ned->type = EXTENT_TYPE_REGULAR;
- ned2 = (EXTENT_DATA2*)ned->data;
- ned2->num_bytes = end_data - ext->offset;
-
- RtlCopyMemory(nedb, ext->data, ext->datalen);
- ned2 = (EXTENT_DATA2*)nedb->data;
- ned2->offset += end_data - ext->offset;
- ned2->num_bytes -= end_data - ext->offset;
-
- Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("do_write_data returned %08x\n", Status);
- return Status;
- }
-
- last_written = end_data;
-
- newext1->offset = ext->offset;
- newext1->data = ned;
- newext1->datalen = ext->datalen;
- newext1->unique = FALSE;
- newext1->ignore = FALSE;
- InsertHeadList(&ext->list_entry, &newext1->list_entry);
-
- newext2->offset = end_data;
- newext2->data = nedb;
- newext2->datalen = ext->datalen;
- newext2->unique = FALSE;
- newext2->ignore = FALSE;
- InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-
- c = get_chunk_from_address(Vcb, ed2->address);
-
- if (!c)
- ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
- else {
- Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-
- if (!NT_SUCCESS(Status)) {
- ERR("update_changed_extent_ref returned %08x\n", Status);
- return Status;
- }
- }
-
- remove_fcb_extent(ext, rollback);
- } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
- EXTENT_DATA *ned, *nedb;
- EXTENT_DATA2* ned2;
- extent *newext1, *newext2;
-
- // FIXME - test this
-
- ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!ned) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!nedb) {
- ERR("out of memory\n");
- ExFreePool(ned);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext1) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext2) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- ExFreePool(newext1);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ned, ext->data, ext->datalen);
-
- ned2 = (EXTENT_DATA2*)ned->data;
- ned2->num_bytes = start_data - ext->offset;
-
- RtlCopyMemory(nedb, ext->data, ext->datalen);
-
- nedb->type = EXTENT_TYPE_REGULAR;
- ned2 = (EXTENT_DATA2*)nedb->data;
- ned2->offset += start_data - ext->offset;
- ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
-
- Status = do_write_data(Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("do_write_data returned %08x\n", Status);
-
- return Status;
- }
-
- last_written = start_data + ned2->num_bytes;
-
- newext1->offset = ext->offset;
- newext1->data = ned;
- newext1->datalen = ext->datalen;
- newext1->unique = FALSE;
- newext1->ignore = FALSE;
- InsertHeadList(&ext->list_entry, &newext1->list_entry);
-
- newext2->offset = start_data;
- newext2->data = nedb;
- newext2->datalen = ext->datalen;
- newext2->unique = FALSE;
- newext2->ignore = FALSE;
- InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-
- c = get_chunk_from_address(Vcb, ed2->address);
-
- if (!c)
- ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
- else {
- Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-
- if (!NT_SUCCESS(Status)) {
- ERR("update_changed_extent_ref returned %08x\n", Status);
- return Status;
- }
- }
-
- remove_fcb_extent(ext, rollback);
- } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
- EXTENT_DATA *ned, *nedb, *nedc;
- EXTENT_DATA2* ned2;
- extent *newext1, *newext2, *newext3;
-
- // FIXME - test this
-
- ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!ned) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!nedb) {
- ERR("out of memory\n");
- ExFreePool(ned);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
- if (!nedb) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext1) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- ExFreePool(nedc);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext2) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- ExFreePool(nedc);
- ExFreePool(newext1);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
- if (!newext2) {
- ERR("out of memory\n");
- ExFreePool(ned);
- ExFreePool(nedb);
- ExFreePool(nedc);
- ExFreePool(newext1);
- ExFreePool(newext2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ned, ext->data, ext->datalen);
- RtlCopyMemory(nedb, ext->data, ext->datalen);
- RtlCopyMemory(nedc, ext->data, ext->datalen);
-
- ned2 = (EXTENT_DATA2*)ned->data;
- ned2->num_bytes = start_data - ext->offset;
-
- nedb->type = EXTENT_TYPE_REGULAR;
- ned2 = (EXTENT_DATA2*)nedb->data;
- ned2->offset += start_data - ext->offset;
- ned2->num_bytes = end_data - start_data;
-
- ned2 = (EXTENT_DATA2*)nedc->data;
- ned2->offset += end_data - ext->offset;
- ned2->num_bytes -= end_data - ext->offset;
-
- ned2 = (EXTENT_DATA2*)nedb->data;
- Status = do_write_data(Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("do_write_data returned %08x\n", Status);
- return Status;
- }
-
- last_written = end_data;
-
- newext1->offset = ext->offset;
- newext1->data = ned;
- newext1->datalen = ext->datalen;
- newext1->unique = FALSE;
- newext1->ignore = FALSE;
- InsertHeadList(&ext->list_entry, &newext1->list_entry);
-
- newext2->offset = start_data;
- newext2->data = nedb;
- newext2->datalen = ext->datalen;
- newext2->unique = FALSE;
- newext2->ignore = FALSE;
- InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-
- newext3->offset = end_data;
- newext3->data = nedc;
- newext3->datalen = ext->datalen;
- newext3->unique = FALSE;
- newext3->ignore = FALSE;
- InsertHeadList(&newext2->list_entry, &newext3->list_entry);
-
- c = get_chunk_from_address(Vcb, ed2->address);
-
- if (!c)
- ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
- else {
- Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
- fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-
- if (!NT_SUCCESS(Status)) {
- ERR("update_changed_extent_ref returned %08x\n", Status);
- return Status;
- }
- }
-
- remove_fcb_extent(ext, rollback);
- }
- }
- }
-
- le = le2;
- }
-
- if (last_written < end_data) {
- Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("do_cow_write returned %08x\n", Status);
- return Status;
- }
- }
-
- fcb->extents_changed = TRUE;
- mark_fcb_dirty(fcb);
-
- return STATUS_SUCCESS;
-}
-
-NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
- NTSTATUS Status;
- UINT64 size, new_start, new_end, last_written = start_data;
- extent* ext = NULL;
- LIST_ENTRY* le;
-
- TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, data, changed_sector_list);
-
- le = fcb->extents.Flink;
-
- while (le != &fcb->extents) {
- extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!nextext->ignore) {
- if (nextext->offset == start_data) {
- ext = nextext;
- break;
- } else if (nextext->offset > start_data)
- break;
-
- ext = nextext;
- }
-
- le = le->Flink;
- }
-
- if (!ext)
- return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
-
- le = &ext->list_entry;
-
- while (le != &fcb->extents) {
- EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
- BOOL do_cow;
- LIST_ENTRY* le2 = le->Flink;
-
- ext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (!ext->ignore) {
- ed = ext->data;
-
- if (ext->offset >= end_data)
- break;
-
- if (ext->datalen < sizeof(EXTENT_DATA)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
- return STATUS_INTERNAL_ERROR;
- }
-
- if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
- if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- return STATUS_INTERNAL_ERROR;
- }
-
- ed2 = (EXTENT_DATA2*)ed->data;
- }
-
- if (ed->type == EXTENT_TYPE_REGULAR) {
- do_cow = !ext->unique;
- } else {
- do_cow = TRUE;
- }
-
- if (ed->compression != BTRFS_COMPRESSION_NONE) {
- FIXME("FIXME: compression not yet supported\n");
- return STATUS_NOT_SUPPORTED;
- }
-
- if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
- WARN("encryption not supported\n");
- return STATUS_NOT_SUPPORTED;
- }
-
- if (ed->encoding != BTRFS_ENCODING_NONE) {
- WARN("other encodings not supported\n");
- return STATUS_NOT_SUPPORTED;
- }
-
- size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-
- TRACE("extent: start = %llx, length = %llx\n", ext->offset, size);
-
- new_start = ext->offset < start_data ? start_data : ext->offset;
- new_end = ext->offset + size > end_data ? end_data : (ext->offset + size);
-
- TRACE("new_start = %llx\n", new_start);
- TRACE("new_end = %llx\n", new_end);
-
- if (ed->type == EXTENT_TYPE_PREALLOC) {
- Status = do_prealloc_write(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("do_prealloc_write returned %08x\n", Status);
- return Status;
- }
- } else if (do_cow) {
- TRACE("doing COW write\n");
-
- Status = excise_extents(Vcb, fcb, new_start, new_end, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - excise_extents returned %08x\n", Status);
- return Status;
- }
-
- Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - insert_extent returned %08x\n", Status);
- return Status;
- }
- } else {
- UINT64 writeaddr = ed2->address + ed2->offset + new_start - ext->offset;
-
- TRACE("doing non-COW write to %llx\n", writeaddr);
-
- Status = write_data_complete(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start, Irp);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - write_data returned %08x\n", Status);
- return Status;
- }
-
- if (changed_sector_list) {
- unsigned int i;
- changed_sector* sc;
-
- sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
- if (!sc) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sc->ol.key = writeaddr;
- sc->length = (new_end - new_start) / Vcb->superblock.sector_size;
- sc->deleted = FALSE;
-
- sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
- if (!sc->checksums) {
- ERR("out of memory\n");
- ExFreePool(sc);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- for (i = 0; i < sc->length; i++) {
- sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + new_start - start_data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
- }
-
- insert_into_ordered_list(changed_sector_list, &sc->ol);
- }
- }
-
- last_written = new_end;
- }
-
- le = le2;
- }
-
- if (last_written < end_data) {
- Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("do_cow_write returned %08x\n", Status);
- return Status;
- }
- }
-
- Status = STATUS_SUCCESS;
-
- fcb->extents_changed = TRUE;
- mark_fcb_dirty(fcb);
-
- return Status;
-}
-
-// #ifdef DEBUG_PARANOID
-// static void print_loaded_trees(tree* t, int spaces) {
-// char pref[10];
-// 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) {
+// static void check_extent_tree_consistent(device_extension* Vcb) {
// 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;
-// }
+// UINT64 lastaddr;
+// BOOL b, inconsistency;
//
-// 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;
+// searchkey.obj_id = 0;
+// searchkey.obj_type = 0;
+// searchkey.offset = 0;
+//
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
// }
//
-// ed = (EXTENT_DATA*)tp.item->data;
-// ed2 = (EXTENT_DATA2*)&ed->data[0];
+// lastaddr = 0;
+// inconsistency = FALSE;
//
-// length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-// lastoff = tp.item->key.offset;
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// // ERR("inconsistency!\n");
+// // int3;
+// inconsistency = TRUE;
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+// }
+//
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
+// }
+// } while (b);
//
-// TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+// free_traverse_ptr(&tp);
//
-// alloc = 0;
-// if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
-// alloc += length;
-// }
+// if (!inconsistency)
+// return;
//
-// 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];
+// ERR("Inconsistency detected:\n");
//
-// length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+// ERR("error - could not find any entries in extent_root\n");
+// int3;
+// }
//
-// 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;
+// do {
+// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//
+// if (tp.item->key.obj_id < lastaddr) {
+// ERR("inconsistency!\n");
+// }
+//
+// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
// }
//
-// if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
-// alloc += length;
+// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+// if (b) {
+// free_traverse_ptr(&tp);
+// tp = next_tp;
// }
-//
-// 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;
-// // }
+// } while (b);
//
-// return;
+// free_traverse_ptr(&tp);
//
-// failure:
-// if (fcb->subvol->treeholder.tree)
-// print_loaded_trees(fcb->subvol->treeholder.tree, 0);
-//
// int3;
// }
+// #endif
+
+static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
+ LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ EXTENT_DATA* ed = ext->data;
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+ NTSTATUS Status;
+ chunk* c;
+
+ if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
+ EXTENT_DATA* ned;
+ extent* newext;
+
+ ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!ned) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(ned, ext->data, ext->datalen);
+
+ ned->type = EXTENT_TYPE_REGULAR;
+
+ Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+ return Status;
+ }
+
+ *written = ed2->num_bytes;
+
+ newext->offset = ext->offset;
+ newext->data = ned;
+ newext->datalen = ext->datalen;
+ newext->unique = ext->unique;
+ newext->ignore = FALSE;
+ InsertHeadList(&ext->list_entry, &newext->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext);
+
+ remove_fcb_extent(fcb, ext, rollback);
+ } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
+ EXTENT_DATA *ned, *nedb;
+ EXTENT_DATA2* ned2;
+ extent *newext1, *newext2;
+
+ ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!ned) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext1) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext2) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(newext1);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(ned, ext->data, ext->datalen);
+ ned->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = end_data - ext->offset;
+
+ RtlCopyMemory(nedb, ext->data, ext->datalen);
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += end_data - ext->offset;
+ ned2->num_bytes -= end_data - ext->offset;
+
+ Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+ return Status;
+ }
+
+ *written = end_data - ext->offset;
+
+ newext1->offset = ext->offset;
+ newext1->data = ned;
+ newext1->datalen = ext->datalen;
+ newext1->unique = FALSE;
+ newext1->ignore = FALSE;
+ InsertHeadList(&ext->list_entry, &newext1->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext1);
+
+ newext2->offset = end_data;
+ newext2->data = nedb;
+ newext2->datalen = ext->datalen;
+ newext2->unique = FALSE;
+ newext2->ignore = FALSE;
+ InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext2);
+
+ c = get_chunk_from_address(fcb->Vcb, ed2->address);
+
+ if (!c)
+ ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+ else {
+ Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_changed_extent_ref returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ remove_fcb_extent(fcb, ext, rollback);
+ } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
+ EXTENT_DATA *ned, *nedb;
+ EXTENT_DATA2* ned2;
+ extent *newext1, *newext2;
+
+ ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!ned) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext1) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext2) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(newext1);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(ned, ext->data, ext->datalen);
+
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = start_data - ext->offset;
+
+ RtlCopyMemory(nedb, ext->data, ext->datalen);
+
+ nedb->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += start_data - ext->offset;
+ ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
+
+ Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+ return Status;
+ }
+
+ *written = ned2->num_bytes;
+
+ newext1->offset = ext->offset;
+ newext1->data = ned;
+ newext1->datalen = ext->datalen;
+ newext1->unique = FALSE;
+ newext1->ignore = FALSE;
+ InsertHeadList(&ext->list_entry, &newext1->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext1);
+
+ newext2->offset = start_data;
+ newext2->data = nedb;
+ newext2->datalen = ext->datalen;
+ newext2->unique = FALSE;
+ newext2->ignore = FALSE;
+ InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext2);
+
+ c = get_chunk_from_address(fcb->Vcb, ed2->address);
+
+ if (!c)
+ ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+ else {
+ Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_changed_extent_ref returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ remove_fcb_extent(fcb, ext, rollback);
+ } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
+ EXTENT_DATA *ned, *nedb, *nedc;
+ EXTENT_DATA2* ned2;
+ extent *newext1, *newext2, *newext3;
+
+ ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!ned) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext1) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(nedc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext2) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(nedc);
+ ExFreePool(newext1);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+ if (!newext2) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(nedc);
+ ExFreePool(newext1);
+ ExFreePool(newext2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(ned, ext->data, ext->datalen);
+ RtlCopyMemory(nedb, ext->data, ext->datalen);
+ RtlCopyMemory(nedc, ext->data, ext->datalen);
+
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = start_data - ext->offset;
+
+ nedb->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += start_data - ext->offset;
+ ned2->num_bytes = end_data - start_data;
+
+ ned2 = (EXTENT_DATA2*)nedc->data;
+ ned2->offset += end_data - ext->offset;
+ ned2->num_bytes -= end_data - ext->offset;
+
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+ return Status;
+ }
+
+ *written = end_data - start_data;
+
+ newext1->offset = ext->offset;
+ newext1->data = ned;
+ newext1->datalen = ext->datalen;
+ newext1->unique = FALSE;
+ newext1->ignore = FALSE;
+ InsertHeadList(&ext->list_entry, &newext1->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext1);
+
+ newext2->offset = start_data;
+ newext2->data = nedb;
+ newext2->datalen = ext->datalen;
+ newext2->unique = FALSE;
+ newext2->ignore = FALSE;
+ InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext2);
+
+ newext3->offset = end_data;
+ newext3->data = nedc;
+ newext3->datalen = ext->datalen;
+ newext3->unique = FALSE;
+ newext3->ignore = FALSE;
+ InsertHeadList(&newext2->list_entry, &newext3->list_entry);
+
+ add_insert_extent_rollback(rollback, fcb, newext3);
+
+ c = get_chunk_from_address(fcb->Vcb, ed2->address);
+
+ if (!c)
+ ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+ else {
+ Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
+ fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_changed_extent_ref returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ remove_fcb_extent(fcb, ext, rollback);
+ }
+
+ return STATUS_SUCCESS;
+}
-// static void check_extent_tree_consistent(device_extension* Vcb) {
-// KEY searchkey;
-// traverse_ptr tp, next_tp;
-// UINT64 lastaddr;
-// BOOL b, inconsistency;
-//
-// searchkey.obj_id = 0;
-// searchkey.obj_type = 0;
-// searchkey.offset = 0;
-//
-// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
-// ERR("error - could not find any entries in extent_root\n");
-// int3;
-// }
-//
-// lastaddr = 0;
-// inconsistency = FALSE;
-//
-// do {
-// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
-// // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//
-// if (tp.item->key.obj_id < lastaddr) {
-// // ERR("inconsistency!\n");
-// // int3;
-// inconsistency = TRUE;
-// }
-//
-// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
-// }
-//
-// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
-// if (b) {
-// free_traverse_ptr(&tp);
-// tp = next_tp;
-// }
-// } while (b);
-//
-// free_traverse_ptr(&tp);
-//
-// if (!inconsistency)
-// return;
-//
-// ERR("Inconsistency detected:\n");
-//
-// if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
-// ERR("error - could not find any entries in extent_root\n");
-// int3;
-// }
-//
-// do {
-// if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
-// ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//
-// if (tp.item->key.obj_id < lastaddr) {
-// ERR("inconsistency!\n");
-// }
-//
-// lastaddr = tp.item->key.obj_id + tp.item->key.offset;
-// }
-//
-// b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
-// if (b) {
-// free_traverse_ptr(&tp);
-// tp = next_tp;
-// }
-// } while (b);
-//
-// free_traverse_ptr(&tp);
-//
-// int3;
-// }
-// #endif
+NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ LIST_ENTRY *le, *le2;
+ UINT64 written = 0, length = end_data - start;
+ UINT64 last_cow_start;
+#ifdef DEBUG_PARANOID
+ UINT64 last_off;
+#endif
+
+ last_cow_start = 0;
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+ le2 = le->Flink;
+
+ if (!ext->ignore) {
+ EXTENT_DATA* ed = ext->data;
+ EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
+ UINT64 len;
+ BOOL nocow;
+
+ len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+
+ if (ext->offset + len <= start)
+ goto nextitem;
+
+ if (ext->offset > start + written + length)
+ break;
+
+ nocow = (ext->unique && fcb->inode_item.flags & BTRFS_INODE_NODATACOW) || ed->type == EXTENT_TYPE_PREALLOC;
+
+ if (nocow) {
+ if (max(last_cow_start, start + written) < ext->offset) {
+ UINT64 start_write = max(last_cow_start, start + written);
+
+ Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, data, changed_sector_list, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ written += ext->offset - start_write;
+ length -= ext->offset - start_write;
+
+ if (length == 0)
+ break;
+ }
+
+ if (ed->type == EXTENT_TYPE_REGULAR) {
+ UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset;
+ UINT64 write_len = min(len, length);
+
+ TRACE("doing non-COW write to %llx\n", writeaddr);
+
+ Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data_complete returned %08x\n", Status);
+ return Status;
+ }
+
+ if (changed_sector_list) {
+ unsigned int i;
+ changed_sector* sc;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ if (!sc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sc->ol.key = writeaddr;
+ sc->length = write_len / fcb->Vcb->superblock.sector_size;
+ sc->deleted = FALSE;
+
+ sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+ if (!sc->checksums) {
+ ERR("out of memory\n");
+ ExFreePool(sc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + written + (i * fcb->Vcb->superblock.sector_size), fcb->Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ written += write_len;
+ length -= write_len;
+
+ if (length == 0)
+ break;
+ } else if (ed->type == EXTENT_TYPE_PREALLOC) {
+ UINT64 write_len;
+
+ Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
+ changed_sector_list, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_file_prealloc returned %08x\n", Status);
+ return Status;
+ }
+
+ written += write_len;
+ length -= write_len;
+
+ if (length == 0)
+ break;
+ }
+
+ last_cow_start = ext->offset + len;
+ }
+ }
+
+nextitem:
+ le = le2;
+ }
+
+ if (length > 0) {
+ UINT64 start_write = max(last_cow_start, start + written);
+
+ Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, data, changed_sector_list, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ // FIXME - make extending work again (here?)
+ // FIXME - make maximum extent size 128 MB again (here?)
+
+#ifdef DEBUG_PARANOID
+ last_off = 0xffffffffffffffff;
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+ if (!ext->ignore) {
+ if (ext->offset == last_off) {
+ ERR("offset %llx duplicated\n", ext->offset);
+ int3;
+ } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) {
+ ERR("offsets out of order\n");
+ int3;
+ }
+
+ last_off = ext->offset;
+ }
+
+ le = le->Flink;
+ }
+#endif
+
+ fcb->extents_changed = TRUE;
+ mark_fcb_dirty(fcb);
+
+ return STATUS_SUCCESS;
+}
-static void STDCALL deferred_write_callback(void* context1, void* context2) {
- PIRP Irp = context1;
- device_extension* Vcb = context2;
+NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ UINT64 i;
+
+ for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) {
+ UINT64 s2, e2;
+ BOOL compressed;
+
+ s2 = start_data + (i * COMPRESSED_EXTENT_SIZE);
+ e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data);
+
+ Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, changed_sector_list, Irp, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_compressed_bit returned %08x\n", Status);
+ return Status;
+ }
+
+ // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
+ // bother with the rest of it.
+ if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) {
+ fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
+ mark_fcb_dirty(fcb);
+
+ // write subsequent data non-compressed
+ if (e2 < end_data) {
+ Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, changed_sector_list, Irp, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_file returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ return STATUS_SUCCESS;
+ }
+ }
- if (!add_thread_job(Vcb, Irp))
- do_write_job(Vcb, Irp);
+ return STATUS_SUCCESS;
}
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
UINT8* data;
LIST_ENTRY changed_sector_list;
INODE_ITEM* origii;
- BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE, write_eof = FALSE*/;
+ BOOL changed_length = FALSE, nocsum/*, lazy_writer = FALSE, write_eof = FALSE*/;
NTSTATUS Status;
LARGE_INTEGER time;
BTRFS_TIME now;
fcb* fcb;
ccb* ccb;
file_ref* fileref;
- BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE;
+ BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile;
ULONG filter = 0;
TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
- if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write)) {
- CcDeferWrite(FileObject, (PCC_POST_DEFERRED_WRITE)deferred_write_callback, Irp, Vcb, *length, deferred_write);
-
+ if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write))
return STATUS_PENDING;
- }
if (!wait && no_cache)
return STATUS_PENDING;
paging_lock = TRUE;
}
- if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+ pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io;
+
+ if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
Status = STATUS_PENDING;
goto end;
}
nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
- nocow = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATACOW;
newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
}
}
- make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->max_inline;
+ make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->options.max_inline;
if (changed_length) {
if (newlength > fcb->Header.AllocationSize.QuadPart) {
}
if (fcb->ads) {
-// UINT32 maxlen;
-
if (changed_length) {
char* data2;
-// // find maximum length of xattr
-// maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
-//
-// searchkey.obj_id = fcb->inode;
-// searchkey.obj_type = TYPE_XATTR_ITEM;
-// searchkey.offset = fcb->adshash;
-//
-// Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
-// if (!NT_SUCCESS(Status)) {
-// ERR("error - find_item returned %08x\n", Status);
-// goto end;
-// }
-//
-// if (keycmp(&tp.item->key, &searchkey)) {
-// ERR("error - could not find key for xattr\n");
-// Status = STATUS_INTERNAL_ERROR;
-// goto end;
-// }
-//
-// if (tp.item->size < datalen) {
-// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen);
-// Status = STATUS_INTERNAL_ERROR;
-// goto end;
-// }
-//
-// maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
-//
-// if (newlength > maxlen) {
-// ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen);
-// Status = STATUS_DISK_FULL;
-// goto end;
-// }
+ if (newlength > fcb->adsmaxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen);
+ Status = STATUS_DISK_FULL;
+ goto end;
+ }
data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG);
if (!data2) {
if (fileref)
mark_fileref_dirty(fileref);
} else {
+ BOOL compress = write_fcb_compressed(fcb);
+
if (make_inline) {
start_data = 0;
end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
bufhead = sizeof(EXTENT_DATA) - 1;
+ } else if (compress) {
+ start_data = offset.QuadPart & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
+ end_data = min(sector_align(offset.QuadPart + *length, COMPRESSED_EXTENT_SIZE),
+ sector_align(newlength, fcb->Vcb->superblock.sector_size));
+ bufhead = 0;
} else {
start_data = offset.QuadPart & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size);
InitializeListHead(&changed_sector_list);
if (make_inline) {
- Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback);
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - excise_extents returned %08x\n", Status);
ExFreePool(data);
}
fcb->inode_item.st_blocks += newlength;
- } else if (!nocow) {
- if (is_file_prealloc(fcb, start_data, end_data)) {
- Status = do_prealloc_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - do_prealloc_write returned %08x\n", Status);
- ExFreePool(data);
- goto end;
- }
- } else {
- Status = do_cow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
-
- if (!NT_SUCCESS(Status)) {
- ERR("error - do_cow_write returned %08x\n", Status);
- ExFreePool(data);
- goto end;
- }
+ } else if (compress) {
+ Status = write_compressed(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_compressed returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
}
ExFreePool(data);
} else {
- Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+ Status = do_write_file(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("error - do_nocow_write returned %08x\n", Status);
+ ERR("do_write_file returned %08x\n", Status);
ExFreePool(data);
goto end;
}
}
}
- KeQuerySystemTime(&time);
- win_time_to_unix(time, &now);
-
-// ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+ if (!pagefile) {
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
-// if (!no_cache) {
-// if (!FileObject->PrivateCacheMap) {
-// CC_FILE_SIZES ccfs;
-//
-// ccfs.AllocationSize = fcb->Header.AllocationSize;
-// ccfs.FileSize = fcb->Header.FileSize;
-// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
-//
-// TRACE("calling CcInitializeCacheMap...\n");
-// CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
-//
-// changed_length = FALSE;
+// ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+//
+// if (!no_cache) {
+// if (!FileObject->PrivateCacheMap) {
+// CC_FILE_SIZES ccfs;
+//
+// ccfs.AllocationSize = fcb->Header.AllocationSize;
+// ccfs.FileSize = fcb->Header.FileSize;
+// ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//
+// TRACE("calling CcInitializeCacheMap...\n");
+// CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+//
+// changed_length = FALSE;
+// }
// }
-// }
-
- if (fcb->ads) {
- if (fileref && fileref->parent)
- origii = &fileref->parent->fcb->inode_item;
- else {
- ERR("no parent fcb found for stream\n");
- Status = STATUS_INTERNAL_ERROR;
- goto end;
- }
- } else
- origii = &fcb->inode_item;
-
- origii->transid = Vcb->superblock.generation;
- origii->sequence++;
- origii->st_ctime = now;
-
- if (!fcb->ads) {
- if (changed_length) {
- TRACE("setting st_size to %llx\n", newlength);
- origii->st_size = newlength;
- filter |= FILE_NOTIFY_CHANGE_SIZE;
+
+ if (fcb->ads) {
+ if (fileref && fileref->parent)
+ origii = &fileref->parent->fcb->inode_item;
+ else {
+ ERR("no parent fcb found for stream\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else
+ origii = &fcb->inode_item;
+
+ origii->transid = Vcb->superblock.generation;
+ origii->sequence++;
+ origii->st_ctime = now;
+
+ if (!fcb->ads) {
+ if (changed_length) {
+ TRACE("setting st_size to %llx\n", newlength);
+ origii->st_size = newlength;
+ filter |= FILE_NOTIFY_CHANGE_SIZE;
+ }
+
+ origii->st_mtime = now;
+ filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
}
- origii->st_mtime = now;
- filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
}
- mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
-
if (!nocsum) {
ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
commit_checksum_changes(Vcb, &changed_sector_list);
goto end;
}
- if (!(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
+ if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
WARN("insufficient permissions\n");
Status = STATUS_ACCESS_DENIED;
goto end;
--- /dev/null
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
+ inc)
+
+list(APPEND SOURCE
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/adler32.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/crc32.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/deflate.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inffast.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inflate.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inftrees.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/trees.c
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/zutil.c)
+
+add_library(zlib_solo ${SOURCE})
+
+add_definitions(-DZ_SOLO)
The following FSD are shared with: https://github.com/maharmstone/btrfs.
-reactos/drivers/filesystems/btrfs # Synced to 0.5
+reactos/drivers/filesystems/btrfs # Synced to 0.6
The following FSD are shared with: http://www.ext2fsd.com/