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);