Upgrade the WinBtrfs to release 0.4.
CORE-11172 #resolve #Committed in r71265
svn path=/trunk/; revision=71265
crc32c.c
create.c
dirctrl.c
+ extent-tree.c
fastio.c
fileinfo.c
flushthread.c
+ free-space.c
fsctl.c
+ pnp.c
read.c
reparse.c
search.c
add_definitions(-D__KERNEL__)
set_module_type(btrfs kernelmodedriver)
-target_link_libraries(btrfs ntoskrnl_vista)
+target_link_libraries(btrfs ntoskrnl_vista ${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)
HANDLE log_handle = NULL;
#endif
+int __security_cookie = __LINE__;
+
static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
typedef struct {
}
#ifdef DEBUG_LONG_MESSAGES
-void STDCALL _debug_message(const char* func, UINT8 priority, const char* file, unsigned int line, char* s, ...) {
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
#else
-void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...) {
+void STDCALL _debug_message(const char* func, char* s, ...) {
#endif
LARGE_INTEGER offset;
PIO_STACK_LOCATION IrpSp;
read_context* context = NULL;
UINT32 length;
- if (log_started && priority > debug_log_level)
- return;
-
buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
if (!buf2) {
}
while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = prev_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
+ if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
r->lastinode = tp.item->key.obj_id;
- free_traverse_ptr(&tp);
TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
return TRUE;
}
}
- free_traverse_ptr(&tp);
-
r->lastinode = SUBVOL_ROOT_INODE;
WARN("no INODE_ITEMs in tree %llx\n", r->id);
if (keycmp(&tp.item->key, &searchkey)) {
TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
return FALSE;
}
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
- free_traverse_ptr(&tp);
return FALSE;
}
while (TRUE) {
if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- free_traverse_ptr(&tp);
return FALSE;
}
*data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
if (!*data) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return FALSE;
}
} else
*data = NULL;
- free_traverse_ptr(&tp);
return TRUE;
}
TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
-
return FALSE;
}
xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
+ // FIXME - make sure xasize not too big
+
if (!keycmp(&tp.item->key, &searchkey)) { // key exists
UINT8* newdata;
ULONG size = tp.item->size;
newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
if (!newdata) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
if (!newdata) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
if (!xa) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback);
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
- free_traverse_ptr(&tp);
return FALSE;
} else {
xa = (DIR_ITEM*)tp.item->data;
if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- free_traverse_ptr(&tp);
return FALSE;
}
if (newsize == 0) {
TRACE("xattr %s deleted\n", name);
- free_traverse_ptr(&tp);
return TRUE;
}
newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
if (!newdata) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return FALSE;
}
insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback);
- free_traverse_ptr(&tp);
return TRUE;
}
if (xa->m + xa->n >= size) { // FIXME - test this works
WARN("xattr %s not found\n", name);
- free_traverse_ptr(&tp);
return FALSE;
} else {
}
} else {
WARN("xattr %s not found\n", name);
- free_traverse_ptr(&tp);
return FALSE;
}
if (tp.item->size + disize > maxlen) {
WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
if (!di2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
} else {
insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
}
-
- free_traverse_ptr(&tp);
return STATUS_SUCCESS;
}
if (!keycmp(&searchkey, &tp.item->key)) {
if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = prev_tp;
TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
} else
dirpos = 2;
- free_traverse_ptr(&tp);
-
return dirpos;
}
static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
BOOL top_level;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
FsRtlEnterFileSystem();
// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
- Status = write_file(DeviceObject, Irp);
+ _SEH2_TRY {
+ if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
+ CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress);
+
+ Irp->MdlAddress = NULL;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ } else {
+ Status = write_file(DeviceObject, Irp);
+ }
+ } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
Irp->IoStatus.Status = Status;
// }
// }
+// static void test_dropping_tree(device_extension* Vcb) {
+// LIST_ENTRY* le = Vcb->roots.Flink;
+//
+// while (le != &Vcb->roots) {
+// root* r = CONTAINING_RECORD(le, root, list_entry);
+//
+// if (r->id == 0x101) {
+// RemoveEntryList(&r->list_entry);
+// InsertTailList(&Vcb->drop_roots, &r->list_entry);
+// return;
+// }
+//
+// le = le->Flink;
+// }
+// }
+
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
+ root* r;
+ tree* t;
+ ROOT_ITEM* ri;
+ traverse_ptr tp;
+
+ r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
+ if (!r) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
+ if (!r->nonpaged) {
+ ERR("out of memory\n");
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (!no_tree) {
+ t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ if (!t) {
+ ERR("out of memory\n");
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+ if (!ri) {
+ ERR("out of memory\n");
+
+ if (!no_tree)
+ ExFreePool(t);
+
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ r->id = id;
+ r->treeholder.address = 0;
+ r->treeholder.generation = Vcb->superblock.generation;
+ r->treeholder.tree = no_tree ? NULL : t;
+ r->lastinode = 0;
+ r->path.Buffer = NULL;
+ RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
+ r->root_item.num_references = 1;
+ InitializeListHead(&r->fcbs);
+
+ RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
+
+ // 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)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ri);
+
+ if (!no_tree)
+ ExFreePool(t);
+
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
+
+ InsertTailList(&Vcb->roots, &r->list_entry);
+
+ if (!no_tree) {
+ t->header.fs_uuid = tp.tree->header.fs_uuid;
+ t->header.address = 0;
+ t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
+ t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
+ t->header.generation = Vcb->superblock.generation;
+ t->header.tree_id = id;
+ t->header.num_items = 0;
+ t->header.level = 0;
+
+ t->has_address = FALSE;
+ t->size = 0;
+ t->Vcb = Vcb;
+ t->parent = NULL;
+ t->paritem = NULL;
+ t->root = r;
+
+ InitializeListHead(&t->itemlist);
+
+ t->new_address = 0;
+ t->has_new_address = FALSE;
+ t->flags = tp.tree->flags;
+
+ InsertTailList(&Vcb->trees, &t->list_entry);
+
+ t->write = TRUE;
+ Vcb->write_trees++;
+ }
+
+ *rootptr = r;
+
+ return STATUS_SUCCESS;
+}
+
+// static void test_creating_root(device_extension* Vcb) {
+// NTSTATUS Status;
+// LIST_ENTRY rollback;
+// UINT64 id;
+// root* r;
+//
+// InitializeListHead(&rollback);
+//
+// if (Vcb->root_root->lastinode == 0)
+// get_last_inode(Vcb, Vcb->root_root);
+//
+// id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
+// Status = create_root(Vcb, id, &r, &rollback);
+//
+// if (!NT_SUCCESS(Status)) {
+// ERR("create_root returned %08x\n", Status);
+// do_rollback(Vcb, &rollback);
+// } else {
+// Vcb->root_root->lastinode = id;
+// clear_rollback(&rollback);
+// }
+// }
+
static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
ULONG utf8len;
NTSTATUS Status;
// test_tree_deletion(Vcb); // TESTING
// test_tree_splitting(Vcb);
+// test_dropping_tree(Vcb);
+// test_creating_root(Vcb);
Status = consider_write(Vcb);
goto end;
}
+ if (Vcb->removing) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
case FileFsControlInformation:
FIXME("STUB: FileFsControlInformation\n");
if (!newdi) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
if (!newir) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
}
- free_traverse_ptr(&tp);
-
if (changed)
return STATUS_SUCCESS;
if (!newier) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
}
- free_traverse_ptr(&tp);
-
return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
}
-NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+static NTSTATUS delete_subvol(file_ref* fileref, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ UINT64 index;
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT32 crc32;
+ ROOT_ITEM* ri;
+ BOOL no_ref = FALSE;
+ fcb* fcb = fileref->fcb;
+
+ // delete ROOT_REF in root tree
+
+ Status = delete_root_ref(fcb->Vcb, fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->utf8, &index, rollback);
+
+ // A bug in Linux means that if you create a snapshot of a subvol containing another subvol,
+ // the ROOT_REF and ROOT_BACKREF items won't be created, nor will num_references of ROOT_ITEM
+ // be increased. In this case, we just unlink the subvol from its parent, and don't worry
+ // about anything else.
+
+ if (Status == STATUS_NOT_FOUND)
+ no_ref = TRUE;
+ else if (!NT_SUCCESS(Status)) {
+ ERR("delete_root_ref returned %08x\n", Status);
+ return Status;
+ }
+
+ if (!no_ref) {
+ // delete ROOT_BACKREF in root tree
+
+ Status = update_root_backref(fcb->Vcb, fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_root_backref returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ // delete DIR_ITEM in parent
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+ Status = delete_dir_item(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, &fileref->utf8, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_dir_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // delete DIR_INDEX in parent
+
+ if (!no_ref) {
+ searchkey.obj_id = fileref->parent->fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = index;
+
+ Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ if (!keycmp(&searchkey, &tp.item->key)) {
+ delete_tree_item(fcb->Vcb, &tp, rollback);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ } else {
+ BOOL b;
+ traverse_ptr next_tp;
+
+ // If we have no ROOT_REF, we have to look through all the DIR_INDEX entries manually :-(
+
+ searchkey.obj_id = fileref->parent->fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 0;
+
+ Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item 1 returned %08x\n", Status);
+ return Status;
+ }
+
+ do {
+ if (tp.item->key.obj_type == TYPE_DIR_INDEX && tp.item->size >= sizeof(DIR_ITEM)) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+
+ if (di->key.obj_id == fcb->subvol->id && di->key.obj_type == TYPE_ROOT_ITEM && di->n == fileref->utf8.Length &&
+ tp.item->size >= sizeof(DIR_ITEM) - 1 + di->m + di->n && RtlCompareMemory(fileref->utf8.Buffer, di->name, di->n) == di->n) {
+ delete_tree_item(fcb->Vcb, &tp, rollback);
+ break;
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
+ break;
+ }
+ } while (b);
+ }
+
+ if (no_ref)
+ return STATUS_SUCCESS;
+
+ if (fcb->subvol->root_item.num_references > 1) {
+ UINT64 offset;
+
+ // change ROOT_ITEM num_references
+
+ fcb->subvol->root_item.num_references--;
+
+ searchkey.obj_id = fcb->subvol->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(fcb->Vcb, fcb->Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item 2 returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(fcb->Vcb, &tp, rollback);
+ TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ offset = tp.item->key.offset;
+ } else {
+ ERR("could not find ROOT_ITEM for subvol %llx\n", fcb->subvol->id);
+ offset = 0;
+ }
+
+ ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+ if (!ri) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(ri, &fcb->subvol->root_item, sizeof(ROOT_ITEM));
+
+ if (!insert_tree_item(fcb->Vcb, fcb->Vcb->root_root, fcb->subvol->id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ RemoveEntryList(&fcb->subvol->list_entry);
+
+ InsertTailList(&fcb->Vcb->drop_roots, &fcb->subvol->list_entry);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static WCHAR* file_desc_fcb(fcb* fcb) {
+ char s[60];
+ UNICODE_STRING us;
+ ANSI_STRING as;
+
+ if (fcb->debug_desc)
+ return fcb->debug_desc;
+
+ fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
+ if (!fcb->debug_desc)
+ return L"(memory error)";
+
+ // I know this is pretty hackish...
+ // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
+ // without the CRT, which breaks drivers.
+
+ sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
+
+ as.Buffer = s;
+ as.Length = as.MaximumLength = strlen(s);
+
+ us.Buffer = fcb->debug_desc;
+ us.MaximumLength = 60 * sizeof(WCHAR);
+ us.Length = 0;
+
+ RtlAnsiStringToUnicodeString(&us, &as, FALSE);
+
+ us.Buffer[us.Length / sizeof(WCHAR)] = 0;
+
+ return fcb->debug_desc;
+}
+
+WCHAR* file_desc_fileref(file_ref* fileref) {
+ if (fileref->debug_desc)
+ return fileref->debug_desc;
+
+ fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length + sizeof(WCHAR), ALLOC_TAG);
+ if (!fileref->debug_desc)
+ return L"(memory error)";
+
+ RtlCopyMemory(fileref->debug_desc, fileref->full_filename.Buffer, fileref->full_filename.Length);
+ fileref->debug_desc[fileref->full_filename.Length / sizeof(WCHAR)] = 0;
+
+ return fileref->debug_desc;
+}
+
+WCHAR* file_desc(PFILE_OBJECT FileObject) {
+ fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
+
+ if (fileref)
+ return file_desc_fileref(fileref);
+ else
+ return file_desc_fcb(fcb);
+}
+
+void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
+ fcb* fcb = fileref->fcb;
+
+ FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fileref->full_filename, fileref->name_offset * sizeof(WCHAR),
+ NULL, NULL, filter_match, action, NULL);
+}
+
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
ULONG bytecount;
NTSTATUS Status;
char* utf8 = NULL;
KEY searchkey;
traverse_ptr tp, tp2;
UINT64 parinode, index;
+ root* parsubvol;
INODE_ITEM *ii, *dirii;
LARGE_INTEGER time;
BTRFS_TIME now;
LIST_ENTRY changed_sector_list;
+ fcb* fcb = fileref->fcb;
#ifdef _DEBUG
LARGE_INTEGER freq, time1, time2;
#endif
- // FIXME - throw error if try to delete subvol root(?)
-
- // FIXME - delete all children if deleting directory
-
- if (fcb->deleted) {
+ if (fileref->deleted || fcb->deleted) {
WARN("trying to delete already-deleted file\n");
return STATUS_SUCCESS;
}
- if (!fcb->par) {
+ if (fileref == fcb->Vcb->root_fileref) {
ERR("error - trying to delete root FCB\n");
return STATUS_INTERNAL_ERROR;
}
+ if (fcb->inode == SUBVOL_ROOT_INODE) {
+ Status = delete_subvol(fileref, rollback);
+
+ if (!NT_SUCCESS(Status))
+ goto exit;
+ else {
+ parinode = fileref->parent->fcb->inode;
+ parsubvol = fileref->parent->fcb->subvol;
+ bytecount = fileref->utf8.Length;
+ goto success2;
+ }
+ }
+
#ifdef _DEBUG
time1 = KeQueryPerformanceCounter(&freq);
#endif
RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length);
s[fcb->adsxattr.Length] = 0;
- if (!delete_xattr(fcb->Vcb, fcb->par->subvol, fcb->par->inode, s, fcb->adshash, rollback)) {
+ if (!delete_xattr(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, s, fcb->adshash, rollback)) {
ERR("failed to delete xattr %s\n", s);
}
ExFreePool(s);
- fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
+ fileref->parent->fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+ fileref->parent->fcb->inode_item.sequence++;
+ fileref->parent->fcb->inode_item.st_ctime = now;
- searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_id = fileref->parent->fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(fcb->Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->par->inode, fcb->par->subvol->id);
- free_traverse_ptr(&tp);
+ ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->subvol->id);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
- RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
delete_tree_item(fcb->Vcb, &tp, rollback);
- insert_tree_item(fcb->Vcb, fcb->par->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
+ insert_tree_item(fcb->Vcb, fileref->parent->fcb->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
- free_traverse_ptr(&tp);
-
- fcb->par->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
- fcb->par->subvol->root_item.ctime = now;
+ fileref->parent->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fileref->parent->fcb->subvol->root_item.ctime = now;
goto success;
}
- Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
return Status;
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
+ RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length);
utf8[bytecount] = 0;
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount);
- TRACE("deleting %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("deleting %.*S\n", file_desc_fileref(fileref));
- if (fcb->par->subvol == fcb->subvol)
- parinode = fcb->par->inode;
+ if (fileref->parent->fcb->subvol == fcb->subvol)
+ parinode = fileref->parent->fcb->inode;
else
parinode = SUBVOL_ROOT_INODE;
+ parsubvol = fcb->subvol;
+
// delete DIR_ITEM (0x54)
- Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fcb->utf8, rollback);
+ Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fileref->utf8, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_dir_item returned %08x\n", Status);
return Status;
index = 0;
- Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fcb->utf8, &index, rollback);
+ Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fileref->utf8, &index, rollback);
// delete DIR_INDEX (0x60)
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
Status = find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
goto exit;
}
- free_traverse_ptr(&tp);
tp = tp2;
if (keycmp(&searchkey, &tp.item->key)) {
ERR("error - INODE_ITEM not found\n");
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if (tp.item->size < sizeof(INODE_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!newii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newii, sizeof(INODE_ITEM), NULL, rollback))
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
-
goto success2;
}
delete_tree_item(fcb->Vcb, &tp, rollback);
TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ fcb->deleted = TRUE;
+
// delete XATTR_ITEM (0x18)
while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
- free_traverse_ptr(&tp);
tp = tp2;
if (tp.item->key.obj_id == fcb->inode) {
break;
}
- free_traverse_ptr(&tp);
-
// excise extents
InitializeListHead(&changed_sector_list);
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fcb->Vcb, parsubvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_tree returned %08x\n", Status);
goto exit;
}
if (keycmp(&searchkey, &tp.item->key)) {
- ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, fcb->subvol->id);
- free_traverse_ptr(&tp);
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, parsubvol->id);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
- TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
- fcb->par->inode_item.st_size -= bytecount * 2;
- TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
- fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
- fcb->par->inode_item.st_mtime = now;
+ TRACE("fileref->parent->fcb->inode_item.st_size was %llx\n", fileref->parent->fcb->inode_item.st_size);
+ fileref->parent->fcb->inode_item.st_size -= bytecount * 2;
+ TRACE("fileref->parent->fcb->inode_item.st_size now %llx\n", fileref->parent->fcb->inode_item.st_size);
+ fileref->parent->fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+ fileref->parent->fcb->inode_item.sequence++;
+ fileref->parent->fcb->inode_item.st_ctime = now;
+ fileref->parent->fcb->inode_item.st_mtime = now;
dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!dirii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
- RtlCopyMemory(dirii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(dirii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
delete_tree_item(fcb->Vcb, &tp, rollback);
- insert_tree_item(fcb->Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
-
- free_traverse_ptr(&tp);
+ insert_tree_item(fcb->Vcb, parsubvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
- fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
- fcb->subvol->root_item.ctime = now;
+ parsubvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ parsubvol->root_item.ctime = now;
success:
consider_write(fcb->Vcb);
- fcb->deleted = TRUE;
+ fileref->deleted = TRUE;
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
// FIXME - set deleted flag of any open FCBs for ADS
- TRACE("sending notification for deletion of %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-
- FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
- fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
- FILE_ACTION_REMOVED, NULL);
+ if (FileObject && FileObject->FsContext2) {
+ ccb* ccb = FileObject->FsContext2;
+
+ if (ccb->fileref)
+ send_notification_fileref(ccb->fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
+ }
#ifdef _DEBUG
time2 = KeQueryPerformanceCounter(NULL);
}
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
- ULONG rc;
-
+ LONG rc;
+
rc = InterlockedDecrement(&fcb->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
// WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, 1, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ _debug_message(func, file, line, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
#else
- _debug_message(func, 1, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ _debug_message(func, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
#endif
#endif
return;
ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
-
- if (fcb->filepart.Buffer)
- RtlFreeUnicodeString(&fcb->filepart);
ExDeleteResourceLite(&fcb->nonpaged->resource);
ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
ExFreePool(fcb->nonpaged);
- if (fcb->par/* && fcb->par != fcb->par->Vcb->root_fcb*/) {
+ if (fcb->list_entry.Flink)
RemoveEntryList(&fcb->list_entry);
- _free_fcb(fcb->par, func, file, line);
- }
-
- if (fcb->prev)
- fcb->prev->next = fcb->next;
-
- if (fcb->next)
- fcb->next->prev = fcb->prev;
-
- if (fcb->Vcb->fcbs == fcb)
- fcb->Vcb->fcbs = fcb->next;
-
- if (fcb->full_filename.Buffer)
- ExFreePool(fcb->full_filename.Buffer);
if (fcb->sd)
ExFreePool(fcb->sd);
if (fcb->adsxattr.Buffer)
ExFreePool(fcb->adsxattr.Buffer);
- if (fcb->utf8.Buffer)
- ExFreePool(fcb->utf8.Buffer);
+ if (fcb->debug_desc)
+ ExFreePool(fcb->debug_desc);
FsRtlUninitializeFileLock(&fcb->lock);
ExFreePool(fcb);
#ifdef DEBUG_FCB_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, 1, file, line, "freeing fcb %p\n", fcb);
+ _debug_message(func, file, line, "freeing fcb %p\n", fcb);
+#else
+ _debug_message(func, "freeing fcb %p\n", fcb);
+#endif
+#endif
+}
+
+void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
+ LONG rc;
+
+ rc = InterlockedDecrement(&fr->refcount);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+ _debug_message(func, file, line, "fileref %p: refcount now %i\n", fr, rc);
#else
- _debug_message(func, 1, "freeing fcb %p\n", fcb);
+ _debug_message(func, "fileref %p: refcount now %i\n", fr, rc);
+#endif
#endif
+
+#ifdef _DEBUG
+ if (rc < 0) {
+ ERR("fileref %p: refcount now %i\n", fr, rc);
+ int3;
+ }
#endif
+
+ if (rc > 0)
+ return;
+
+ // FIXME - do we need a file_ref lock?
+
+ // FIXME - do delete if needed
+
+ if (fr->filepart.Buffer)
+ ExFreePool(fr->filepart.Buffer);
+
+ if (fr->utf8.Buffer)
+ ExFreePool(fr->utf8.Buffer);
+
+ if (fr->full_filename.Buffer)
+ ExFreePool(fr->full_filename.Buffer);
+
+ if (fr->debug_desc)
+ ExFreePool(fr->debug_desc);
+
+ // FIXME - throw error if children not empty
+
+ free_fcb(fr->fcb);
+
+ if (fr->list_entry.Flink)
+ RemoveEntryList(&fr->list_entry);
+
+ if (fr->parent)
+ free_fileref((file_ref*)fr->parent);
+
+ ExFreePool(fr);
}
static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
fcb* fcb;
ccb* ccb;
+ file_ref* fileref = NULL;
TRACE("FileObject = %p\n", FileObject);
ccb = FileObject->FsContext2;
- TRACE("close called for %.*S (fcb == %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+ TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
if (ccb->query_string.Buffer)
RtlFreeUnicodeString(&ccb->query_string);
+ // FIXME - use refcounts for fileref
+ fileref = ccb->fileref;
+
ExFreePool(ccb);
}
CcUninitializeCacheMap(FileObject, NULL, NULL);
- free_fcb(fcb);
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+
+ if (fileref)
+ free_fileref(fileref);
+ else
+ free_fcb(fcb);
+
+ ExReleaseResourceLite(&Vcb->fcb_lock);
return STATUS_SUCCESS;
}
-static void STDCALL uninit(device_extension* Vcb) {
+void STDCALL uninit(device_extension* Vcb, BOOL flush) {
chunk* c;
space* s;
UINT64 i;
LIST_ENTRY rollback;
- InitializeListHead(&rollback);
-
- acquire_tree_lock(Vcb, TRUE);
+ if (flush) {
+ InitializeListHead(&rollback);
+
+ acquire_tree_lock(Vcb, TRUE);
- if (Vcb->write_trees > 0)
- do_write(Vcb, &rollback);
-
- free_tree_cache(&Vcb->tree_cache);
-
- clear_rollback(&rollback);
+ if (Vcb->write_trees > 0)
+ do_write(Vcb, &rollback);
+
+ free_trees(Vcb);
+
+ clear_rollback(&rollback);
- release_tree_lock(Vcb, TRUE);
+ release_tree_lock(Vcb, TRUE);
+ }
- while (Vcb->roots) {
- root* r = Vcb->roots->next;
+ while (!IsListEmpty(&Vcb->roots)) {
+ LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
+ root* r = CONTAINING_RECORD(le, root, list_entry);
- ExDeleteResourceLite(&Vcb->roots->nonpaged->load_tree_lock);
- ExFreePool(Vcb->roots->nonpaged);
- ExFreePool(Vcb->roots);
-
- Vcb->roots = r;
+ ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
}
while (!IsListEmpty(&Vcb->chunks)) {
}
free_fcb(Vcb->volume_fcb);
- free_fcb(Vcb->root_fcb);
+ free_fileref(Vcb->root_fileref);
for (i = 0; i < Vcb->superblock.num_devices; i++) {
while (!IsListEmpty(&Vcb->devices[i].disk_holes)) {
goto exit;
}
- if (FileObject) {
+ if (FileObject && FileObject->FsContext) {
LONG oc;
+ ccb* ccb;
+ file_ref* fileref;
fcb = FileObject->FsContext;
+ ccb = FileObject->FsContext2;
+ fileref = ccb ? ccb->fileref : NULL;
TRACE("cleanup called for FileObject %p\n", FileObject);
- TRACE("fcb %p (%.*S), refcount = %u, open_count = %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount, fcb->open_count);
+ TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
IoRemoveShareAccess(FileObject, &fcb->share_access);
ERR("fcb %p: open_count now %i\n", fcb, oc);
#endif
+ if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
+ fileref->delete_on_close = TRUE;
+
+ if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
+ fileref->delete_on_close = FALSE;
+
if (oc == 0) {
- if (fcb->delete_on_close && fcb != fcb->Vcb->root_fcb && fcb != fcb->Vcb->volume_fcb) {
+ if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
LIST_ENTRY rollback;
InitializeListHead(&rollback);
acquire_tree_lock(fcb->Vcb, TRUE);
- Status = delete_fcb(fcb, FileObject, &rollback);
+ Status = delete_fileref(fileref, FileObject, &rollback);
if (NT_SUCCESS(Status)) {
LARGE_INTEGER newlength;
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 (ealen > 2) {
if (eaval[0] == '0' && eaval[1] == 'x') {
ExFreePool(eaval);
+ if (type == BTRFS_TYPE_DIRECTORY)
+ dosnum |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (type == BTRFS_TYPE_SYMLINK)
+ dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
+
return dosnum;
}
}
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
- ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override)
+NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
+ PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
{
PIRP Irp;
KEVENT Event;
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
}
+
+ if (iosb)
+ *iosb = IoStatus;
return Status;
}
}
r->id = id;
+ r->path.Buffer = NULL;
r->treeholder.address = addr;
r->treeholder.tree = NULL;
init_tree_holder(&r->treeholder);
- r->prev = NULL;
- r->next = Vcb->roots;
+ InitializeListHead(&r->fcbs);
r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
if (!r->nonpaged) {
RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
}
- if (Vcb->roots)
- Vcb->roots->prev = r;
-
- Vcb->roots = r;
+ InsertTailList(&Vcb->roots, &r->list_entry);
switch (r->id) {
case BTRFS_ROOT_ROOT:
case BTRFS_ROOT_CHECKSUM:
Vcb->checksum_root = r;
break;
+
+ case BTRFS_ROOT_UUID:
+ Vcb->uuid_root = r;
+ break;
}
return STATUS_SUCCESS;
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- if (b) {
- free_traverse_ptr(&tp);
+ if (b)
tp = next_tp;
- }
} while (b);
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
break;
}
} while (b);
- free_traverse_ptr(&tp);
-
if (lastaddr < dev->devitem.num_bytes) {
Status = add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
if (!NT_SUCCESS(Status)) {
c->offset = tp.item->key.offset;
c->used = c->oldused = 0;
c->space_changed = FALSE;
+ c->cache_inode = 0;
+ c->cache_size = 0;
c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- if (b) {
- free_traverse_ptr(&tp);
+ if (b)
tp = next_tp;
- }
} while (b);
- free_traverse_ptr(&tp);
-
Vcb->log_to_phys_loaded = TRUE;
return STATUS_SUCCESS;
}
-static BOOL load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
- KEY searchkey;
- traverse_ptr tp, tp2;
- FREE_SPACE_ITEM* fsi;
- UINT64 inode, num_sectors, i, generation;
- INODE_ITEM* ii;
- UINT8* data;
- NTSTATUS Status;
- UINT32 *checksums, crc32;
-#ifdef _DEBUG
- FREE_SPACE_ENTRY* fse;
- UINT64 num_entries;
-#endif
-
- TRACE("(%p, %llx)\n", Vcb, c->offset);
-
- if (Vcb->superblock.generation != Vcb->superblock.cache_generation)
- return FALSE;
-
- searchkey.obj_id = FREE_SPACE_CACHE_ID;
- searchkey.obj_type = 0;
- searchkey.offset = c->offset;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return FALSE;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
- 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));
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- fsi = (FREE_SPACE_ITEM*)tp.item->data;
-
- if (fsi->generation != Vcb->superblock.cache_generation) {
- WARN("cache had generation %llx, expecting %llx\n", fsi->generation, Vcb->superblock.cache_generation);
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if (fsi->key.obj_type != TYPE_INODE_ITEM) {
- WARN("cache pointed to something other than an INODE_ITEM\n");
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if (fsi->num_bitmaps > 0) {
- WARN("cache had bitmaps, unsure of how to deal with these\n");
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- inode = fsi->key.obj_id;
-
- searchkey = fsi->key;
-#ifdef _DEBUG
- num_entries = fsi->num_entries;
-#endif
-
- Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- free_traverse_ptr(&tp);
-
- if (keycmp(&tp2.item->key, &searchkey)) {
- WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- if (tp2.item->size < sizeof(INODE_ITEM)) {
- WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, sizeof(INODE_ITEM));
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- ii = (INODE_ITEM*)tp2.item->data;
-
- data = ExAllocatePoolWithTag(PagedPool, ii->st_size, ALLOC_TAG);
-
- if (!data) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL);
- if (!NT_SUCCESS(Status)) {
- ERR("read_file returned %08x\n", Status);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- num_sectors = ii->st_size / Vcb->superblock.sector_size;
-
- generation = *(data + (num_sectors * sizeof(UINT32)));
-
- if (generation != Vcb->superblock.cache_generation) {
- ERR("generation was %llx, expected %llx\n", generation, Vcb->superblock.cache_generation);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * num_sectors, ALLOC_TAG); // FIXME - get rid of this
-
- if (!checksums) {
- ERR("out of memory\n");
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
-
- RtlCopyMemory(checksums, data, sizeof(UINT32) * num_sectors);
-
- for (i = 0; i < num_sectors; i++) {
- if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
- crc32 = ~calc_crc32c(0xffffffff, &data[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
- else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
- crc32 = 0; // FIXME - test this
- else
- crc32 = ~calc_crc32c(0xffffffff, &data[sizeof(UINT32) * num_sectors], ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
-
- if (crc32 != checksums[i]) {
- WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]);
- ExFreePool(checksums);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
- }
-
- ExFreePool(checksums);
-
-#ifdef _DEBUG
- fse = (FREE_SPACE_ENTRY*)&data[(sizeof(UINT32) * num_sectors) + sizeof(UINT64)];
-
- for (i = 0; i < num_entries; i++) {
- TRACE("(%llx,%llx,%x)\n", fse[i].offset, fse[i].size, fse[i].type);
- }
-#endif
-
- FIXME("FIXME - read cache\n");
-
- ExFreePool(data);
- free_traverse_ptr(&tp2);
-
- return FALSE;
-}
-
-static NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
- traverse_ptr tp, next_tp;
- KEY searchkey;
- UINT64 lastaddr;
- BOOL b;
- space *s, *s2;
- LIST_ENTRY* le;
- NTSTATUS Status;
-
- load_stored_free_space_cache(Vcb, c);
-
- TRACE("generating free space cache for chunk %llx\n", c->offset);
-
- searchkey.obj_id = c->offset;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- lastaddr = c->offset;
-
- do {
- if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
- break;
-
- if (tp.item->key.obj_id >= c->offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
- if (tp.item->key.obj_id > lastaddr) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- s->offset = lastaddr;
- s->size = tp.item->key.obj_id - lastaddr;
- s->type = SPACE_TYPE_FREE;
- InsertTailList(&c->space, &s->list_entry);
-
- TRACE("(%llx,%llx)\n", s->offset, s->size);
- }
-
- if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
- lastaddr = tp.item->key.obj_id + Vcb->superblock.node_size;
- else
- lastaddr = tp.item->key.obj_id + tp.item->key.offset;
- }
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- if (b) {
- free_traverse_ptr(&tp);
- tp = next_tp;
- }
- } while (b);
-
- if (lastaddr < c->offset + c->chunk_item->size) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- s->offset = lastaddr;
- s->size = c->offset + c->chunk_item->size - lastaddr;
- s->type = SPACE_TYPE_FREE;
- InsertTailList(&c->space, &s->list_entry);
-
- TRACE("(%llx,%llx)\n", s->offset, s->size);
- }
-
- free_traverse_ptr(&tp);
-
- // add allocated space
-
- lastaddr = c->offset;
-
- le = c->space.Flink;
- while (le != &c->space) {
- s = CONTAINING_RECORD(le, space, list_entry);
-
- if (s->offset > lastaddr) {
- s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s2) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- s2->offset = lastaddr;
- s2->size = s->offset - lastaddr;
- s2->type = SPACE_TYPE_USED;
-
- InsertTailList(&s->list_entry, &s2->list_entry);
- }
-
- lastaddr = s->offset + s->size;
-
- le = le->Flink;
- }
-
- if (lastaddr < c->offset + c->chunk_item->size) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- s->offset = lastaddr;
- s->size = c->offset + c->chunk_item->size - lastaddr;
- s->type = SPACE_TYPE_USED;
- InsertTailList(&c->space, &s->list_entry);
- }
-
- le = c->space.Flink;
- while (le != &c->space) {
- s = CONTAINING_RECORD(le, space, list_entry);
-
- TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type);
-
- le = le->Flink;
- }
-
- return STATUS_SUCCESS;
-}
-
void protect_superblocks(device_extension* Vcb, chunk* c) {
int i = 0, j;
- UINT64 addr;
+ UINT64 off_start, off_end;
// FIXME - this will need modifying for RAID
for (j = 0; j < ci->num_stripes; j++) {
if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
- UINT32 size;
-
TRACE("cut out superblock in chunk %llx\n", c->offset);
- addr = (superblock_addrs[i] - cis[j].offset) + c->offset;
- TRACE("addr %llx\n", addr);
+ // The Linux driver protects the whole stripe in which the superblock lives
- // This prevents trees from spanning a stripe boundary, which btrfs check complains
- // about. It also prevents the chunk tree being placed at 0x11000, which for some
- // reason makes the FS unmountable on Linux (it tries to read 0x10000, i.e. the
- // superblock, instead).
- if (ci->type & BLOCK_FLAG_SYSTEM || ci->type & BLOCK_FLAG_METADATA)
- size = max(sizeof(superblock), Vcb->superblock.node_size);
- else
- size = sizeof(superblock);
+ off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
+ off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
- add_to_space_list(c, addr, size, SPACE_TYPE_USED);
+ add_to_space_list(c, c->offset + off_start, off_end - off_start, SPACE_TYPE_USED);
}
}
}
}
- free_traverse_ptr(&tp);
// if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
// cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
//
UINT64 inode;
UINT8 type;
UNICODE_STRING filename;
+ LIST_ENTRY* le;
static WCHAR fn[] = L"default";
static UINT32 crc32 = 0x8dbfc2d2;
return subvol;
}
- subvol = Vcb->roots;
- while (subvol && subvol->id != BTRFS_ROOT_FSTREE)
- subvol = subvol->next;
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == BTRFS_ROOT_FSTREE)
+ return r;
+
+ le = le->Flink;
+ }
- return subvol;
+ return NULL;
+}
+
+static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
+ NTSTATUS Status;
+ STORAGE_HOTPLUG_INFO shi;
+
+ Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return FALSE;
+ }
+
+ return shi.MediaRemovable != 0 ? TRUE : FALSE;
+}
+
+static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
+ NTSTATUS Status;
+ ULONG cc;
+ IO_STATUS_BLOCK iosb;
+
+ Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return 0;
+ }
+
+ if (iosb.Information < sizeof(ULONG)) {
+ ERR("iosb.Information was too short\n");
+ return 0;
+ }
+
+ return cc;
}
static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
LIST_ENTRY* le;
KEY searchkey;
traverse_ptr tp;
+ fcb* root_fcb = NULL;
TRACE("mount_vol called\n");
// }
Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
- &piex, sizeof(piex), TRUE);
+ &piex, sizeof(piex), TRUE, NULL);
if (!NT_SUCCESS(Status)) {
ERR("error reading partition information: %08x\n", Status);
+ Status = STATUS_UNRECOGNIZED_VOLUME;
goto exit;
}
0,
FALSE,
&NewDeviceObject);
- if (!NT_SUCCESS(Status))
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateDevice returned %08x\n", Status);
+ Status = STATUS_UNRECOGNIZED_VOLUME;
goto exit;
+ }
// TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
Vcb = (PVOID)NewDeviceObject->DeviceExtension;
RtlZeroMemory(Vcb, sizeof(device_extension));
- InitializeListHead(&Vcb->tree_cache);
-
ExInitializeResourceLite(&Vcb->tree_lock);
Vcb->tree_lock_counter = 0;
Vcb->open_trees = 0;
Vcb->devices[0].devobj = DeviceToMount;
RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
+ Vcb->devices[0].removable = is_device_removable(Vcb->devices[0].devobj);
+ Vcb->devices[0].change_count = Vcb->devices[0].removable ? get_device_change_count(Vcb->devices[0].devobj) : 0;
if (Vcb->superblock.num_devices > 1)
RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
- Vcb->roots = NULL;
+ InitializeListHead(&Vcb->roots);
+ InitializeListHead(&Vcb->drop_roots);
+
Vcb->log_to_phys_loaded = FALSE;
Vcb->max_inline = Vcb->superblock.node_size / 2;
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);
+ if (!NT_SUCCESS(Status)) {
+ ERR("clear_free_space_cache returned %08x\n", Status);
+ goto exit;
+ }
+ }
Vcb->volume_fcb = create_fcb();
if (!Vcb->volume_fcb) {
Vcb->volume_fcb->Vcb = Vcb;
Vcb->volume_fcb->sd = NULL;
- Vcb->root_fcb = create_fcb();
- if (!Vcb->root_fcb) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
- }
-
- Vcb->root_fcb->Vcb = Vcb;
- Vcb->root_fcb->inode = SUBVOL_ROOT_INODE;
- Vcb->root_fcb->type = BTRFS_TYPE_DIRECTORY;
-
- Vcb->root_fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
-
- if (!Vcb->root_fcb->full_filename.Buffer) {
+ root_fcb = create_fcb();
+ if (!root_fcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
- Vcb->root_fcb->full_filename.Buffer[0] = '\\';
- Vcb->root_fcb->full_filename.Length = Vcb->root_fcb->full_filename.MaximumLength = sizeof(WCHAR);
+ root_fcb->Vcb = Vcb;
+ root_fcb->inode = SUBVOL_ROOT_INODE;
+ root_fcb->type = BTRFS_TYPE_DIRECTORY;
#ifdef DEBUG_FCB_REFCOUNTS
WARN("volume FCB = %p\n", Vcb->volume_fcb);
- WARN("root FCB = %p\n", Vcb->root_fcb);
+ WARN("root FCB = %p\n", root_fcb);
#endif
- Vcb->root_fcb->subvol = find_default_subvol(Vcb);
+ root_fcb->subvol = find_default_subvol(Vcb);
- if (!Vcb->root_fcb->subvol) {
+ if (!root_fcb->subvol) {
ERR("could not find top subvol\n");
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
- Vcb->fcbs = Vcb->root_fcb;
-
- searchkey.obj_id = Vcb->root_fcb->inode;
+ searchkey.obj_id = root_fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, Vcb->root_fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("couldn't find INODE_ITEM for root directory\n");
Status = STATUS_INTERNAL_ERROR;
- free_traverse_ptr(&tp);
goto exit;
}
if (tp.item->size > 0)
- RtlCopyMemory(&Vcb->root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
+ RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
- free_traverse_ptr(&tp);
+ fcb_get_sd(root_fcb, NULL);
- fcb_get_sd(Vcb->root_fcb);
+ root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
- Vcb->root_fcb->atts = get_file_attributes(Vcb, &Vcb->root_fcb->inode_item, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, Vcb->root_fcb->type,
- FALSE, FALSE);
-
+ Vcb->root_fileref = create_fileref();
+ if (!Vcb->root_fileref) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Vcb->root_fileref->fcb = root_fcb;
+ InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
+
+ Vcb->root_fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
+
+ if (!Vcb->root_fileref->full_filename.Buffer) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Vcb->root_fileref->full_filename.Buffer[0] = '\\';
+ Vcb->root_fileref->full_filename.Length = Vcb->root_fileref->full_filename.MaximumLength = sizeof(WCHAR);
+
for (i = 0; i < Vcb->superblock.num_devices; i++) {
Status = find_disk_holes(Vcb, &Vcb->devices[i]);
if (!NT_SUCCESS(Status)) {
// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
// Vcb->NtfsInfo.VolumeLabel,
// Vcb->NtfsInfo.VolumeLabelLength);
-
- Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, Vcb);
- if (!NT_SUCCESS(Status)) {
- ERR("PsCreateSystemThread returned %08x\n", Status);
- goto exit;
- }
NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
NewDeviceObject->Vpb->VolumeLabel[1] = 0;
NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+ Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+ goto exit;
+ }
+
Status = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Status)) {
if (Vcb) {
- if (Vcb->root_fcb)
- free_fcb(Vcb->root_fcb);
+ if (Vcb->root_fileref)
+ free_fileref(Vcb->root_fileref);
+ else if (root_fcb)
+ free_fcb(root_fcb);
if (Vcb->volume_fcb)
free_fcb(Vcb->volume_fcb);
case IRP_MN_KERNEL_CALL:
TRACE("IRP_MN_KERNEL_CALL\n");
- break;
- case IRP_MN_LOAD_FILE_SYSTEM:
- TRACE("IRP_MN_LOAD_FILE_SYSTEM\n");
+ status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE);
break;
case IRP_MN_USER_FS_REQUEST:
break;
case IRP_MN_VERIFY_VOLUME:
- TRACE("IRP_MN_VERIFY_VOLUME\n");
+ FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
break;
default:
- WARN("unknown minor %u\n", IrpSp->MinorFunction);
break;
-
}
Irp->IoStatus.Status = status;
TRACE("lock control\n");
Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
if (top_level)
IoSetTopLevelIrp(NULL);
FIXME("FIXME - pass through\n");
Status = STATUS_NOT_IMPLEMENTED;
} else {
- TRACE("filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("filename = %S\n", file_desc(FileObject));
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
TRACE("shutting down Vcb %p\n", Vcb);
- uninit(Vcb);
+ uninit(Vcb, TRUE);
}
Irp->IoStatus.Status = Status;
return Status;
}
-static NTSTATUS STDCALL drv_pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- device_extension* Vcb = DeviceObject->DeviceExtension;
- NTSTATUS Status;
- BOOL top_level;
-
- FIXME("STUB: pnp\n");
+BOOL is_file_name_valid(PUNICODE_STRING us) {
+ ULONG i;
- FsRtlEnterFileSystem();
-
- top_level = is_top_level(Irp);
+ if (us->Length < sizeof(WCHAR))
+ return FALSE;
- Status = STATUS_NOT_IMPLEMENTED;
+ if (us->Length > 255 * sizeof(WCHAR))
+ return FALSE;
- switch (IrpSp->MinorFunction) {
- case IRP_MN_CANCEL_REMOVE_DEVICE:
- TRACE(" IRP_MN_CANCEL_REMOVE_DEVICE\n");
- break;
-
- case IRP_MN_QUERY_REMOVE_DEVICE:
- TRACE(" IRP_MN_QUERY_REMOVE_DEVICE\n");
- break;
-
- case IRP_MN_REMOVE_DEVICE:
- TRACE(" IRP_MN_REMOVE_DEVICE\n");
- break;
-
- case IRP_MN_START_DEVICE:
- TRACE(" IRP_MN_START_DEVICE\n");
- break;
-
- case IRP_MN_SURPRISE_REMOVAL:
- TRACE(" IRP_MN_SURPRISE_REMOVAL\n");
- break;
-
- case IRP_MN_QUERY_DEVICE_RELATIONS:
- TRACE(" IRP_MN_QUERY_DEVICE_RELATIONS\n");
- break;
-
- default:
- WARN("Unrecognized minor function 0x%x\n", IrpSp->MinorFunction);
- break;
+ for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
+ if (us->Buffer[i] == '/' || us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
+ us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))
+ return FALSE;
}
-
-// Irp->IoStatus.Status = Status;
-// Irp->IoStatus.Information = 0;
-
- IoSkipCurrentIrpStackLocation(Irp);
- Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
-
-// IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
- IoSetTopLevelIrp(NULL);
+ if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
+ return FALSE;
- FsRtlExitFileSystem();
-
- return Status;
+ return TRUE;
}
#ifdef _DEBUG
#define TYPE_DEV_EXTENT 0xCC
#define TYPE_DEV_ITEM 0xD8
#define TYPE_CHUNK_ITEM 0xE4
+#define TYPE_SUBVOL_UUID 0xFB
#define BTRFS_ROOT_ROOT 1
#define BTRFS_ROOT_EXTENT 2
#define BTRFS_ROOT_DEVTREE 4
#define BTRFS_ROOT_FSTREE 5
#define BTRFS_ROOT_CHECKSUM 7
+#define BTRFS_ROOT_UUID 9
#define BTRFS_COMPRESSION_NONE 0
#define BTRFS_COMPRESSION_ZLIB 1
} DEV_ITEM;
#define SYS_CHUNK_ARRAY_SIZE 0x800
+#define BTRFS_NUM_BACKUP_ROOTS 4
+
+typedef struct {
+ UINT64 root_tree_addr;
+ UINT64 root_tree_generation;
+ UINT64 chunk_tree_addr;
+ UINT64 chunk_tree_generation;
+ UINT64 extent_tree_addr;
+ UINT64 extent_tree_generation;
+ UINT64 fs_tree_addr;
+ UINT64 fs_tree_generation;
+ UINT64 dev_root_addr;
+ UINT64 dev_root_generation;
+ UINT64 csum_root_addr;
+ UINT64 csum_root_generation;
+ UINT64 total_bytes;
+ UINT64 bytes_used;
+ UINT64 num_devices;
+ UINT64 reserved[4];
+ UINT8 root_level;
+ UINT8 chunk_root_level;
+ UINT8 extent_root_level;
+ UINT8 fs_root_level;
+ UINT8 dev_root_level;
+ UINT8 csum_root_level;
+ UINT8 reserved2[10];
+} superblock_backup;
typedef struct {
UINT8 checksum[32];
UINT64 uuid_tree_generation;
UINT64 reserved[30];
UINT8 sys_chunk_array[SYS_CHUNK_ARRAY_SIZE];
-// struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
- UINT8 reserved2[1237];
+ superblock_backup backup[BTRFS_NUM_BACKUP_ROOTS];
+ UINT8 reserved2[565];
} superblock;
#define BTRFS_TYPE_UNKNOWN 0
UINT64 flags;
} EXTENT_ITEM;
+typedef struct {
+ KEY firstitem;
+ UINT8 level;
+} EXTENT_ITEM2;
+
typedef struct {
UINT32 refcount;
} EXTENT_ITEM_V0;
UINT64 root;
UINT64 gen;
UINT64 objid;
- UINT64 count;
+ UINT32 count;
} EXTENT_REF_V0;
typedef struct {
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "WinBtrfs"
- VALUE "FileVersion", "0.2"
+ VALUE "FileVersion", "0.4"
VALUE "InternalName", "btrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
VALUE "OriginalFilename", "btrfs.sys"
VALUE "ProductName", "WinBtrfs"
- VALUE "ProductVersion", "0.2"
+ VALUE "ProductVersion", "0.4"
END
END
BLOCK "VarFileInfo"
#ifdef __REACTOS__
#include <rtlfuncs.h>
#include <iotypes.h>
+#include <pseh/pseh2.h>
#endif /* __REACTOS__ */
//#include <windows.h>
#include <windef.h>
#include "btrfs.h"
#ifdef _DEBUG
-// #define DEBUG_TREE_REFCOUNTS
// #define DEBUG_FCB_REFCOUNTS
// #define DEBUG_LONG_MESSAGES
#define DEBUG_PARANOID
#define EA_DOSATTRIB "user.DOSATTRIB"
#define EA_DOSATTRIB_HASH 0x914f9939
+#define EA_REPARSE "system.reparse"
+#define EA_REPARSE_HASH 0x786f6167
+
#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
#ifdef _MSC_VER
struct _fcb_nonpaged* nonpaged;
LONG refcount;
LONG open_count;
- UNICODE_STRING filepart;
- ANSI_STRING utf8;
struct _device_extension* Vcb;
- struct _fcb* par;
- struct _fcb* prev;
- struct _fcb* next;
struct _root* subvol;
- LIST_ENTRY children;
UINT64 inode;
UINT8 type;
- BOOL delete_on_close;
INODE_ITEM inode_item;
- UNICODE_STRING full_filename;
- ULONG name_offset;
SECURITY_DESCRIPTOR* sd;
FILE_LOCK lock;
BOOL deleted;
PKTHREAD lazy_writer_thread;
ULONG atts;
SHARE_ACCESS share_access;
+ WCHAR* debug_desc;
BOOL ads;
UINT32 adssize;
LIST_ENTRY list_entry;
} fcb;
+struct _file_ref;
+
+typedef struct _file_ref {
+ fcb* fcb;
+ UNICODE_STRING filepart;
+ ANSI_STRING utf8;
+ UNICODE_STRING full_filename;
+ ULONG name_offset;
+ BOOL delete_on_close;
+ BOOL deleted;
+ LIST_ENTRY children;
+ LONG refcount;
+ struct _file_ref* parent;
+ WCHAR* debug_desc;
+
+ LIST_ENTRY list_entry;
+} file_ref;
+
typedef struct _ccb {
USHORT NodeType;
CSHORT NodeSize;
UNICODE_STRING query_string;
BOOL has_wildcard;
BOOL specific_file;
+ ACCESS_MASK access;
+ file_ref* fileref;
} ccb;
// typedef struct _log_to_phys {
// UINT64 address;
// UINT8 level;
tree_header header;
- LONG refcount;
BOOL has_address;
UINT32 size;
struct _device_extension* Vcb;
UINT64 new_address;
BOOL has_new_address;
UINT64 flags;
+ BOOL write;
} tree;
typedef struct {
root_nonpaged* nonpaged;
UINT64 lastinode;
ROOT_ITEM root_item;
-
- struct _root* prev;
- struct _root* next;
+ UNICODE_STRING path;
+ LIST_ENTRY fcbs;
+ LIST_ENTRY list_entry;
} root;
typedef struct {
tree_data* item;
} traverse_ptr;
-typedef struct _tree_cache {
- tree* tree;
- BOOL write;
- LIST_ENTRY list_entry;
-} tree_cache;
-
typedef struct _root_cache {
root* root;
struct _root_cache* next;
typedef struct {
PDEVICE_OBJECT devobj;
DEV_ITEM devitem;
+ BOOL removable;
+ ULONG change_count;
LIST_ENTRY disk_holes;
} device;
UINT32 oldused;
BOOL space_changed;
device** devices;
+ UINT64 cache_size;
+ UINT64 cache_inode;
LIST_ENTRY space;
LIST_ENTRY list_entry;
} chunk;
superblock superblock;
// WCHAR label[MAX_LABEL_SIZE];
BOOL readonly;
- fcb* fcbs;
+ BOOL removing;
fcb* volume_fcb;
- fcb* root_fcb;
+ file_ref* root_fileref;
ERESOURCE DirResource;
KSPIN_LOCK FcbListLock;
ERESOURCE fcb_lock;
// UINT64 chunk_root_phys_addr;
UINT64 root_tree_phys_addr;
// log_to_phys* log_to_phys;
- root* roots;
+ LIST_ENTRY roots;
+ LIST_ENTRY drop_roots;
root* chunk_root;
root* root_root;
root* extent_root;
root* checksum_root;
root* dev_root;
+ root* uuid_root;
BOOL log_to_phys_loaded;
UINT32 max_inline;
LIST_ENTRY sys_chunks;
LIST_ENTRY chunks;
LIST_ENTRY trees;
- LIST_ENTRY tree_cache;
HANDLE flush_thread_handle;
- KTIMER flush_thread_timer;
LIST_ENTRY list_entry;
} device_extension;
UINT32 uid;
} uid_map;
+typedef struct {
+ LIST_ENTRY list_entry;
+ UINT64 key;
+} ordered_list;
+
+typedef struct {
+ ordered_list ol;
+ ULONG length;
+ UINT32* checksums;
+ BOOL deleted;
+} changed_sector;
+
+enum write_tree_status {
+ WriteTreeStatus_Pending,
+ WriteTreeStatus_Success,
+ WriteTreeStatus_Error,
+ WriteTreeStatus_Cancelling,
+ WriteTreeStatus_Cancelled
+};
+
+struct write_tree_context;
+
+typedef struct {
+ struct write_tree_context* context;
+ UINT8* buf;
+ device* device;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ enum write_tree_status status;
+ LIST_ENTRY list_entry;
+} write_tree_stripe;
+
+typedef struct {
+ KEVENT Event;
+ LIST_ENTRY stripes;
+} write_tree_context;
+
// #pragma pack(pop)
static __inline void init_tree_holder(tree_holder* th) {
out->nanoseconds = (l % 10000000) * 100;
}
+static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* ins) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > ins->key) {
+ le->Blink->Flink = &ins->list_entry;
+ ins->list_entry.Blink = le->Blink;
+ le->Blink = &ins->list_entry;
+ ins->list_entry.Flink = le;
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ InsertTailList(list, &ins->list_entry);
+}
+
// in btrfs.c
device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback);
BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback);
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
+void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line);
BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode);
NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
-NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
fcb* create_fcb();
+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);
+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);
+BOOL is_file_name_valid(PUNICODE_STRING us);
+void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action);
+WCHAR* file_desc(PFILE_OBJECT FileObject);
+WCHAR* file_desc_fileref(file_ref* fileref);
#ifdef _MSC_VER
#define funcname __FUNCTION__
// FIXME - we probably shouldn't be moving funcname etc. around if we're not printing debug messages
#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
+#define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
#ifdef _DEBUG
+extern BOOL log_started;
+extern UINT32 debug_log_level;
+
#ifdef DEBUG_LONG_MESSAGES
-#define TRACE(s, ...) _debug_message(funcname, 3, __FILE__, __LINE__, s, ##__VA_ARGS__)
-#define WARN(s, ...) _debug_message(funcname, 2, __FILE__, __LINE__, s, ##__VA_ARGS__)
-#define FIXME(s, ...) _debug_message(funcname, 1, __FILE__, __LINE__, s, ##__VA_ARGS__)
-#define ERR(s, ...) _debug_message(funcname, 1, __FILE__, __LINE__, s, ##__VA_ARGS__)
+#define MSG(fn, file, line, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, file, line, s, ##__VA_ARGS__) : 0
-void STDCALL _debug_message(const char* func, UINT8 priority, const char* file, unsigned int line, char* s, ...);
+#define TRACE(s, ...) MSG(funcname, __FILE__, __LINE__, s, 3, ##__VA_ARGS__)
+#define WARN(s, ...) MSG(funcname, __FILE__, __LINE__, s, 2, ##__VA_ARGS__)
+#define FIXME(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
+#define ERR(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
+
+void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...);
#else
-#define TRACE(s, ...) _debug_message(funcname, 3, s, ##__VA_ARGS__)
-#define WARN(s, ...) _debug_message(funcname, 2, s, ##__VA_ARGS__)
-#define FIXME(s, ...) _debug_message(funcname, 1, s, ##__VA_ARGS__)
-#define ERR(s, ...) _debug_message(funcname, 1, s, ##__VA_ARGS__)
+#define MSG(fn, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, s, ##__VA_ARGS__) : 0
+
+#define TRACE(s, ...) MSG(funcname, s, 3, ##__VA_ARGS__)
+#define WARN(s, ...) MSG(funcname, s, 2, ##__VA_ARGS__)
+#define FIXME(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
+#define ERR(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
-void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...);
+void STDCALL _debug_message(const char* func, char* s, ...);
#endif
#endif
+static __inline void increase_chunk_usage(chunk* c, UINT64 delta) {
+ c->used += delta;
+
+ TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
// in fastio.c
void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
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);
-void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line);
-void STDCALL free_tree_cache(LIST_ENTRY* tc);
+void STDCALL free_trees(device_extension* Vcb);
BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback);
-void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write);
tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, const char* func, const char* file, unsigned int line);
NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
void clear_rollback(LIST_ENTRY* rollback);
void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
+void free_trees_root(device_extension* Vcb, root* r);
+NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf);
#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
#define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
#define load_tree(t, addr, r, pt) _load_tree(t, addr, r, pt, funcname, __FILE__, __LINE__)
-#define free_traverse_ptr(tp) _free_traverse_ptr(tp, funcname, __FILE__, __LINE__)
#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)
// in search.c
NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, LIST_ENTRY* rollback);
NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
-NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
+NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback);
+NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
-NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback);
-NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
-void print_trees(LIST_ENTRY* tc);
chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type);
NTSTATUS consider_write(device_extension* Vcb);
+BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data,
+ UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length);
+NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc);
+void free_write_tree_stripes(write_tree_context* wtc);
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, 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);
// 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);
+void fcb_get_sd(fcb* fcb, struct _fcb* parent);
// UINT32 STDCALL get_uid();
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
-NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as);
+UINT32 sid_to_uid(PSID sid);
+NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as);
// in fileinfo.c
NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
+NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
+NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback);
+BOOL has_open_children(file_ref* fileref);
+NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback);
// in reparse.c
-BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject);
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// in create.c
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent);
BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol,
UINT64* inode, UINT8* type, PANSI_STRING utf8);
+NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback);
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
// in fsctl.c
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
+// in pnp.c
+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);
+
+// in extent-tree.c
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
+void decrease_chunk_usage(chunk* c, UINT64 delta);
+NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
+
+#define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
+
static __inline void print_open_trees(device_extension* Vcb) {
LIST_ENTRY* le = Vcb->trees.Flink;
while (le != &Vcb->trees) {
tree* t = CONTAINING_RECORD(le, tree, list_entry);
tree_data* td = CONTAINING_RECORD(t->itemlist.Flink, tree_data, list_entry);
- ERR("tree %p: root %llx, level %u, refcount %u, first key (%llx,%x,%llx)\n",
- t, t->root->id, t->header.level, t->refcount, td->key.obj_id, td->key.obj_type, td->key.offset);
+ ERR("tree %p: root %llx, level %u, first key (%llx,%x,%llx)\n",
+ t, t->root->id, t->header.level, td->key.obj_id, td->key.obj_type, td->key.offset);
le = le->Flink;
}
ExReleaseResourceLite(&Vcb->tree_lock); \
}
-#ifdef DEBUG_TREE_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
-#define _increase_tree_rc(t, func, file, line) { \
- LONG rc = InterlockedIncrement(&t->refcount); \
- _debug_message(func, file, line, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
-}
-#else
-#define _increase_tree_rc(t, func, file, line) { \
- LONG rc = InterlockedIncrement(&t->refcount); \
- _debug_message(func, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
-}
-#endif
-#define increase_tree_rc(t) _increase_tree_rc(t, funcname, __FILE__, __LINE__)
-#else
-#define increase_tree_rc(t) InterlockedIncrement(&t->refcount);
-#define _increase_tree_rc(t, func, file, line) increase_tree_rc(t)
-#endif
-
// from sys/stat.h
#define __S_IFMT 0170000 /* These bits determine file type. */
#define __S_IFDIR 0040000 /* Directory. */
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#endif
+#ifndef S_IRUSR
+#define S_IRUSR 0000400
+#endif
+
+#ifndef S_IWUSR
+#define S_IWUSR 0000200
+#endif
+
#ifndef S_IXUSR
#define S_IXUSR 0000100
#endif
#define S_IFREG __S_IFREG
#endif /* __REACTOS__ */
+#ifndef S_IRGRP
+#define S_IRGRP (S_IRUSR >> 3)
+#endif
+
+#ifndef S_IWGRP
+#define S_IWGRP (S_IWUSR >> 3)
+#endif
+
#ifndef S_IXGRP
#define S_IXGRP (S_IXUSR >> 3)
#endif
+#ifndef S_IROTH
+#define S_IROTH (S_IRGRP >> 3)
+#endif
+
+#ifndef S_IWOTH
+#define S_IWOTH (S_IWGRP >> 3)
+#endif
+
#ifndef S_IXOTH
#define S_IXOTH (S_IXGRP >> 3)
#endif
#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA)
NTSTATUS NTAPI FsRtlRemoveDotsFromPath(PWSTR OriginalString,
USHORT PathLength, USHORT *NewLength);
+NTSTATUS NTAPI FsRtlValidateReparsePointBuffer(ULONG BufferLength,
+ PREPARSE_DATA_BUFFER ReparseBuffer);
#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
#endif
#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)
typedef struct {
UINT64 subvol;
BOOL top;
} btrfs_get_file_ids;
+typedef struct {
+ HANDLE subvol;
+ UINT32 namelen;
+ WCHAR name[1];
+} btrfs_create_snapshot;
+
#endif
if (!utf16) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return FALSE;
}
if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
if (di->key.obj_type == TYPE_ROOT_ITEM) {
- root* fcbroot = Vcb->roots;
- while (fcbroot && fcbroot->id != di->key.obj_id)
- fcbroot = fcbroot->next;
+ LIST_ENTRY* le = Vcb->roots.Flink;
- *subvol = fcbroot;
- *inode = SUBVOL_ROOT_INODE;
- *type = BTRFS_TYPE_DIRECTORY;
+ if (subvol) {
+ *subvol = NULL;
+
+ while (le != &Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r2->id == di->key.obj_id) {
+ *subvol = r2;
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ if (inode)
+ *inode = SUBVOL_ROOT_INODE;
+
+ if (type)
+ *type = BTRFS_TYPE_DIRECTORY;
} else {
- *subvol = r;
- *inode = di->key.obj_id;
- *type = di->type;
+ if (subvol)
+ *subvol = r;
+
+ if (inode)
+ *inode = di->key.obj_id;
+
+ if (type)
+ *type = di->type;
}
if (utf8) {
utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
if (!utf8->Buffer) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
ExFreePool(utf16);
return FALSE;
}
RtlCopyMemory(utf8->Buffer, di->name, di->n);
}
- free_traverse_ptr(&tp);
ExFreePool(utf16);
- TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+// TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
return TRUE;
}
Status = find_item(Vcb, r, &tp2, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
return FALSE;
}
- free_traverse_ptr(&tp);
tp = tp2;
TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
if (keycmp(&tp.item->key, &searchkey) == -1) {
if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
}
}
- if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
- free_traverse_ptr(&tp);
+ if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX)
return FALSE;
- }
b = TRUE;
do {
if (!utf16) {
ERR("out of memory\n");
-
- free_traverse_ptr(&tp);
return FALSE;
}
if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
if (di->key.obj_type == TYPE_ROOT_ITEM) {
- root* fcbroot = Vcb->roots;
- while (fcbroot && fcbroot->id != di->key.obj_id)
- fcbroot = fcbroot->next;
+ LIST_ENTRY* le = Vcb->roots.Flink;
+
+ if (subvol) {
+ *subvol = NULL;
+
+ while (le != &Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r2->id == di->key.obj_id) {
+ *subvol = r2;
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ if (inode)
+ *inode = SUBVOL_ROOT_INODE;
- *subvol = fcbroot;
- *inode = SUBVOL_ROOT_INODE;
- *type = BTRFS_TYPE_DIRECTORY;
+ if (type)
+ *type = BTRFS_TYPE_DIRECTORY;
} else {
- *subvol = r;
- *inode = di->key.obj_id;
- *type = di->type;
+ if (subvol)
+ *subvol = r;
+
+ if (inode)
+ *inode = di->key.obj_id;
+
+ if (type)
+ *type = di->type;
}
- TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+// TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
if (utf8) {
utf8->MaximumLength = di->n;
utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
if (!utf8->Buffer) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
ExFreePool(utf16);
return FALSE;
RtlCopyMemory(utf8->Buffer, di->name, di->n);
}
- free_traverse_ptr(&tp);
ExFreePool(utf16);
return TRUE;
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
}
} while (b);
- free_traverse_ptr(&tp);
-
return FALSE;
}
FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
- InitializeListHead(&fcb->children);
-
return fcb;
}
+file_ref* create_fileref() {
+ file_ref* fr;
+
+ fr = ExAllocatePoolWithTag(PagedPool, sizeof(file_ref), ALLOC_TAG);
+ if (!fr) {
+ ERR("out of memory\n");
+ return NULL;
+ }
+
+ RtlZeroMemory(fr, sizeof(file_ref));
+
+ fr->refcount = 1;
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fileref %p: refcount now %i\n", fr, fr->refcount);
+#endif
+
+ InitializeListHead(&fr->children);
+
+ return fr;
+}
+
static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
char* fn;
xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
if (!xattr->Buffer) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
goto end;
}
RtlCopyMemory(xattr->Buffer, di->name, di->n);
xattr->Buffer[di->n] = 0;
- free_traverse_ptr(&tp);
-
success = TRUE;
goto end;
}
}
}
- free_traverse_ptr(&tp);
-
searchkey.offset = 0;
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
if (!utf16) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
goto end;
}
xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
if (!xattr->Buffer) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
ExFreePool(utf16);
goto end;
}
RtlCopyMemory(xattr->Buffer, di->name, di->n);
xattr->Buffer[di->n] = 0;
- free_traverse_ptr(&tp);
-
success = TRUE;
goto end;
}
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
}
} while (b);
- free_traverse_ptr(&tp);
-
end:
ExFreePool(utf8);
return STATUS_SUCCESS;
}
-static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
+// #ifdef DEBUG_FCB_REFCOUNTS
+// static void print_fcbs(device_extension* Vcb) {
+// fcb* fcb = Vcb->fcbs;
+//
+// while (fcb) {
+// ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
+//
+// fcb = fcb->next;
+// }
+// }
+// #endif
+
+static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
LIST_ENTRY* le;
- fcb *c, *deleted = NULL;
+ file_ref *c, *deleted = NULL;
+#ifdef DEBUG_FCB_REFCOUNTS
ULONG rc;
+#endif
le = dir->children.Flink;
while (le != &dir->children) {
- c = CONTAINING_RECORD(le, fcb, list_entry);
+ c = CONTAINING_RECORD(le, file_ref, list_entry);
if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
if (c->deleted) {
deleted = c;
} else {
- rc = InterlockedIncrement(&c->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", c, rc, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+ rc = InterlockedIncrement(&c->refcount);
+ WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
+#else
+ InterlockedIncrement(&c->refcount);
#endif
return c;
}
}
if (deleted) {
- rc = InterlockedIncrement(&deleted->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", deleted, rc, deleted->full_filename.Length / sizeof(WCHAR), deleted->full_filename.Buffer);
+ rc = InterlockedIncrement(&deleted->refcount);
+ WARN("fileref %p: refcount now %i (%S)\n", deleted, rc, file_desc_fileref(deleted));
+#else
+ InterlockedIncrement(&deleted->refcount);
#endif
}
return deleted;
}
-// #ifdef DEBUG_FCB_REFCOUNTS
-// static void print_fcbs(device_extension* Vcb) {
-// fcb* fcb = Vcb->fcbs;
-//
-// while (fcb) {
-// ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
-//
-// fcb = fcb->next;
-// }
-// }
-// #endif
+static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
+ KEY searchkey;
+ traverse_ptr tp;
+ NTSTATUS Status;
+ fcb* fcb;
+
+ if (!IsListEmpty(&subvol->fcbs)) {
+ LIST_ENTRY* le = subvol->fcbs.Flink;
+
+ while (le != &subvol->fcbs) {
+ fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (fcb->inode == inode && !fcb->ads) {
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc = InterlockedIncrement(&fcb->refcount);
+
+ WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
+#else
+ InterlockedIncrement(&fcb->refcount);
+#endif
-NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
- fcb *dir, *sf, *sf2;
- ULONG i, num_parts;
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ fcb = create_fcb();
+ if (!fcb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fcb->Vcb = Vcb;
+
+ fcb->subvol = subvol;
+ fcb->inode = inode;
+ fcb->type = type;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size > 0)
+ RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
+
+ fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8->Buffer[0] == '.', FALSE);
+
+ fcb_get_sd(fcb, parent);
+
+ InsertTailList(&subvol->fcbs, &fcb->list_entry);
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+
+ if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+ } else {
+ EXTENT_DATA* ed;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+ tp.item->size, sizeof(EXTENT_DATA));
+
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (ed->type == EXTENT_TYPE_INLINE)
+ fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
+ else
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ }
+
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
+ UINT32 streamsize, UINT32 streamhash, fcb* parent, fcb** pfcb) {
+ fcb* fcb;
+
+ if (!IsListEmpty(&subvol->fcbs)) {
+ LIST_ENTRY* le = subvol->fcbs.Flink;
+
+ while (le != &subvol->fcbs) {
+ fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (fcb->inode == inode && fcb->ads && fcb->adsxattr.Length == xattr->Length &&
+ RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc = InterlockedIncrement(&fcb->refcount);
+
+ WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
+#else
+ InterlockedIncrement(&fcb->refcount);
+#endif
+
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ fcb = create_fcb();
+ if (!fcb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fcb->Vcb = Vcb;
+
+ fcb->subvol = parent->subvol;
+ fcb->inode = parent->inode;
+ fcb->type = parent->type;
+ fcb->ads = TRUE;
+ fcb->adssize = streamsize;
+ fcb->adshash = streamhash;
+ fcb->adsxattr = *xattr;
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+ fcb->Header.AllocationSize.QuadPart = fcb->adssize;
+ fcb->Header.FileSize.QuadPart = fcb->adssize;
+ fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
+
+ TRACE("stream found: size = %x, hash = %08x\n", fcb->adssize, fcb->adshash);
+
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
+ *pfcb = fcb;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
UNICODE_STRING fnus2;
+ file_ref *dir, *sf, *sf2;
+ ULONG i, num_parts;
UNICODE_STRING* parts = NULL;
BOOL has_stream;
NTSTATUS Status;
- TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
+ TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
-// #ifdef DEBUG_FCB_REFCOUNTS
-// print_fcbs(Vcb);
-// #endif
+ if (Vcb->removing)
+ return STATUS_ACCESS_DENIED;
fnus2 = *fnus;
- if (fnus2.Length < sizeof(WCHAR) && !relatedfcb) {
+ if (fnus2.Length < sizeof(WCHAR) && !related) {
ERR("error - fnus was too short\n");
return STATUS_INTERNAL_ERROR;
}
- if (relatedfcb) {
- dir = relatedfcb;
+ if (related && fnus->Length == 0) {
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc = InterlockedIncrement(&related->refcount);
+ WARN("fileref %p: refcount now %i\n", related, rc);
+#else
+ InterlockedIncrement(&related->refcount);
+#endif
+
+
+ *pfr = related;
+ return STATUS_SUCCESS;
+ }
+
+ if (related) {
+ dir = related;
} else {
if (fnus2.Buffer[0] != '\\') {
ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
}
if (fnus2.Length == sizeof(WCHAR)) {
- LONG rc;
-
- *pfcb = Vcb->root_fcb;
- rc = InterlockedIncrement(&Vcb->root_fcb->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, rc);
+ LONG rc = InterlockedIncrement(&Vcb->root_fileref->refcount);
+ WARN("fileref %p: refcount now %i (root)\n", Vcb->root_fileref, rc);
+#else
+ InterlockedIncrement(&Vcb->root_fileref->refcount);
#endif
+ *pfr = Vcb->root_fileref;
return STATUS_SUCCESS;
}
- dir = Vcb->root_fcb;
+ dir = Vcb->root_fileref;
fnus2.Buffer++;
fnus2.Length -= sizeof(WCHAR);
fnus2.MaximumLength -= sizeof(WCHAR);
}
- if (dir->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
- WARN("passed relatedfcb which isn't a directory (%.*S) (fnus = %.*S)\n",
- relatedfcb->full_filename.Length / sizeof(WCHAR), relatedfcb->full_filename.Buffer, fnus->Length / sizeof(WCHAR), fnus->Buffer);
+ if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
+ WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
+ file_desc_fileref(related), fnus->Length / sizeof(WCHAR), fnus->Buffer);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
}
}
- // FIXME - handle refcounts(?)
sf = dir;
dir->refcount++;
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", dir, dir->refcount, dir->full_filename.Length / sizeof(WCHAR), dir->full_filename.Buffer);
+ WARN("fileref %p: refcount now %i (%S)\n", dir, dir->refcount, file_desc_fileref(dir));
#endif
if (parent) {
num_parts--;
- if (has_stream) {
+ if (has_stream && num_parts > 0) {
num_parts--;
has_stream = FALSE;
}
if (num_parts == 0) {
Status = STATUS_SUCCESS;
- *pfcb = dir;
+ *pfr = dir;
goto end2;
}
for (i = 0; i < num_parts; i++) {
BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
- sf2 = search_fcb_children(sf, &parts[i]);
+ sf2 = search_fileref_children(sf, &parts[i]);
- if (sf2 && sf2->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
+ if (sf2 && sf2->fcb->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
WARN("passed path including file as subdirectory\n");
+ free_fileref(sf2);
Status = STATUS_OBJECT_PATH_NOT_FOUND;
goto end;
xattr.Buffer = NULL;
xattr.Length = xattr.MaximumLength = 0;
- if (!find_stream(Vcb, sf, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
+ // FIXME - check if already opened
+
+ if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
} else {
ULONG fnlen;
+ fcb* fcb;
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc;
+#endif
if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
goto end;
}
- sf2 = create_fcb();
+ Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamsize, streamhash, sf->fcb, &fcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb_stream returned %08x\n", Status);
+ goto end;
+ }
+
+ sf2 = create_fileref();
if (!sf2) {
ERR("out of memory\n");
+ free_fcb(fcb);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
-
- sf2->Vcb = Vcb;
+
+ sf2->fcb = fcb;
if (streamname.Buffer) // case has changed
sf2->filepart = streamname;
sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
if (!sf2->filepart.Buffer) {
ERR("out of memory\n");
- free_fcb(sf2);
+ free_fileref(sf2);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
}
- sf2->par = sf;
-
- sf->refcount++;
-#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
-#endif
-
- sf2->subvol = sf->subvol;
- sf2->inode = sf->inode;
- sf2->type = sf->type;
- sf2->ads = TRUE;
- sf2->adssize = streamsize;
- sf2->adshash = streamhash;
- sf2->adsxattr = xattr;
-
- TRACE("stream found: size = %x, hash = %08x\n", sf2->adssize, sf2->adshash);
-
- if (Vcb->fcbs)
- Vcb->fcbs->prev = sf2;
-
- sf2->next = Vcb->fcbs;
- Vcb->fcbs = sf2;
-
sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
- if (sf != Vcb->root_fcb)
+ if (sf != Vcb->root_fileref)
sf2->name_offset++;
fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
if (!sf2->full_filename.Buffer) {
ERR("out of memory\n");
- free_fcb(sf2);
+ free_fileref(sf2);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
// FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
- TRACE("found stream %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, sf->subvol);
-
+ sf2->parent = (struct _file_ref*)sf;
InsertTailList(&sf->children, &sf2->list_entry);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&sf->refcount);
+ WARN("fileref %p: refcount now %i\n", sf, rc);
+#else
+ InterlockedIncrement(&sf->refcount);
+#endif
}
} else {
root* subvol;
UINT64 inode;
UINT8 type;
ANSI_STRING utf8;
- KEY searchkey;
- traverse_ptr tp;
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc;
+#endif
- if (!find_file_in_dir(Vcb, &parts[i], sf->subvol, sf->inode, &subvol, &inode, &type, &utf8)) {
+ if (!find_file_in_dir(Vcb, &parts[i], sf->fcb->subvol, sf->fcb->inode, &subvol, &inode, &type, &utf8)) {
TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
goto end;
- } else if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
- WARN("passed path including file as subdirectory\n");
-
- Status = STATUS_OBJECT_PATH_NOT_FOUND;
- goto end;
} else {
- ULONG fnlen, strlen;
+ fcb* fcb;
+ ULONG strlen, fnlen;
- sf2 = create_fcb();
+ 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);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb returned %08x\n", Status);
+ goto end;
+ }
+
+ sf2 = create_fileref();
if (!sf2) {
ERR("out of memory\n");
+ free_fcb(fcb);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- sf2->Vcb = Vcb;
-
+ sf2->fcb = fcb;
+
+ sf2->utf8 = utf8;
+
Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fcb(sf2);
+ free_fileref(sf2);
goto end;
- } else {
- sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
- sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
- if (!sf2->filepart.Buffer) {
- ERR("out of memory\n");
- free_fcb(sf2);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
-
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- free_fcb(sf2);
- goto end;
- }
}
- sf2->par = sf;
-
- sf->refcount++;
-#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
-#endif
-
- sf2->subvol = subvol;
- sf2->inode = inode;
- sf2->type = type;
-
- if (Vcb->fcbs)
- Vcb->fcbs->prev = sf2;
+ sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+ if (!sf2->filepart.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(sf2);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
- sf2->next = Vcb->fcbs;
- Vcb->fcbs = sf2;
+ Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ free_fileref(sf2);
+ goto end;
+ }
sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
-
- if (sf != Vcb->root_fcb)
+
+ if (sf != Vcb->root_fileref)
sf2->name_offset++;
fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
if (!sf2->full_filename.Buffer) {
ERR("out of memory\n");
- free_fcb(sf2);
+ free_fileref(sf2);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
- if (sf != Vcb->root_fcb)
+ if (sf != Vcb->root_fileref)
sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
- sf2->utf8 = utf8;
-
- searchkey.obj_id = sf2->inode;
- searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(sf2->Vcb, sf2->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_fcb(sf2);
- goto end;
- }
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id);
- Status = STATUS_INTERNAL_ERROR;
- free_fcb(sf2);
- free_traverse_ptr(&tp);
- goto end;
- }
-
- if (tp.item->size > 0)
- RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
-
- free_traverse_ptr(&tp);
-
- sf2->atts = get_file_attributes(Vcb, &sf2->inode_item, sf2->subvol, sf2->inode, sf2->type, sf2->filepart.Buffer[0] == '.', FALSE);
-
- fcb_get_sd(sf2);
-
- TRACE("found %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, subvol);
-
+ sf2->parent = (struct _file_ref*)sf;
InsertTailList(&sf->children, &sf2->list_entry);
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&sf->refcount);
+ WARN("fileref %p: refcount now %i\n", sf, rc);
+#else
+ InterlockedIncrement(&sf->refcount);
+#endif
}
}
}
if (i == num_parts - 1)
break;
- free_fcb(sf);
+ if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+ Status = STATUS_REPARSE;
+
+ if (unparsed)
+ *unparsed = fnus->Length - ((parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR));
+
+ break;
+ }
+
+ free_fileref(sf);
sf = sf2;
}
- Status = STATUS_SUCCESS;
- *pfcb = sf2;
+ if (Status != STATUS_REPARSE)
+ Status = STATUS_SUCCESS;
+ *pfr = sf2;
end:
- free_fcb(sf);
+ free_fileref(sf);
end2:
if (parts)
ExFreePool(parts);
-// #ifdef DEBUG_FCB_REFCOUNTS
-// print_fcbs(Vcb);
-// #endif
-
TRACE("returning %08x\n", Status);
return Status;
}
-static NTSTATUS STDCALL attach_fcb_to_fileobject(device_extension* Vcb, fcb* fcb, PFILE_OBJECT FileObject) {
- FileObject->FsContext = fcb;
-// FileObject->FsContext2 = 0x0badc0de;//NULL;
-
- // FIXME - cache stuff
-
- return STATUS_SUCCESS;
-}
-
-static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, fcb* parfcb, ULONG options, fcb** pfcb, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options, file_ref** pfr, LIST_ENTRY* rollback) {
NTSTATUS Status;
fcb* fcb;
ULONG utf8len;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
ANSI_STRING utf8as;
ULONG defda;
+ file_ref* fileref;
+#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
+#endif
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
if (!NT_SUCCESS(Status))
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
- dirpos = find_next_dir_index(Vcb, parfcb->subvol, parfcb->inode);
+ dirpos = find_next_dir_index(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode);
if (dirpos == 0) {
Status = STATUS_INTERNAL_ERROR;
ExFreePool(utf8);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
- TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
- parfcb->inode_item.st_size += utf8len * 2;
- TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
- parfcb->inode_item.transid = Vcb->superblock.generation;
- parfcb->inode_item.sequence++;
- parfcb->inode_item.st_ctime = now;
- parfcb->inode_item.st_mtime = now;
+// TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfileref->fcb->inode_item.st_size += utf8len * 2;
+// TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+ parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
+ parfileref->fcb->inode_item.sequence++;
+ parfileref->fcb->inode_item.st_ctime = now;
+ parfileref->fcb->inode_item.st_mtime = now;
- searchkey.obj_id = parfcb->inode;
+ searchkey.obj_id = parfileref->fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0;
+ searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
ExFreePool(utf8);
return Status;
}
- if (keycmp(&searchkey, &tp.item->key)) {
- ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfcb->inode, parfcb->subvol->id);
- free_traverse_ptr(&tp);
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfileref->fcb->inode, parfileref->fcb->subvol->id);
ExFreePool(utf8);
return STATUS_INTERNAL_ERROR;
}
dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!dirii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
ExFreePool(utf8);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(dirii, &parfcb->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(dirii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM));
delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, parfcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
+ insert_tree_item(Vcb, parfileref->fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
- free_traverse_ptr(&tp);
+ if (parfileref->fcb->subvol->lastinode == 0)
+ get_last_inode(Vcb, parfileref->fcb->subvol);
- if (parfcb->subvol->lastinode == 0)
- get_last_inode(Vcb, parfcb->subvol);
-
- inode = parfcb->subvol->lastinode + 1;
+ inode = parfileref->fcb->subvol->lastinode + 1;
type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
di->type = type;
RtlCopyMemory(di->name, utf8, utf8len);
- insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback);
+ insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback);
di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
if (!di2) {
RtlCopyMemory(di2, di, disize);
- Status = add_dir_item(Vcb, parfcb->subvol, parfcb->inode, crc32, di2, disize, rollback);
+ Status = add_dir_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, crc32, di2, disize, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_dir_item returned %08x\n", Status);
ExFreePool(utf8);
return Status;
}
- // FIXME - handle Irp->Overlay.AllocationSize
-
utf8as.Buffer = utf8;
utf8as.Length = utf8as.MaximumLength = utf8len;
- Status = add_inode_ref(Vcb, parfcb->subvol, inode, parfcb->inode, dirpos, &utf8as, rollback);
+ Status = add_inode_ref(Vcb, parfileref->fcb->subvol, inode, parfileref->fcb->inode, dirpos, &utf8as, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_inode_ref returned %08x\n", Status);
ExFreePool(utf8);
sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
- Status = set_xattr(Vcb, parfcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
+ Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
ExFreePool(utf8);
}
}
- parfcb->subvol->lastinode++;
+ parfileref->fcb->subvol->lastinode++;
fcb = create_fcb();
if (!fcb) {
fcb->Vcb = Vcb;
- RtlZeroMemory(&fcb->inode_item, sizeof(INODE_ITEM));
fcb->inode_item.generation = Vcb->superblock.generation;
fcb->inode_item.transid = Vcb->superblock.generation;
fcb->inode_item.st_size = 0;
fcb->inode_item.st_nlink = 1;
// fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
- fcb->inode_item.st_mode = parfcb ? (parfcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
+ fcb->inode_item.st_mode = parfileref->fcb ? (parfileref->fcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
fcb->inode_item.st_rdev = 0;
fcb->inode_item.flags = 0;
fcb->inode_item.sequence = 1;
}
// inherit nodatacow flag from parent directory
- if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+ 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;
}
-// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.ValidDataLength.QuadPart = 0;
fcb->atts = IrpSp->Parameters.Create.FileAttributes;
- if (options & FILE_DELETE_ON_CLOSE)
- fcb->delete_on_close = TRUE;
-
- fcb->par = parfcb;
- rc = InterlockedIncrement(&parfcb->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+ 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 = parfcb->subvol;
+ fcb->subvol = parfileref->fcb->subvol;
fcb->inode = inode;
fcb->type = type;
- fcb->utf8.MaximumLength = fcb->utf8.Length = utf8len;
- fcb->utf8.Buffer = utf8;
-
- Status = fcb_get_new_sd(fcb, IrpSp->Parameters.Create.SecurityContext->AccessState);
+ Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
if (!NT_SUCCESS(Status)) {
ERR("fcb_get_new_sd returned %08x\n", Status);
- ExFreePool(utf8);
+ free_fcb(fcb);
return Status;
}
+
+ fileref = create_fileref();
+ if (!fileref) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fileref->fcb = fcb;
- fcb->filepart = *fpus;
+ fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
+ fileref->utf8.Buffer = utf8;
+
+ fileref->filepart = *fpus;
- Status = set_xattr(Vcb, parfcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
+ Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
- ExFreePool(utf8);
+ free_fileref(fileref);
return Status;
}
- fcb->full_filename.Length = parfcb->full_filename.Length + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fcb->filepart.Length;
- fcb->full_filename.MaximumLength = fcb->full_filename.Length;
- fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.Length, ALLOC_TAG);
- if (!fcb->full_filename.Buffer) {
+ fileref->full_filename.Length = parfileref->full_filename.Length + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fileref->filepart.Length;
+ fileref->full_filename.MaximumLength = fileref->full_filename.Length;
+ fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length, ALLOC_TAG);
+ if (!fileref->full_filename.Buffer) {
ERR("out of memory\n");
- ExFreePool(utf8);
+ free_fileref(fileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
+ RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length);
- if (parfcb->full_filename.Length > sizeof(WCHAR))
- fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = '\\';
+ if (parfileref->full_filename.Length > sizeof(WCHAR))
+ fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = '\\';
- RtlCopyMemory(&fcb->full_filename.Buffer[(parfcb->full_filename.Length / sizeof(WCHAR)) + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], fcb->filepart.Buffer, fcb->filepart.Length);
+ RtlCopyMemory(&fileref->full_filename.Buffer[(parfileref->full_filename.Length / sizeof(WCHAR)) + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : 1)],
+ fileref->filepart.Buffer, fileref->filepart.Length);
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- ExFreePool(utf8);
+ free_fileref(fileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
- insert_tree_item(Vcb, parfcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
+ if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+ Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("extend_file returned %08x\n", Status);
+ free_fileref(fileref);
+ return Status;
+ }
+ }
- *pfcb = fcb;
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ insert_tree_item(Vcb, fcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
- InsertTailList(&fcb->par->children, &fcb->list_entry);
+ fileref->parent = parfileref;
+ InsertTailList(&parfileref->children, &fileref->list_entry);
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&parfileref->refcount);
+ WARN("fileref %p: refcount now %i\n", parfileref, rc);
+#else
+ InterlockedIncrement(&parfileref->refcount);
+#endif
+
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
+ *pfr = fileref;
- TRACE("created new file %.*S in subvol %llx, inode %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->subvol->id, fcb->inode);
+ TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
return STATUS_SUCCESS;
}
static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
NTSTATUS Status;
- fcb *fcb, *parfcb = NULL;
+// fcb *fcb, *parfcb = NULL;
+ file_ref *fileref, *parfileref = NULL, *related;
ULONG i, j;
- ULONG utf8len;
+// ULONG utf8len;
ccb* ccb;
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
UNICODE_STRING dsus, fpus, stream;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG access;
+ PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
+#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
+#endif
TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
fpus.Buffer = NULL;
- // FIXME - apparently you can open streams using RelatedFileObject. How can we test this?
+ if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
+ struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+
+ related = relatedccb->fileref;
+ } else
+ related = NULL;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- Status = get_fcb(Vcb, &parfcb, fnus, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, TRUE);
+ Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
ExReleaseResourceLite(&Vcb->fcb_lock);
if (!NT_SUCCESS(Status))
goto end;
- if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
+ if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
Status = STATUS_OBJECT_PATH_NOT_FOUND;
goto end;
}
- if (parfcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ if (parfileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
}
if (stream.Length > 0) {
- struct _fcb* newpar;
+ file_ref* newpar;
+ fcb* fcb;
static char xapref[] = "user.";
ULONG xapreflen = strlen(xapref), fnlen;
LARGE_INTEGER time;
KEY searchkey;
traverse_ptr tp;
INODE_ITEM* ii;
+ ULONG utf8len;
+ UINT64 offset;
+#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);
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- Status = get_fcb(Vcb, &newpar, &fpus, parfcb, FALSE);
+ Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
ExReleaseResourceLite(&Vcb->fcb_lock);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &newpar, rollback);
+ UNICODE_STRING fpus2;
+
+ if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
+ WARN("SeAccessCheck failed, returning %08x\n", Status);
+ goto end;
+ }
+
+ if (!is_file_name_valid(&fpus))
+ return STATUS_OBJECT_NAME_INVALID;
+
+ 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;
}
+
+ // FIXME - send notification
} else if (!NT_SUCCESS(Status)) {
- ERR("get_fcb returned %08x\n", Status);
+ ERR("open_fileref returned %08x\n", Status);
goto end;
}
- free_fcb(parfcb);
- parfcb = newpar;
+ free_fileref(parfileref);
+ parfileref = newpar;
+
+ if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
+ WARN("SeAccessCheck failed, returning %08x\n", Status);
+ goto end;
+ }
- if (newpar->type != BTRFS_TYPE_FILE && newpar->type != BTRFS_TYPE_SYMLINK) {
+ 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;
fcb->Vcb = Vcb;
-// fcb->Header.IsFastIoPossible = TRUE;
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.ValidDataLength.QuadPart = 0;
- if (options & FILE_DELETE_ON_CLOSE)
- fcb->delete_on_close = TRUE;
-
- fcb->par = parfcb;
- rc = InterlockedIncrement(&parfcb->refcount);
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+ 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 = parfcb->subvol;
- fcb->inode = parfcb->inode;
- fcb->type = parfcb->type;
+ fcb->subvol = parfileref->fcb->subvol;
+ fcb->inode = parfileref->fcb->inode;
+ fcb->type = parfileref->fcb->type;
fcb->ads = TRUE;
fcb->adssize = 0;
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
- if (!NT_SUCCESS(Status))
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+ free_fcb(fcb);
goto end;
+ }
fcb->adsxattr.Length = utf8len + xapreflen;
fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
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->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
TRACE("adshash = %08x\n", fcb->adshash);
- fcb->name_offset = parfcb->full_filename.Length / sizeof(WCHAR);
- if (parfcb != Vcb->root_fcb)
- fcb->name_offset++;
-
- fcb->filepart.MaximumLength = fcb->filepart.Length = stream.Length;
- fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.MaximumLength, ALLOC_TAG);
- if (!fcb->filepart.Buffer) {
+ fileref = create_fileref();
+ if (!fileref) {
ERR("out of memory\n");
free_fcb(fcb);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- RtlCopyMemory(fcb->filepart.Buffer, stream.Buffer, stream.Length);
+ fileref->fcb = fcb;
- fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+ fileref->name_offset = parfileref->full_filename.Length / sizeof(WCHAR);
+ if (parfileref != Vcb->root_fileref)
+ fileref->name_offset++;
- fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
- if (!fcb->full_filename.Buffer) {
+ 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_fcb(fcb);
+ free_fileref(fileref);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
- RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
-
- fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = ':';
+ RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
+
+ fnlen = (fileref->name_offset * sizeof(WCHAR)) + fileref->filepart.Length;
- RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
- TRACE("full_filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+ if (!fileref->full_filename.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(fileref);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
- InsertTailList(&fcb->par->children, &fcb->list_entry);
+ fileref->full_filename.Length = fileref->full_filename.MaximumLength = fnlen;
+ RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length);
+
+ fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = ':';
+
+ RtlCopyMemory(&fileref->full_filename.Buffer[fileref->name_offset], fileref->filepart.Buffer, fileref->filepart.Length);
+ TRACE("full_filename = %.*S\n", fileref->full_filename.Length / sizeof(WCHAR), fileref->full_filename.Buffer);
- Status = set_xattr(Vcb, parfcb->subvol, parfcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback);
+ Status = set_xattr(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
- free_fcb(fcb);
+ free_fileref(fileref);
goto end;
}
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
- parfcb->inode_item.transid = Vcb->superblock.generation;
- parfcb->inode_item.sequence++;
- parfcb->inode_item.st_ctime = now;
+ parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
+ parfileref->fcb->inode_item.sequence++;
+ parfileref->fcb->inode_item.st_ctime = now;
- searchkey.obj_id = parfcb->inode;
+ searchkey.obj_id = parfileref->fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
+ free_fileref(fileref);
goto end;
}
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
delete_tree_item(Vcb, &tp, rollback);
+ offset = tp.item->key.offset;
} else {
- WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfcb->subvol->id);
+ WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfileref->fcb->subvol->id);
+ offset = 0;
}
- free_traverse_ptr(&tp);
-
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+ free_fileref(fileref);
goto end;
}
- RtlCopyMemory(ii, &parfcb->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM));
+
+ insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_INODE_ITEM, offset, ii, sizeof(INODE_ITEM), NULL, rollback);
- insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
+ parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ parfileref->fcb->subvol->root_item.ctime = now;
- parfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
- parfcb->subvol->root_item.ctime = now;
+ fileref->parent = (struct _file_ref*)parfileref;
+ InsertTailList(&parfileref->children, &fileref->list_entry);
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&parfileref->refcount);
+ WARN("fileref %p: refcount now %i\n", parfileref, rc);
+#else
+ InterlockedIncrement(&parfileref->refcount);
+#endif
ExFreePool(fpus.Buffer);
fpus.Buffer = NULL;
} else {
- Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &fcb, rollback);
+ if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
+ WARN("SeAccessCheck failed, returning %08x\n", Status);
+ goto end;
+ }
+
+ if (!is_file_name_valid(&fpus)) {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto end;
+ }
+
+ Status = file_create2(Irp, Vcb, &fpus, parfileref, options, &fileref, rollback);
if (!NT_SUCCESS(Status)) {
ERR("file_create2 returned %08x\n", Status);
}
}
- ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-
- if (Vcb->fcbs)
- Vcb->fcbs->prev = fcb;
-
- fcb->next = Vcb->fcbs;
- Vcb->fcbs = fcb;
-
- ExReleaseResourceLite(&Vcb->fcb_lock);
-
- Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
+ FileObject->FsContext = fileref->fcb;
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
if (!ccb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
+ free_fileref(fileref);
goto end;
}
RtlZeroMemory(ccb, sizeof(*ccb));
+
+ ccb->fileref = fileref;
+
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
ccb->NodeSize = sizeof(ccb);
ccb->disposition = disposition;
RtlInitUnicodeString(&ccb->query_string, NULL);
ccb->has_wildcard = FALSE;
ccb->specific_file = FALSE;
+ ccb->access = access;
- oc = InterlockedIncrement(&fcb->open_count);
#ifdef DEBUG_FCB_REFCOUNTS
- ERR("fcb %p: open_count now %i\n", fcb, oc);
+ oc = InterlockedIncrement(&fileref->fcb->open_count);
+ ERR("fcb %p: open_count now %i\n", fileref->fcb, oc);
+#else
+ InterlockedIncrement(&fileref->fcb->open_count);
#endif
FileObject->FsContext2 = ccb;
- FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+ FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
- TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
+// TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
Status = consider_write(Vcb);
if (NT_SUCCESS(Status)) {
- ULONG fnlen;
-
- fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
-
- if (fcb->par != Vcb->root_fcb)
- fcb->name_offset++;
-
- fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
-
- fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
- if (!fcb->full_filename.Buffer) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
- RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
-
- if (fcb->par != Vcb->root_fcb)
- fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
-
- RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
+// ULONG fnlen;
+//
+// fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
+//
+// if (fcb->par != Vcb->root_fcb)
+// fcb->name_offset++;
+//
+// fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+//
+// fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+// if (!fcb->full_filename.Buffer) {
+// ERR("out of memory\n");
+// Status = STATUS_INSUFFICIENT_RESOURCES;
+// goto end;
+// }
+//
+// fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+// RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+//
+// if (fcb->par != Vcb->root_fcb)
+// fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
+//
+// RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
- FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
- options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
- FILE_ACTION_ADDED, NULL);
+ send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
goto end2;
}
ExFreePool(fpus.Buffer);
end2:
- if (parfcb)
- free_fcb(parfcb);
+ if (parfileref)
+ free_fileref(parfileref);
return Status;
}
}
}
-static NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) {
+NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
INODE_ITEM* newii;
NTSTATUS Status;
+ UINT64 offset = 0;
searchkey.obj_id = inode;
searchkey.obj_type = TYPE_INODE_ITEM;
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
delete_tree_item(Vcb, &tp, rollback);
+
+ offset = tp.item->key.offset;
} else {
WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
}
- free_traverse_ptr(&tp);
-
newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!newii) {
ERR("out of memory\n");
RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
- insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, 0, newii, sizeof(INODE_ITEM), NULL, rollback);
+ insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, offset, newii, sizeof(INODE_ITEM), NULL, rollback);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
+ NTSTATUS Status;
+
+ if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
+ ULONG size, bytes_read, i;
+
+ if (fcb->inode_item.st_size < sizeof(ULONG)) {
+ WARN("file was too short to be a reparse point\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
+ size = min(0x10007, fcb->inode_item.st_size);
+
+ *data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+ if (!*data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, *data, 0, size, &bytes_read);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(*data);
+ return Status;
+ }
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ ULONG stringlen, subnamelen, printnamelen, reqlen;
+ REPARSE_DATA_BUFFER* rdb;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)*data, bytes_read);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(*data);
+ return Status;
+ }
+
+ subnamelen = stringlen;
+ printnamelen = stringlen;
+
+ reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
+
+ rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+
+ if (!rdb) {
+ ERR("out of memory\n");
+ ExFreePool(*data);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+ rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+ rdb->Reserved = 0;
+
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
+ rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+
+ Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ stringlen, &stringlen, (char*)*data, size);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(rdb);
+ ExFreePool(*data);
+ return Status;
+ }
+
+ for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
+ if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
+ rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
+ }
+
+ RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
+ &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
+
+ ExFreePool(*data);
+
+ *data = (UINT8*)rdb;
+ } else {
+ Status = FsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
+ ExFreePool(*data);
+ return Status;
+ }
+ }
+ } else {
+ UINT16 datalen;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, data, &datalen))
+ return STATUS_INTERNAL_ERROR;
+
+ if (!*data)
+ return STATUS_INTERNAL_ERROR;
+
+ if (datalen < sizeof(ULONG)) {
+ WARN("xattr was too short to be a reparse point\n");
+ ExFreePool(*data);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ Status = FsRtlValidateReparsePointBuffer(datalen, (REPARSE_DATA_BUFFER*)*data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
+ ExFreePool(*data);
+ return Status;
+ }
+ }
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
PFILE_OBJECT FileObject;
ULONG RequestedDisposition;
ULONG options;
NTSTATUS Status;
- fcb* fcb;
ccb* ccb;
device_extension* Vcb = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
ULONG access;
PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState;
+ USHORT unparsed;
+ file_ref *related, *fileref;
+#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
+#endif
Irp->IoStatus.Information = 0;
Status = STATUS_INVALID_PARAMETER;
goto exit;
}
+
+ if (options & FILE_OPEN_BY_FILE_ID) {
+ WARN("FILE_OPEN_BY_FILE_ID not supported\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
FileObject = Stack->FileObject;
}
// FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
+
+ if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
+ struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+
+ related = relatedccb->fileref;
+ } else
+ related = NULL;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
- Status = get_fcb(Vcb, &fcb, &FileObject->FileName, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, Stack->Flags & SL_OPEN_TARGET_DIRECTORY);
+ Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
ExReleaseResourceLite(&Vcb->fcb_lock);
- if (NT_SUCCESS(Status) && fcb->deleted) {
- free_fcb(fcb);
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ if (Status == STATUS_REPARSE) {
+ REPARSE_DATA_BUFFER* data;
+
+ Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_reparse_block returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ goto exit;
+ }
+
+ Status = STATUS_REPARSE;
+ RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+
+ data->Reserved = unparsed;
+
+ Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
+ if (NT_SUCCESS(Status) && fileref->deleted) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto exit; // FIXME?
+ }
+
if (NT_SUCCESS(Status)) {
if (RequestedDisposition == FILE_CREATE) {
- TRACE("file %.*S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
Status = STATUS_OBJECT_NAME_COLLISION;
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
goto exit;
}
} else {
- TRACE("get_fcb returned %08x\n", Status);
+ TRACE("open_fileref returned %08x\n", Status);
goto exit;
}
if (NT_SUCCESS(Status)) { // file already exists
- struct _fcb* sf;
+ file_ref* sf;
if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
Status = STATUS_MEDIA_WRITE_PROTECTED;
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
- if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
+ if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
Status = STATUS_ACCESS_DENIED;
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
- TRACE("deleted = %s\n", fcb->deleted ? "TRUE" : "FALSE");
+ TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
- sf = fcb;
+ sf = fileref;
while (sf) {
if (sf->delete_on_close) {
WARN("could not open as deletion pending\n");
Status = STATUS_DELETE_PENDING;
- free_fcb(fcb);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
- sf = sf->par;
+ sf = sf->parent;
+ }
+
+ if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
+ Status = STATUS_ACCESS_DENIED;
+ free_fileref(fileref);
+ goto exit;
}
- if (fcb->type == BTRFS_TYPE_SYMLINK && !(options & FILE_OPEN_REPARSE_POINT)) {
- if (!follow_symlink(fcb, FileObject)) {
- ERR("follow_symlink failed\n");
- Status = STATUS_INTERNAL_ERROR;
- free_fcb(fcb);
+ if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) {
+ UINT8* 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
+ * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
+ * translation. If we instead return the reparse tag in Information, and store
+ * 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);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_reparse_block returned %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
Status = STATUS_REPARSE;
- Irp->IoStatus.Information = IO_REPARSE;
- free_fcb(fcb);
+ RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+
+ Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
- if (!SeAccessCheck(fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
+ if (!SeAccessCheck(fileref->fcb->ads ? fileref->parent->fcb->sd : fileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
WARN("SeAccessCheck failed, returning %08x\n", Status);
- free_fcb(fcb);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
- if (fcb->open_count > 0) {
- Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access, TRUE);
+ if (fileref->fcb->open_count > 0) {
+ Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
if (!NT_SUCCESS(Status)) {
WARN("IoCheckShareAccess failed, returning %08x\n", Status);
- free_fcb(fcb);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
} else {
- IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access);
+ IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
}
if (access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
- if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForWrite)) {
+ if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
- free_fcb(fcb);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
}
if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
ULONG defda;
- LIST_ENTRY changed_sector_list;
- if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fcb->atts & FILE_ATTRIBUTE_READONLY) {
+ if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
WARN("cannot overwrite readonly file\n");
Status = STATUS_ACCESS_DENIED;
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
// free_fcb(fcb);
// goto exit;
// }
- InitializeListHead(&changed_sector_list);
// FIXME - make sure not ADS!
- Status = truncate_file(fcb, fcb->inode_item.st_size, rollback);
+ Status = truncate_file(fileref->fcb, fileref->fcb->inode_item.st_size, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
- Status = update_inode_item(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback);
+ if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+ Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("extend_file returned %08x\n", Status);
+ free_fileref(fileref);
+ goto exit;
+ }
+ }
+
+ Status = update_inode_item(Vcb, fileref->fcb->subvol, fileref->fcb->inode, &fileref->fcb->inode_item, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_inode_item returned %08x\n", Status);
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
- defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+ 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);
if (RequestedDisposition == FILE_SUPERSEDE)
- fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
else
- fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ fileref->fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
if (Stack->Parameters.Create.FileAttributes != defda) {
char val[64];
sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
- Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
+ Status = set_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
} else
- delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
+ delete_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
// FIXME - truncate streams
// FIXME - do we need to alter parent directory's times?
// FIXME - send notifications
- Status = consider_write(fcb->Vcb);
+ Status = consider_write(Vcb);
if (!NT_SUCCESS(Status)) {
ERR("consider_write returned %08x\n", Status);
- free_fcb(fcb);
+ free_fileref(fileref);
goto exit;
}
}
-// fcb->Header.IsFastIoPossible = TRUE;
-
- if (fcb->ads) {
- fcb->Header.AllocationSize.QuadPart = fcb->adssize;
- fcb->Header.FileSize.QuadPart = fcb->adssize;
- fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
- } else if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
- fcb->Header.AllocationSize.QuadPart = 0;
- fcb->Header.FileSize.QuadPart = 0;
- fcb->Header.ValidDataLength.QuadPart = 0;
- } else {
- KEY searchkey;
- traverse_ptr tp;
- EXTENT_DATA* ed;
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_EXTENT_DATA;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_fcb(fcb);
- goto exit;
- }
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
- free_traverse_ptr(&tp);
- free_fcb(fcb);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
- }
-
- if (tp.item->size < sizeof(EXTENT_DATA)) {
- ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
- tp.item->size, sizeof(EXTENT_DATA));
- free_traverse_ptr(&tp);
- free_fcb(fcb);
- Status = STATUS_INTERNAL_ERROR;
- 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);
- ed = (EXTENT_DATA*)tp.item->data;
-
- if (ed->type == EXTENT_TYPE_INLINE)
- fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
- else
- fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
-
- fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
- fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
-
- free_traverse_ptr(&tp);
- }
-
- if (options & FILE_NON_DIRECTORY_FILE && fcb->type == BTRFS_TYPE_DIRECTORY) {
- free_fcb(fcb);
Status = STATUS_FILE_IS_A_DIRECTORY;
goto exit;
- } else if (options & FILE_DIRECTORY_FILE && fcb->type != BTRFS_TYPE_DIRECTORY) {
- TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, path = %.*S)\n", fcb->type, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
- free_fcb(fcb);
+ } 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;
}
- Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
-
- if (options & FILE_DELETE_ON_CLOSE)
- fcb->delete_on_close = TRUE;
+ FileObject->FsContext = fileref->fcb;
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
if (!ccb) {
ERR("out of memory\n");
- free_fcb(fcb);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
RtlZeroMemory(ccb, sizeof(*ccb));
+
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
ccb->NodeSize = sizeof(ccb);
ccb->disposition = RequestedDisposition;
RtlInitUnicodeString(&ccb->query_string, NULL);
ccb->has_wildcard = FALSE;
ccb->specific_file = FALSE;
+ ccb->access = access;
+
+ ccb->fileref = fileref;
FileObject->FsContext2 = ccb;
- FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
+ FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
if (NT_SUCCESS(Status)) {
switch (RequestedDisposition) {
}
}
- oc = InterlockedIncrement(&fcb->open_count);
#ifdef DEBUG_FCB_REFCOUNTS
- ERR("fcb %p: open_count now %i\n", fcb, oc);
+ oc = InterlockedIncrement(&fileref->fcb->open_count);
+ ERR("fcb %p: open_count now %i\n", fileref->fcb, oc);
+#else
+ InterlockedIncrement(&fileref->fcb->open_count);
#endif
} else {
Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
-
-// if (!NT_SUCCESS(Status))
-// free_fcb(fcb);
}
if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+#ifdef DEBUG_FCB_REFCOUNTS
LONG rc, oc;
+#endif
TRACE("open operation for volume\n");
goto exit;
}
+#ifdef DEBUG_FCB_REFCOUNTS
rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
oc = InterlockedIncrement(&Vcb->volume_fcb->open_count);
-#ifdef DEBUG_FCB_REFCOUNTS
WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);
WARN("fcb %p: open_count now %i (volume)\n", Vcb->volume_fcb, oc);
+#else
+ InterlockedIncrement(&Vcb->volume_fcb->refcount);
+ InterlockedIncrement(&Vcb->volume_fcb->open_count);
#endif
- attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
-// // NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
-// // DeviceExt->VolumeFcb->RefCount++;
+ IrpSp->FileObject->FsContext = Vcb->volume_fcb;
IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
Irp->IoStatus.Information = FILE_OPENED;
Status = STATUS_SUCCESS;
} else {
- BOOL exclusive;
+ BOOL exclusive, skip_lock;
ULONG disposition;
TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
- if (IrpSp->FileObject->RelatedFileObject) {
- fcb* relfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
-
- if (relfcb)
- TRACE("related file name = %.*S\n", relfcb->full_filename.Length / sizeof(WCHAR), relfcb->full_filename.Buffer);
- }
+ if (IrpSp->FileObject->RelatedFileObject)
+ TRACE("related file = %S\n", file_desc(IrpSp->FileObject->RelatedFileObject));
disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
// We acquire the lock exclusively if there's the possibility we might be writing
exclusive = disposition != FILE_OPEN;
+
+ // Don't lock again if we're being called from within CcCopyRead etc.
+ skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
- acquire_tree_lock(Vcb, exclusive);
+ if (!skip_lock)
+ acquire_tree_lock(Vcb, exclusive);
// ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
// Status = NtfsCreateFile(DeviceObject,
// Irp);
- Status = create_file(DeviceObject, Irp, &rollback);
+ Status = open_file(DeviceObject, Irp, &rollback);
// ExReleaseResourceLite(&Vpb->DirResource);
if (exclusive && !NT_SUCCESS(Status))
else
clear_rollback(&rollback);
- release_tree_lock(Vcb, exclusive);
+ if (!skip_lock)
+ release_tree_lock(Vcb, exclusive);
// Status = STATUS_ACCESS_DENIED;
}
enum DirEntryType dir_entry_type;
} dir_entry;
-static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type) {
+ ULONG att, tag, br;
+ NTSTATUS Status;
+
+ // FIXME - will this slow things down?
+
+ if (type == BTRFS_TYPE_SYMLINK)
+ return IO_REPARSE_TAG_SYMLINK;
+
+ if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY)
+ return 0;
+
+ att = get_file_attributes(Vcb, NULL, subvol, inode, type, FALSE, FALSE);
+
+ if (!(att & FILE_ATTRIBUTE_REPARSE_POINT))
+ return 0;
+
+ if (type == BTRFS_TYPE_DIRECTORY) {
+ UINT8* data;
+ UINT16 datalen;
+
+ if (!get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen))
+ return 0;
+
+ if (!data)
+ return 0;
+
+ if (datalen < sizeof(ULONG)) {
+ ExFreePool(data);
+ return 0;
+ }
+
+ RtlCopyMemory(&tag, data, sizeof(ULONG));
+
+ ExFreePool(data);
+ } else {
+ // FIXME - see if file loaded and cached, and do CcCopyRead if it is
+
+ Status = read_file(Vcb, subvol, inode, (UINT8*)&tag, 0, sizeof(ULONG), &br);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ return 0;
+ }
+
+ if (br < sizeof(ULONG))
+ return 0;
+ }
+
+ return tag;
+}
+
+static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
PIO_STACK_LOCATION IrpSp;
UINT32 needed;
UINT64 inode;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol
- r = fcb->Vcb->roots;
- while (r && r->id != de->key.obj_id)
- r = r->next;
+ LIST_ENTRY* le;
+
+ r = NULL;
+
+ le = fcb->Vcb->roots.Flink;
+ while (le != &fcb->Vcb->roots) {
+ root* subvol = CONTAINING_RECORD(le, root, list_entry);
+
+ if (subvol->id == de->key.obj_id) {
+ r = subvol;
+ break;
+ }
+
+ le = le->Flink;
+ }
if (!r) {
ERR("could not find root %llx\n", de->key.obj_id);
LIST_ENTRY* le;
BOOL found = FALSE;
- le = fcb->children.Flink;
- while (le != &fcb->children) {
- struct _fcb* c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+ if (fileref) {
+ ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
- if (c->subvol == r && c->inode == inode) {
- ii = c->inode_item;
- found = TRUE;
- break;
+ le = fileref->children.Flink;
+ while (le != &fileref->children) {
+ file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
+
+ if (c->fcb->subvol == r && c->fcb->inode == inode && !c->fcb->ads) {
+ ii = c->fcb->inode_item;
+ found = TRUE;
+ break;
+ }
+
+ le = le->Flink;
}
- le = le->Flink;
+ ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
}
if (!found) {
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
if (tp.item->size > 0)
RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
-
- free_traverse_ptr(&tp);
}
break;
break;
case DirEntryType_Parent:
- ii = fcb->par->inode_item;
- r = fcb->par->subvol;
- inode = fcb->par->inode;
+ if (fileref && fileref->parent) {
+ ii = fileref->parent->fcb->inode_item;
+ r = fileref->parent->fcb->subvol;
+ inode = fileref->parent->fcb->inode;
+ } else {
+ ERR("no fileref\n");
+ return STATUS_INTERNAL_ERROR;
+ }
break;
}
}
fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fbdi->FileNameLength = stringlen;
- fbdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fbdi->ShortNameLength = 0;
// fibdi->ShortName[12];
ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
ffdi->FileNameLength = stringlen;
- ffdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fibdi->FileNameLength = stringlen;
- fibdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
+ fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fibdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fibdi->FileId.QuadPart = inode;
return STATUS_NO_MORE_FILES;
}
-static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
+static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
KEY searchkey;
traverse_ptr next_tp;
DIR_ITEM* di;
NTSTATUS Status;
- if (fcb->par) { // don't return . and .. if root directory
+ if (fileref && fileref->parent) { // don't return . and .. if root directory
if (*offset == 0) {
de->key.obj_id = fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
return STATUS_SUCCESS;
} else if (*offset == 1) {
- de->key.obj_id = fcb->par->inode;
+ de->key.obj_id = fileref->parent->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Parent;
Status = find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(tp);
tp->tree = NULL;
return Status;
}
if (keycmp(&tp->item->key, &searchkey) == -1) {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
- free_traverse_ptr(tp);
*tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
}
if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) {
- free_traverse_ptr(tp);
tp->tree = NULL;
return STATUS_NO_MORE_FILES;
}
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
- free_traverse_ptr(tp);
tp->tree = NULL;
return STATUS_INTERNAL_ERROR;
}
} else {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) {
- free_traverse_ptr(tp);
*tp = next_tp;
*offset = tp->item->key.offset + 1;
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
-
- free_traverse_ptr(&next_tp);
return STATUS_INTERNAL_ERROR;
}
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
- } else {
- free_traverse_ptr(&next_tp);
+ } else
return STATUS_NO_MORE_FILES;
- }
} else
return STATUS_NO_MORE_FILES;
}
NTSTATUS Status, status2;
fcb* fcb;
ccb* ccb;
+ file_ref* fileref;
void* buf;
UINT8 *curitem, *lastitem;
LONG length;
ULONG count;
- BOOL has_wildcard = FALSE, specific_file = FALSE;
+ BOOL has_wildcard = FALSE, specific_file = FALSE, initial;
// UINT64 num_reads_orig;
traverse_ptr tp;
dir_entry de;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
fcb = IrpSp->FileObject->FsContext;
ccb = IrpSp->FileObject->FsContext2;
+ fileref = ccb ? ccb->fileref : NULL;
acquire_tree_lock(fcb->Vcb, FALSE);
- TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("%S\n", file_desc(IrpSp->FileObject));
if (IrpSp->Flags == 0) {
TRACE("QD flags: (none)\n");
TRACE(" unknown flags: %u\n", flags);
}
+ initial = !ccb->query_string.Buffer;
+
if (IrpSp->Flags & SL_RESTART_SCAN) {
ccb->query_dir_offset = 0;
} else {
has_wildcard = ccb->has_wildcard;
specific_file = ccb->specific_file;
+
+ if (!(IrpSp->Flags & SL_RESTART_SCAN))
+ initial = FALSE;
}
if (ccb->query_string.Buffer) {
}
tp.tree = NULL;
- Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+ Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
if (!NT_SUCCESS(Status)) {
- if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ if (Status == STATUS_NO_MORE_FILES && initial)
Status = STATUS_NO_SUCH_FILE;
goto end;
}
if (Irp->MdlAddress && !buf) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
- if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
di_uni_fn.Buffer = uni_fn;
while (!FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
- Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+ Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
ExFreePool(uni_fn);
if (NT_SUCCESS(Status)) {
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
- if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
di_uni_fn.Buffer = uni_fn;
} else {
- if (tp.tree) free_traverse_ptr(&tp);
-
- if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
+ if (Status == STATUS_NO_MORE_FILES && initial)
Status = STATUS_NO_SUCH_FILE;
goto end;
TRACE("file(0) = %.*s\n", de.namelen, de.name);
TRACE("offset = %u\n", ccb->query_dir_offset - 1);
- Status = query_dir_item(fcb, buf, &length, Irp, &de, fcb->subvol);
+ Status = query_dir_item(fcb, fileref, buf, &length, Irp, &de, fcb->subvol);
count = 0;
if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
WCHAR* uni_fn = NULL;
UNICODE_STRING di_uni_fn;
- Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
+ Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
if (NT_SUCCESS(Status)) {
if (has_wildcard) {
ULONG stringlen;
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
- if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
- if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
TRACE("file(%u) %u = %.*s\n", count, curitem - (UINT8*)buf, de.namelen, de.name);
TRACE("offset = %u\n", ccb->query_dir_offset - 1);
- status2 = query_dir_item(fcb, curitem, &length, Irp, &de, fcb->subvol);
+ status2 = query_dir_item(fcb, fileref, curitem, &length, Irp, &de, fcb->subvol);
if (NT_SUCCESS(status2)) {
ULONG* lastoffset = (ULONG*)lastitem;
Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
- if (tp.tree) free_traverse_ptr(&tp);
-
end:
release_tree_lock(fcb->Vcb, FALSE);
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb->fileref;
NTSTATUS Status;
// WCHAR fn[MAX_PATH];
TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
+ if (!fileref) {
+ ERR("no fileref\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
acquire_tree_lock(fcb->Vcb, FALSE);
if (fcb->type != BTRFS_TYPE_DIRECTORY) {
// FIXME - raise exception if FCB marked for deletion?
- TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("%S\n", file_desc(FileObject));
- FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fcb->full_filename,
+ FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fileref->full_filename,
IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
Status = STATUS_PENDING;
if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) {
Irp->IoStatus.Status = Status;
+
+ if (Irp->UserIosb)
+ *Irp->UserIosb = Irp->IoStatus;
+
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
}
--- /dev/null
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "btrfs_drv.h"
+
+static __inline ULONG get_extent_data_len(UINT8 type) {
+ switch (type) {
+ case TYPE_TREE_BLOCK_REF:
+ return sizeof(TREE_BLOCK_REF);
+
+ case TYPE_EXTENT_DATA_REF:
+ return sizeof(EXTENT_DATA_REF);
+
+ // FIXME - TYPE_EXTENT_REF_V0
+ // FIXME - TYPE_SHARED_BLOCK_REF
+
+ case TYPE_SHARED_DATA_REF:
+ return sizeof(SHARED_DATA_REF);
+
+ default:
+ return 0;
+ }
+}
+
+static __inline UINT64 get_extent_data_refcount(UINT8 type, void* data) {
+ switch (type) {
+ case TYPE_TREE_BLOCK_REF:
+ return 1;
+
+ case TYPE_EXTENT_DATA_REF:
+ {
+ EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
+ return edr->count;
+ }
+
+ // FIXME - TYPE_EXTENT_REF_V0
+ // FIXME - TYPE_SHARED_BLOCK_REF
+
+ case TYPE_SHARED_DATA_REF:
+ {
+ SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
+ return sdr->count;
+ }
+
+ default:
+ return 0;
+ }
+}
+
+static UINT64 get_extent_data_ref_hash(EXTENT_DATA_REF* edr) {
+ UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff;
+
+ high_crc = calc_crc32c(high_crc, (UINT8*)&edr->root, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&edr->objid, sizeof(UINT64));
+ low_crc = calc_crc32c(low_crc, (UINT8*)&edr->offset, sizeof(UINT64));
+
+ return ((UINT64)high_crc << 31) ^ (UINT64)low_crc;
+}
+
+static UINT64 get_extent_hash(UINT8 type, void* data) {
+ if (type == TYPE_EXTENT_DATA_REF) {
+ return get_extent_data_ref_hash((EXTENT_DATA_REF*)data);
+ } else {
+ ERR("unhandled extent type %x\n", type);
+ return 0;
+ }
+}
+
+static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+ ULONG datalen = get_extent_data_len(type), len, max_extent_item_size;
+ EXTENT_ITEM* ei;
+ UINT8* ptr;
+ UINT64 inline_rc, offset;
+ UINT8* data2;
+ EXTENT_ITEM* newei;
+
+ // FIXME - handle A9s
+
+ if (datalen == 0) {
+ ERR("unrecognized extent type %x\n", type);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ // If entry doesn't exist yet, create new inline extent item
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ULONG eisize;
+ EXTENT_ITEM* ei;
+ BOOL is_tree = type == TYPE_TREE_BLOCK_REF;
+ UINT8* ptr;
+
+ eisize = sizeof(EXTENT_ITEM);
+ if (is_tree) eisize += sizeof(EXTENT_ITEM2);
+ eisize += sizeof(UINT8);
+ eisize += datalen;
+
+ ei = ExAllocatePoolWithTag(PagedPool, eisize, ALLOC_TAG);
+ if (!ei) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ei->refcount = get_extent_data_refcount(type, data);
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = is_tree ? EXTENT_ITEM_TREE_BLOCK : EXTENT_ITEM_DATA;
+ ptr = (UINT8*)&ei[1];
+
+ if (is_tree) {
+ EXTENT_ITEM2* ei2 = (EXTENT_ITEM2*)ptr;
+ ei2->firstitem = *firstitem;
+ ei2->level = level;
+ ptr = (UINT8*)&ei2[1];
+ }
+
+ *ptr = type;
+ RtlCopyMemory(ptr + 1, data, datalen);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - add to space list?
+
+ return STATUS_SUCCESS;
+ } else if (tp.item->key.offset != size) {
+ ERR("extent %llx exists, but with size %llx rather than %llx expected\n", tp.item->key.obj_id, tp.item->key.offset, size);
+ return STATUS_INTERNAL_ERROR;
+ } else if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ Status = convert_old_data_extent(Vcb, address, size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback);
+ } else if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ ptr = (UINT8*)&ei[1];
+
+ if (ei->flags & EXTENT_ITEM_TREE_BLOCK) {
+ if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ len -= sizeof(EXTENT_ITEM2);
+ ptr += sizeof(EXTENT_ITEM2);
+ }
+
+ inline_rc = 0;
+
+ // Loop through existing inline extent entries
+
+ while (len > 0) {
+ UINT8 secttype = *ptr;
+ ULONG sectlen = get_extent_data_len(secttype);
+ UINT64 sectcount = get_extent_data_refcount(secttype, ptr + sizeof(UINT8));
+
+ len--;
+
+ if (sectlen > len) {
+ ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (sectlen == 0) {
+ ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (secttype == TYPE_SHARED_DATA_REF) {
+ TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id);
+
+ Status = convert_shared_data_extent(Vcb, address, size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback);
+ }
+
+ // If inline extent already present, increase refcount and return
+
+ if (secttype == type) {
+ if (type == TYPE_EXTENT_DATA_REF) {
+ EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8));
+ EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
+
+ if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
+ UINT32 rc = get_extent_data_refcount(type, data);
+ EXTENT_DATA_REF* sectedr2;
+
+ newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ if (!newei) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(newei, tp.item->data, tp.item->size);
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount += rc;
+
+ sectedr2 = (EXTENT_DATA_REF*)((UINT8*)newei + ((UINT8*)sectedr - tp.item->data));
+ sectedr2->count += rc;
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+ }
+ } else if (type == TYPE_TREE_BLOCK_REF) {
+ ERR("trying to increase refcount of tree extent\n");
+ return STATUS_INTERNAL_ERROR;
+ } else {
+ ERR("unhandled extent type %x\n", type);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ len -= sectlen;
+ ptr += sizeof(UINT8) + sectlen;
+ inline_rc += sectcount;
+ }
+
+ offset = get_extent_hash(type, data);
+
+ max_extent_item_size = (Vcb->superblock.node_size >> 4) - sizeof(leaf_node);
+
+ // If we can, add entry as inline extent item
+
+ if (inline_rc == ei->refcount && tp.item->size + sizeof(UINT8) + datalen < max_extent_item_size) {
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ ptr = (UINT8*)&ei[1];
+
+ if (ei->flags & EXTENT_ITEM_TREE_BLOCK) {
+ len -= sizeof(EXTENT_ITEM2);
+ ptr += sizeof(EXTENT_ITEM2);
+ }
+
+ while (len > 0) {
+ UINT8 secttype = *ptr;
+ ULONG sectlen = get_extent_data_len(secttype);
+
+ if (secttype > type)
+ break;
+
+ len--;
+
+ if (secttype == type) {
+ UINT64 sectoff = get_extent_hash(secttype, ptr + 1);
+
+ if (sectoff > offset)
+ break;
+ }
+
+ len -= sectlen;
+ ptr += sizeof(UINT8) + sectlen;
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, tp.item->size + sizeof(UINT8) + datalen, ALLOC_TAG);
+ RtlCopyMemory(newei, tp.item->data, ptr - tp.item->data);
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount += get_extent_data_refcount(type, data);
+
+ if (len > 0)
+ RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data) + sizeof(UINT8) + datalen, ptr, len + 1);
+
+ ptr = (ptr - tp.item->data) + (UINT8*)newei;
+
+ *ptr = type;
+ RtlCopyMemory(ptr + 1, data, datalen);
+
+ 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)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ // Look for existing non-inline entry, and increase refcount if found
+
+ if (inline_rc != ei->refcount) {
+ traverse_ptr tp2;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = type;
+ searchkey.offset = offset;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (!keycmp(&tp.item->key, &searchkey)) {
+ if (tp.item->size < datalen) {
+ ERR("(%llx,%x,%llx) was %x bytes, expecting %x\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp.item->size, datalen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ data2 = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
+ RtlCopyMemory(data2, tp2.item->data, tp2.item->size);
+
+ if (type == TYPE_EXTENT_DATA_REF) {
+ EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data2;
+
+ edr->count += get_extent_data_refcount(type, data);
+ } else if (type == TYPE_TREE_BLOCK_REF) {
+ ERR("trying to increase refcount of tree extent\n");
+ return STATUS_INTERNAL_ERROR;
+ } else {
+ ERR("unhandled extent type %x\n", type);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ 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)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(newei, tp.item->data, tp.item->size);
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount += get_extent_data_refcount(type, data);
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ // Otherwise, add new non-inline entry
+
+ 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)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ RtlCopyMemory(newei, tp.item->data, tp.item->size);
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount += get_extent_data_refcount(type, data);
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, 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, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+ EXTENT_DATA_REF edr;
+
+ edr.root = subvol->id;
+ edr.objid = inode;
+ edr.offset = offset;
+ edr.count = refcount;
+
+ return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, rollback);
+}
+
+void decrease_chunk_usage(chunk* c, UINT64 delta) {
+ c->used -= delta;
+
+ TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
+}
+
+static NTSTATUS remove_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* changed_sector_list) {
+ chunk* c;
+ LIST_ENTRY* le;
+
+ if (changed_sector_list) {
+ changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+ if (!sc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sc->ol.key = address;
+ sc->checksums = NULL;
+ sc->length = size / Vcb->superblock.sector_size;
+
+ sc->deleted = TRUE;
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ c = NULL;
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
+ break;
+
+ le = le->Flink;
+ }
+ if (le == &Vcb->chunks) c = NULL;
+
+ if (c) {
+ decrease_chunk_usage(c, size);
+
+ add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
+ UINT8 level, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ NTSTATUS Status;
+ traverse_ptr tp, tp2;
+ EXTENT_ITEM* ei;
+ ULONG len;
+ UINT64 inline_rc, offset;
+ UINT8* ptr;
+ UINT32 rc = get_extent_data_refcount(type, data);
+ ULONG datalen = get_extent_data_len(type);
+
+ // FIXME - handle trees
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("could not find EXTENT_ITEM for address %llx\n", address);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.offset != size) {
+ ERR("extent %llx had length %llx, not %llx as expected\n", address, tp.item->key.offset, size);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+ TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ Status = convert_old_data_extent(Vcb, address, size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback);
+ }
+
+ if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ ptr = (UINT8*)&ei[1];
+
+ if (ei->flags & EXTENT_ITEM_TREE_BLOCK) {
+ if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ len -= sizeof(EXTENT_ITEM2);
+ ptr += sizeof(EXTENT_ITEM2);
+ }
+
+ if (ei->refcount < rc) {
+ ERR("error - extent has refcount %llx, trying to reduce by %x\n", ei->refcount, rc);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ inline_rc = 0;
+
+ // Loop through inline extent entries
+
+ while (len > 0) {
+ UINT8 secttype = *ptr;
+ ULONG sectlen = get_extent_data_len(secttype);
+ UINT64 sectcount = get_extent_data_refcount(secttype, ptr + sizeof(UINT8));
+
+ len--;
+
+ if (sectlen > len) {
+ ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (sectlen == 0) {
+ ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (secttype == TYPE_SHARED_DATA_REF) {
+ TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id);
+
+ Status = convert_shared_data_extent(Vcb, address, size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback);
+ }
+
+ if (secttype == type) {
+ if (type == TYPE_EXTENT_DATA_REF) {
+ EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8));
+ EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
+ ULONG neweilen;
+ EXTENT_ITEM* newei;
+
+ if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
+ if (ei->refcount == edr->count) {
+ Status = remove_extent(Vcb, address, size, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+ return STATUS_SUCCESS;
+ }
+
+ if (sectedr->count < edr->count) {
+ ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr->count, edr->count);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (sectedr->count > edr->count) // reduce section refcount
+ neweilen = tp.item->size;
+ else // remove section entirely
+ neweilen = tp.item->size - sizeof(UINT8) - sectlen;
+
+ newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
+ if (!newei) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (sectedr->count > edr->count) {
+ EXTENT_DATA_REF* newedr = (EXTENT_DATA_REF*)((UINT8*)newei + ((UINT8*)sectedr - tp.item->data));
+
+ RtlCopyMemory(newei, ei, neweilen);
+
+ newedr->count -= rc;
+ } else {
+ RtlCopyMemory(newei, ei, ptr - tp.item->data);
+
+ if (len > sectlen)
+ RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(UINT8), len - sectlen);
+ }
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount -= rc;
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+ }
+// } else if (type == TYPE_TREE_BLOCK_REF) {
+// ERR("trying to increase refcount of tree extent\n");
+// return STATUS_INTERNAL_ERROR;
+ } else {
+ ERR("unhandled extent type %x\n", type);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ len -= sectlen;
+ ptr += sizeof(UINT8) + sectlen;
+ inline_rc += sectcount;
+ }
+
+ if (inline_rc == ei->refcount) {
+ ERR("entry not found in inline extent item for address %llx\n", address);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ offset = get_extent_hash(type, data);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = type;
+ searchkey.offset = offset;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ ERR("(%llx,%x,%llx) not found\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp2.item->size < datalen) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (type == TYPE_EXTENT_DATA_REF) {
+ EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)tp2.item->data;
+ EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
+ EXTENT_ITEM* newei;
+
+ if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
+ if (ei->refcount == edr->count) {
+ Status = remove_extent(Vcb, address, size, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+ delete_tree_item(Vcb, &tp2, rollback);
+ return STATUS_SUCCESS;
+ }
+
+ if (sectedr->count < edr->count) {
+ ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr->count, edr->count);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ delete_tree_item(Vcb, &tp2, rollback);
+
+ if (sectedr->count > edr->count) {
+ EXTENT_DATA_REF* newedr = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
+
+ if (!newedr) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(newedr, sectedr, tp2.item->size);
+
+ 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)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ if (!newei) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(newei, tp.item->data, tp.item->size);
+
+ newei->generation = Vcb->superblock.generation;
+ newei->refcount -= rc;
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+ } else {
+ ERR("error - hash collision?\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+// } else if (type == TYPE_TREE_BLOCK_REF) {
+// ERR("trying to increase refcount of tree extent\n");
+// return STATUS_INTERNAL_ERROR;
+ } else {
+ ERR("unhandled extent type %x\n", type);
+ return STATUS_INTERNAL_ERROR;
+ }
+}
+
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode,
+ UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ EXTENT_DATA_REF edr;
+
+ edr.root = subvol->id;
+ edr.objid = inode;
+ edr.offset = offset;
+ edr.count = refcount;
+
+ return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, changed_sector_list, rollback);
+}
+
+typedef struct {
+ UINT8 type;
+ void* data;
+ BOOL allocated;
+ UINT64 hash;
+ LIST_ENTRY list_entry;
+} extent_ref;
+
+static void free_extent_refs(LIST_ENTRY* extent_refs) {
+ while (!IsListEmpty(extent_refs)) {
+ LIST_ENTRY* le = RemoveHeadList(extent_refs);
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+}
+
+static NTSTATUS add_data_extent_ref(LIST_ENTRY* extent_refs, UINT64 tree_id, UINT64 obj_id, UINT64 offset) {
+ extent_ref* er2;
+ EXTENT_DATA_REF* edr;
+ LIST_ENTRY* le;
+
+ if (!IsListEmpty(extent_refs)) {
+ le = extent_refs->Flink;
+
+ while (le != extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+
+ if (er->type == TYPE_EXTENT_DATA_REF) {
+ edr = (EXTENT_DATA_REF*)er->data;
+
+ if (edr->root == tree_id && edr->objid == obj_id && edr->offset == offset) {
+ edr->count++;
+ return STATUS_SUCCESS;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+ if (!er2) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ edr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA_REF), ALLOC_TAG);
+ if (!edr) {
+ ERR("out of memory\n");
+ ExFreePool(er2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ edr->root = tree_id;
+ edr->objid = obj_id;
+ edr->offset = offset;
+ edr->count = 1; // FIXME - not necessarily
+
+ er2->type = TYPE_EXTENT_DATA_REF;
+ er2->data = edr;
+ er2->allocated = TRUE;
+
+ InsertTailList(extent_refs, &er2->list_entry);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, LIST_ENTRY* rollback) {
+ LIST_ENTRY *le, *next_le;
+ UINT64 refcount;
+ ULONG inline_len;
+ BOOL all_inline = TRUE;
+ extent_ref* first_noninline;
+ EXTENT_ITEM* ei;
+ UINT8* siptr;
+
+ if (IsListEmpty(extent_refs)) {
+ WARN("no extent refs found\n");
+ return STATUS_SUCCESS;
+ }
+
+ refcount = 0;
+ inline_len = sizeof(EXTENT_ITEM);
+
+ le = extent_refs->Flink;
+ while (le != extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ UINT64 rc;
+
+ next_le = le->Flink;
+
+ rc = get_extent_data_refcount(er->type, er->data);
+
+ if (rc == 0) {
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ RemoveEntryList(&er->list_entry);
+
+ ExFreePool(er);
+ } else {
+ ULONG extlen = get_extent_data_len(er->type);
+
+ refcount += rc;
+
+ if (er->type == TYPE_EXTENT_DATA_REF)
+ er->hash = get_extent_data_ref_hash(er->data);
+ else
+ er->hash = 0;
+
+ if (all_inline) {
+ if (inline_len + 1 + extlen > Vcb->superblock.node_size / 4) {
+ all_inline = FALSE;
+ first_noninline = er;
+ } else
+ inline_len += extlen + 1;
+ }
+ }
+
+ le = next_le;
+ }
+
+ ei = ExAllocatePoolWithTag(PagedPool, inline_len, ALLOC_TAG);
+ if (!ei) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ei->refcount = refcount;
+ ei->generation = Vcb->superblock.generation;
+ ei->flags = flags;
+
+ // Do we need to sort the inline extent refs? The Linux driver doesn't seem to bother.
+
+ siptr = (UINT8*)&ei[1];
+ le = extent_refs->Flink;
+ while (le != extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ ULONG extlen = get_extent_data_len(er->type);
+
+ if (!all_inline && er == first_noninline)
+ break;
+
+ *siptr = er->type;
+ siptr++;
+
+ if (extlen > 0) {
+ RtlCopyMemory(siptr, er->data, extlen);
+ siptr += extlen;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, rollback)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ei);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!all_inline) {
+ le = &first_noninline->list_entry;
+
+ 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)) {
+ ERR("error - failed to insert item\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ er->allocated = FALSE;
+
+ le = le->Flink;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tree_address, UINT64 extent_address, LIST_ENTRY* extent_refs) {
+ UINT8* buf;
+ tree_header* th;
+ NTSTATUS Status;
+
+ buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+ if (!buf) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = read_tree(Vcb, tree_address, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_tree returned %08x\n", Status);
+ ExFreePool(buf);
+ return Status;
+ }
+
+ th = (tree_header*)buf;
+
+ if (th->level == 0) {
+ UINT32 i;
+ leaf_node* ln = (leaf_node*)&th[1];
+
+ for (i = 0; i < th->num_items; i++) {
+ if (ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA) && ln[i].offset + ln[i].size <= Vcb->superblock.node_size - sizeof(tree_header)) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)(((UINT8*)&th[1]) + ln[i].offset);
+
+ if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+
+ if (ed2->address == extent_address) {
+ Status = add_data_extent_ref(extent_refs, th->tree_id, ln[i].key.obj_id, ln[i].key.offset);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_data_extent_ref returned %08x\n", Status);
+ ExFreePool(buf);
+ return Status;
+ }
+ }
+ }
+ }
+ }
+ } else
+ WARN("shared data ref pointed to tree of level %x\n", th->level);
+
+ ExFreePool(buf);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ traverse_ptr tp;
+ LIST_ENTRY extent_refs;
+ LIST_ENTRY *le, *next_le;
+ EXTENT_ITEM* ei;
+ UINT64 eiflags, inline_rc;
+ UINT8* siptr;
+ ULONG len;
+ NTSTATUS Status;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ei = (EXTENT_ITEM*)tp.item->data;
+ len = tp.item->size - sizeof(EXTENT_ITEM);
+ eiflags = ei->flags;
+
+ InitializeListHead(&extent_refs);
+
+ inline_rc = 0;
+ siptr = (UINT8*)&ei[1];
+
+ do {
+ extent_ref* er;
+ ULONG extlen;
+
+ extlen = get_extent_data_len(*siptr);
+
+ if (extlen == 0) {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (extlen > len - 1) {
+ ERR("extent %llx was truncated\n", address);
+ free_extent_refs(&extent_refs);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+ if (!er) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ er->type = *siptr;
+
+ er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
+ if (!er->data) {
+ ERR("out of memory\n");
+ ExFreePool(er);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(er->data, siptr+1, extlen);
+ er->allocated = TRUE;
+
+ InsertTailList(&extent_refs, &er->list_entry);
+
+ siptr += extlen;
+ len -= extlen + 1;
+
+ inline_rc += get_extent_data_refcount(er->type, er->data);
+ } while (len > 0);
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (inline_rc < ei->refcount) {
+ BOOL b;
+ traverse_ptr next_tp;
+
+ do {
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.obj_id == address) {
+ ULONG extlen;
+
+ extlen = get_extent_data_len(tp.item->key.obj_type);
+
+ if (extlen != 0 && tp.item->size >= extlen) {
+ extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
+ if (!er) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ er->type = tp.item->key.obj_type;
+
+ er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
+ if (!er->data) {
+ ERR("out of memory\n");
+ ExFreePool(er);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(er->data, siptr+1, extlen);
+ er->allocated = TRUE;
+
+ InsertTailList(&extent_refs, &er->list_entry);
+
+ delete_tree_item(Vcb, &tp, rollback);
+ }
+ }
+
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > address)
+ break;
+ }
+ } while (b);
+ }
+
+ le = extent_refs.Flink;
+ while (le != &extent_refs) {
+ extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ next_le = le->Flink;
+
+ if (er->type == TYPE_SHARED_DATA_REF) {
+ SHARED_DATA_REF* sdr = er->data;
+
+ Status = populate_extent_refs_from_tree(Vcb, sdr->offset, address, &extent_refs);
+ if (!NT_SUCCESS(Status)) {
+ ERR("populate_extent_refs_from_tree returned %08x\n", Status);
+ free_extent_refs(&extent_refs);
+ return Status;
+ }
+
+ RemoveEntryList(&er->list_entry);
+
+ if (er->allocated)
+ ExFreePool(er->data);
+
+ ExFreePool(er);
+ }
+ // FIXME - also do for SHARED_BLOCK_REF?
+
+ le = next_le;
+ }
+
+ Status = construct_extent_item(Vcb, address, size, eiflags, &extent_refs, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("construct_extent_item returned %08x\n", Status);
+ free_extent_refs(&extent_refs);
+ return Status;
+ }
+
+ free_extent_refs(&extent_refs);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LIST_ENTRY extent_refs;
+ NTSTATUS Status;
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = size;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", address, size);
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size != sizeof(EXTENT_ITEM_V0)) {
+ TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
+ return STATUS_SUCCESS;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_REF_V0;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ InitializeListHead(&extent_refs);
+
+ do {
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ Status = populate_extent_refs_from_tree(Vcb, tp.item->key.offset, address, &extent_refs);
+ if (!NT_SUCCESS(Status)) {
+ ERR("populate_extent_refs_from_tree returned %08x\n", Status);
+ return Status;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+ }
+
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
+ break;
+ }
+ } while (b);
+
+ Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("construct_extent_item returned %08x\n", Status);
+ free_extent_refs(&extent_refs);
+ return Status;
+ }
+
+ free_extent_refs(&extent_refs);
+
+ return STATUS_SUCCESS;
+}
#include "btrfs_drv.h"
-static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback);
+static NTSTATUS STDCALL move_subvol(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32,
+ UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback);
static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
ULONG defda;
BOOL inode_item_changed = FALSE;
NTSTATUS Status;
- if (fcb->ads)
- fcb = fcb->par;
+ if (fcb->ads) {
+ if (fileref && fileref->parent)
+ fcb = fileref->parent->fcb;
+ else {
+ ERR("stream did not have fileref\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
- TRACE("file = %.*S, attributes = %x\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fbi->FileAttributes);
+ TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes);
if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
LARGE_INTEGER time;
BTRFS_TIME now;
- defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
+ defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
if (fcb->type == BTRFS_TYPE_DIRECTORY)
fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
else
WARN("couldn't find old INODE_ITEM\n");
- free_traverse_ptr(&tp);
-
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
ULONG atts;
- TRACE("changing delete_on_close to %s for %.*S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+ if (!fileref)
+ return STATUS_INVALID_PARAMETER;
+
+ TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
+
+ if (fcb->ads) {
+ if (fileref->parent)
+ atts = fileref->parent->fcb->atts;
+ else {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else
+ atts = fcb->atts;
- atts = fcb->ads ? fcb->par->atts : fcb->atts;
TRACE("atts = %x\n", atts);
if (atts & FILE_ATTRIBUTE_READONLY)
return STATUS_CANNOT_DELETE;
+ // FIXME - can we skip this bit for subvols?
if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
return STATUS_DIRECTORY_NOT_EMPTY;
return STATUS_CANNOT_DELETE;
}
- if (fcb->inode == SUBVOL_ROOT_INODE) {
- FIXME("FIXME - subvol deletion not yet supported\n");
- return STATUS_INTERNAL_ERROR;
- }
+ ccb->fileref->delete_on_close = fdi->DeleteFile;
- fcb->delete_on_close = fdi->DeleteFile;
- // FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
FileObject->DeletePending = fdi->DeleteFile;
return STATUS_SUCCESS;
if (iersize > maxlen) {
ERR("item would be too long (%u > %u)\n", iersize, maxlen);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
ier2 = ExAllocatePoolWithTag(PagedPool, iersize, ALLOC_TAG);
if (!ier2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
} else {
ier = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_EXTREF) - 1 + utf8->Length, ALLOC_TAG);
if (!ier) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
if (irsize > maxlen) {
if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
TRACE("INODE_REF too long, creating INODE_EXTREF\n");
- free_traverse_ptr(&tp);
return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback);
} else {
ERR("item would be too long (%u > %u)\n", irsize, maxlen);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
}
ir2 = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
if (!ir2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
} else {
ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + utf8->Length, ALLOC_TAG);
if (!ir) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
-static NTSTATUS get_fcb_from_dir_item(device_extension* Vcb, fcb** pfcb, fcb* parent, root* subvol, DIR_ITEM* di) {
+static NTSTATUS get_fileref_from_dir_item(device_extension* Vcb, file_ref** pfr, file_ref* parent, root* subvol, DIR_ITEM* di) {
LIST_ENTRY* le;
+ file_ref* fileref;
fcb* sf2;
- struct _fcb* c;
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
le = parent->children.Flink;
while (le != &parent->children) {
- c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+ file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
- if (c->refcount > 0 && c->inode == di->key.obj_id && c->subvol == subvol) {
- c->refcount++;
+ if (c->fcb->inode == di->key.obj_id && c->fcb->subvol == subvol) {
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
+ LONG rc = InterlockedIncrement(&c->refcount);
+ WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
+#else
+ InterlockedIncrement(&c->refcount);
#endif
- *pfcb = c;
+ *pfr = c;
return STATUS_SUCCESS;
}
le = le->Flink;
}
+ fileref = create_fileref();
+ if (!fileref) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
sf2 = create_fcb();
if (!sf2) {
ERR("out of memory\n");
+ free_fileref(fileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
+ fileref->fcb = sf2;
sf2->Vcb = Vcb;
- sf2->utf8.Length = sf2->utf8.MaximumLength = di->n;
- sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
- if (!sf2->utf8.Buffer) {
+ fileref->utf8.Length = fileref->utf8.MaximumLength = di->n;
+ fileref->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
+ if (!fileref->utf8.Buffer) {
ERR("out of memory\n");
+ free_fileref(fileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(sf2->utf8.Buffer, di->name, di->n);
+ RtlCopyMemory(fileref->utf8.Buffer, di->name, di->n);
- sf2->par = parent;
-
parent->refcount++;
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", parent, parent->refcount, parent->full_filename.Length / sizeof(WCHAR), parent->full_filename.Buffer);
+ WARN("fileref %p: refcount now %i (%S)\n", parent, parent->refcount, file_desc_fileref(parent));
#endif
if (di->key.obj_type == TYPE_ROOT_ITEM) {
- root* fcbroot = Vcb->roots;
- while (fcbroot && fcbroot->id != di->key.obj_id)
- fcbroot = fcbroot->next;
+ root* fcbroot = NULL;
+
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == di->key.obj_id) {
+ fcbroot = r;
+ break;
+ }
+
+ le = le->Flink;
+ }
sf2->subvol = fcbroot;
sf2->inode = SUBVOL_ROOT_INODE;
sf2->type = di->type;
- if (Vcb->fcbs)
- Vcb->fcbs->prev = sf2;
-
- sf2->next = Vcb->fcbs;
- Vcb->fcbs = sf2;
-
- sf2->name_offset = parent->full_filename.Length / sizeof(WCHAR);
+ fileref->name_offset = parent->full_filename.Length / sizeof(WCHAR);
- if (parent != Vcb->root_fcb)
- sf2->name_offset++;
+ if (parent != Vcb->root_fileref)
+ fileref->name_offset++;
- InsertTailList(&parent->children, &sf2->list_entry);
+ InsertTailList(&parent->children, &fileref->list_entry);
searchkey.obj_id = sf2->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
Status = find_item(Vcb, sf2->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_fcb(sf2);
+ free_fileref(fileref);
return Status;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id);
- free_traverse_ptr(&tp);
- free_fcb(sf2);
+ free_fileref(fileref);
return STATUS_INTERNAL_ERROR;
}
if (tp.item->size > 0)
RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
- free_traverse_ptr(&tp);
-
// This is just a quick function for the sake of move_across_subvols. As such, we don't bother
// with sf2->atts, sf2->sd, or sf2->full_filename.
- *pfcb = sf2;
+ fileref->parent = (struct _file_ref*)parent;
+ InsertTailList(&parent->children, &fileref->list_entry);
+
+ *pfr = fileref;
return STATUS_SUCCESS;
}
// return rc;
// }
-static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, UINT64 inode, UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, UINT64 inode,
+ UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
UINT64 oldindex, index;
UINT32 oldcrc32;
INODE_ITEM* ii;
// move INODE_ITEM
- fcb->inode_item.transid = Vcb->superblock.generation;
- fcb->inode_item.sequence++;
- fcb->inode_item.st_ctime = *now;
+ fileref->fcb->inode_item.transid = Vcb->superblock.generation;
+ fileref->fcb->inode_item.sequence++;
+ fileref->fcb->inode_item.st_ctime = *now;
- searchkey.obj_id = fcb->inode;
+ searchkey.obj_id = fileref->fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
if (!keycmp(&searchkey, &tp.item->key)) {
delete_tree_item(Vcb, &tp, rollback);
- if (fcb->inode_item.st_nlink > 1) {
- fcb->inode_item.st_nlink--;
+ if (fileref->fcb->inode_item.st_nlink > 1) {
+ fileref->fcb->inode_item.st_nlink--;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &fileref->fcb->inode_item, sizeof(INODE_ITEM));
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
WARN("couldn't find old INODE_ITEM\n");
}
- free_traverse_ptr(&tp);
-
- fcb->inode_item.st_nlink = 1;
+ fileref->fcb->inode_item.st_nlink = 1;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &fileref->fcb->inode_item, sizeof(INODE_ITEM));
if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_INTERNAL_ERROR;
}
- oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, (ULONG)fileref->utf8.Length);
// delete old DIR_ITEM
- Status = delete_dir_item(Vcb, fcb->subvol, oldparinode, oldcrc32, &fcb->utf8, rollback);
+ Status = delete_dir_item(Vcb, fileref->fcb->subvol, oldparinode, oldcrc32, &fileref->utf8, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_dir_item returned %08x\n", Status);
return Status;
di->transid = Vcb->superblock.generation;
di->m = 0;
di->n = utf8->Length;
- di->type = fcb->type;
+ di->type = fileref->fcb->type;
RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback);
return Status;
}
- Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, oldparinode, &fcb->utf8, &oldindex, rollback);
+ Status = delete_inode_ref(Vcb, fileref->fcb->subvol, fileref->fcb->inode, oldparinode, &fileref->utf8, &oldindex, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_inode_ref returned %08x\n", Status);
return Status;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = oldindex;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
delete_tree_item(Vcb, &tp, rollback);
else
WARN("couldn't find old DIR_INDEX\n");
-
- free_traverse_ptr(&tp);
}
// get new index
if (!keycmp(&searchkey, &tp.item->key)) {
if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = next_tp;
TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
} else
index = 2;
- free_traverse_ptr(&tp);
-
// create INODE_REF
Status = add_inode_ref(Vcb, destsubvol, inode, destinode, index, utf8, rollback);
di->transid = Vcb->superblock.generation;
di->m = 0;
di->n = utf8->Length;
- di->type = fcb->type;
+ di->type = fileref->fcb->type;
RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) {
// move XATTR_ITEMs
- searchkey.obj_id = fcb->inode;
+ searchkey.obj_id = fileref->fcb->inode;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = 0;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
do {
- if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->size > 0) {
+ if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->size > 0) {
di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!di) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_XATTR_ITEM, tp.item->key.offset, di, tp.item->size, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
- if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
+ if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
break;
}
} while (b);
- free_traverse_ptr(&tp);
-
// do extents
- searchkey.obj_id = fcb->inode;
+ searchkey.obj_id = fileref->fcb->inode;
searchkey.obj_type = TYPE_EXTENT_DATA;
searchkey.offset = 0;
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
do {
- if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
if (tp.item->size < sizeof(EXTENT_DATA)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
} else {
if (!ed) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ed, tp.item->size, NULL, rollback)) {
ERR("error - failed to insert item\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
if (ed2->address != 0) {
- Status = add_extent_ref(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset, rollback);
-
+ Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset - ed2->offset, 1, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("add_extent_ref returned %08x\n", Status);
- free_traverse_ptr(&tp);
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
return Status;
}
if (!has_hardlink) {
- Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, NULL, rollback);
+ Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, fileref->fcb->subvol, fileref->fcb->inode,
+ tp.item->key.offset - ed2->offset, 1, NULL, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("remove_extent_ref returned %08x\n", Status);
- free_traverse_ptr(&tp);
+ ERR("decrease_extent_refcount_data returned %08x\n", Status);
return Status;
}
}
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
- if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA)
+ if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA)
break;
}
} while (b);
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
typedef struct {
- fcb* fcb;
+ file_ref* fileref;
UINT8 level;
UINT32 crc32;
UINT64 newinode;
LIST_ENTRY list_entry;
} dir_list;
-static NTSTATUS add_to_dir_list(fcb* fcb, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) {
+static NTSTATUS add_to_dir_list(file_ref* fileref, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) {
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
*empty = TRUE;
- searchkey.obj_id = fcb->inode;
+ searchkey.obj_id = fileref->fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = 2;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
do {
- if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) {
+ if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) {
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
} else {
DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
- struct _fcb* child;
+ file_ref* child;
dir_list* dl2;
if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n + di->m) {
*empty = FALSE;
- Status = get_fcb_from_dir_item(fcb->Vcb, &child, fcb, fcb->subvol, di);
+ Status = get_fileref_from_dir_item(fileref->fcb->Vcb, &child, fileref, fileref->fcb->subvol, di);
if (!NT_SUCCESS(Status)) {
- ERR("get_fcb_from_dir_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
+ ERR("get_fileref_from_dir_item returned %08x\n", Status);
return Status;
}
dl2 = ExAllocatePoolWithTag(PagedPool, sizeof(dir_list), ALLOC_TAG);
if (!dl2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
- dl2->fcb = child;
+ dl2->fileref = child;
dl2->level = level;
dl2->newparinode = newparinode;
dl2->subvol = di->key.obj_type == TYPE_ROOT_ITEM;
dl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dl2->utf8.MaximumLength, ALLOC_TAG);
if (!dl2->utf8.Buffer) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
}
- b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
}
} while (b);
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
UINT64 inode, oldparinode;
NTSTATUS Status;
LIST_ENTRY dl;
+ if (fileref->fcb->inode_item.st_nlink > 1 && fileref->fcb->open_count > 1) {
+ WARN("not moving hard-linked inode across subvols when open more than once\n");
+ // FIXME - don't do this if only one fileref?
+ return STATUS_ACCESS_DENIED;
+ }
+
if (destsubvol->lastinode == 0)
get_last_inode(Vcb, destsubvol);
inode = destsubvol->lastinode + 1;
destsubvol->lastinode++;
- oldparinode = fcb->subvol == fcb->par->subvol ? fcb->par->inode : SUBVOL_ROOT_INODE;
+ oldparinode = fileref->fcb->subvol == fileref->parent->fcb->subvol ? fileref->parent->fcb->inode : SUBVOL_ROOT_INODE;
- Status = move_inode_across_subvols(Vcb, fcb, destsubvol, destinode, inode, oldparinode, utf8, crc32, now, rollback);
+ Status = move_inode_across_subvols(Vcb, fileref, destsubvol, destinode, inode, oldparinode, utf8, crc32, now, rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_inode_across_subvols returned %08x\n", Status);
return Status;
}
- if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
+ if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
BOOL b, empty;
UINT8 level, max_level;
LIST_ENTRY* le;
InitializeListHead(&dl);
- add_to_dir_list(fcb, 0, &dl, inode, &b);
+ add_to_dir_list(fileref, 0, &dl, inode, &b);
level = 0;
do {
dl2->newinode = inode;
- if (dl2->fcb->type == BTRFS_TYPE_DIRECTORY) {
- add_to_dir_list(dl2->fcb, level+1, &dl, dl2->newinode, &b);
+ if (dl2->fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+ add_to_dir_list(dl2->fileref, level+1, &dl, dl2->newinode, &b);
if (!b) empty = FALSE;
}
}
if (dl2->level == level) {
if (dl2->subvol) {
- TRACE("subvol %llx\n", dl2->fcb->subvol->id);
+ TRACE("subvol %llx\n", dl2->fileref->fcb->subvol->id);
- Status = move_subvol(Vcb, dl2->fcb, destsubvol, dl2->newparinode, &dl2->utf8, dl2->crc32, dl2->crc32, now, FALSE, rollback);
+ Status = move_subvol(Vcb, dl2->fileref, destsubvol, dl2->newparinode, &dl2->utf8, dl2->crc32, dl2->crc32, now, FALSE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_subvol returned %08x\n", Status);
return Status;
}
} else {
- TRACE("inode %llx\n", dl2->fcb->inode);
+ TRACE("inode %llx\n", dl2->fileref->fcb->inode);
- Status = move_inode_across_subvols(Vcb, dl2->fcb, destsubvol, dl2->newparinode, dl2->newinode, dl2->fcb->par->inode, &dl2->utf8, dl2->crc32, now, rollback);
+ Status = move_inode_across_subvols(Vcb, dl2->fileref, destsubvol, dl2->newparinode, dl2->newinode, dl2->fileref->parent->fcb->inode, &dl2->utf8, dl2->crc32, now, rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_inode_across_subvols returned %08x\n", Status);
return Status;
dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
ExFreePool(dl2->utf8.Buffer);
- free_fcb(dl2->fcb);
+ free_fileref(dl2->fileref);
ExFreePool(dl2);
}
}
- fcb->inode = inode;
- fcb->subvol = destsubvol;
+ fileref->fcb->inode = inode;
+ fileref->fcb->subvol = destsubvol;
- fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
- fcb->subvol->root_item.ctime = *now;
+ fileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fileref->fcb->subvol->root_item.ctime = *now;
+
+ RemoveEntryList(&fileref->fcb->list_entry);
+ InsertTailList(&fileref->fcb->subvol->fcbs, &fileref->fcb->list_entry);
+
+ if (fileref->fcb->debug_desc) {
+ ExFreePool(fileref->fcb->debug_desc);
+ fileref->fcb->debug_desc = NULL;
+ }
return STATUS_SUCCESS;
}
-static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) {
+NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
NTSTATUS Status;
if (!newrr) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
} else {
WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id);
+ return STATUS_NOT_FOUND;
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(rr2);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
} else {
if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, rollback)) {
ERR("error - failed to insert item\n");
ExFreePool(rr);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) {
+NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
UINT8* data;
datalen = 0;
}
- free_traverse_ptr(&tp);
-
searchkey.obj_id = subvolid;
searchkey.obj_type = TYPE_ROOT_BACKREF;
searchkey.offset = parsubvolid;
if (!keycmp(&tp.item->key, &searchkey))
delete_tree_item(Vcb, &tp, rollback);
- free_traverse_ptr(&tp);
-
if (datalen > 0) {
if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, rollback)) {
ERR("error - failed to insert item\n");
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL move_subvol(device_extension* Vcb, file_ref* fileref, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32,
+ UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback) {
DIR_ITEM* di;
NTSTATUS Status;
KEY searchkey;
// delete old DIR_ITEM
- Status = delete_dir_item(Vcb, fcb->par->subvol, fcb->par->inode, oldcrc32, &fcb->utf8, rollback);
+ Status = delete_dir_item(Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->utf8, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_dir_item returned %08x\n", Status);
return Status;
return STATUS_INSUFFICIENT_RESOURCES;
}
- di->key.obj_id = fcb->subvol->id;
+ di->key.obj_id = fileref->fcb->subvol->id;
di->key.obj_type = TYPE_ROOT_ITEM;
di->key.offset = 0;
di->transid = Vcb->superblock.generation;
di->m = 0;
di->n = utf8->Length;
- di->type = fcb->type;
+ di->type = fileref->fcb->type;
RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback);
oldindex = 0;
- Status = delete_root_ref(Vcb, fcb->subvol->id, fcb->par->subvol->id, fcb->par->inode, &fcb->utf8, &oldindex, rollback);
+ Status = delete_root_ref(Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->utf8, &oldindex, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_root_ref returned %08x\n", Status);
return Status;
// delete old DIR_INDEX
if (oldindex != 0) {
- searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_id = fileref->parent->fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = oldindex;
- Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
} else {
WARN("could not find old DIR_INDEX entry\n");
}
-
- free_traverse_ptr(&tp);
}
// create new DIR_INDEX
- if (fcb->par->subvol == destsubvol && fcb->par->inode == destinode) {
+ if (fileref->parent->fcb->subvol == destsubvol && fileref->parent->fcb->inode == destinode) {
index = oldindex;
} else {
index = find_next_dir_index(Vcb, destsubvol, destinode);
return STATUS_INSUFFICIENT_RESOURCES;
}
- di->key.obj_id = fcb->subvol->id;
+ di->key.obj_id = fileref->fcb->subvol->id;
di->key.obj_type = TYPE_ROOT_ITEM;
di->key.offset = 0;
di->transid = Vcb->superblock.generation;
di->m = 0;
di->n = utf8->Length;
- di->type = fcb->type;
+ di->type = fileref->fcb->type;
RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) {
rr->n = utf8->Length;
RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
- Status = add_root_ref(Vcb, fcb->subvol->id, destsubvol->id, rr, rollback);
+ Status = add_root_ref(Vcb, fileref->fcb->subvol->id, destsubvol->id, rr, rollback);
if (!NT_SUCCESS(Status)) {
ERR("add_root_ref returned %08x\n", Status);
return Status;
}
- Status = update_root_backref(Vcb, fcb->subvol->id, fcb->par->subvol->id, rollback);
+ Status = update_root_backref(Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_root_backref 1 returned %08x\n", Status);
return Status;
}
- if (fcb->par->subvol != destsubvol) {
- Status = update_root_backref(Vcb, fcb->subvol->id, destsubvol->id, rollback);
+ if (fileref->parent->fcb->subvol != destsubvol) {
+ Status = update_root_backref(Vcb, fileref->fcb->subvol->id, destsubvol->id, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_root_backref 1 returned %08x\n", Status);
return Status;
}
- fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
- fcb->par->subvol->root_item.ctime = *now;
+ fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fileref->parent->fcb->subvol->root_item.ctime = *now;
}
destsubvol->root_item.ctransid = Vcb->superblock.generation;
return STATUS_SUCCESS;
}
-static BOOL has_open_children(fcb* fcb) {
- LIST_ENTRY* le = fcb->children.Flink;
- struct _fcb* c;
+BOOL has_open_children(file_ref* fileref) {
+ LIST_ENTRY* le = fileref->children.Flink;
- while (le != &fcb->children) {
- c = CONTAINING_RECORD(le, struct _fcb, list_entry);
+ if (IsListEmpty(&fileref->children))
+ return FALSE;
- if (c->refcount > 0) {
- if (c->open_count > 0)
- return TRUE;
-
- if (has_open_children(c))
- return TRUE;
- }
+ while (le != &fileref->children) {
+ file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
+ if (c->fcb->open_count > 0)
+ return TRUE;
+
+ if (has_open_children(c))
+ return TRUE;
+
le = le->Flink;
}
static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists, LIST_ENTRY* rollback) {
FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
- fcb *fcb = FileObject->FsContext, *tfofcb, *oldparfcb, *oldfcb;
+ fcb *fcb = FileObject->FsContext, *tfofcb/*, *oldfcb*/;
+ file_ref *fileref, *oldfileref = NULL, *related;
+ ccb* ccb = FileObject->FsContext2;
root* parsubvol;
UINT64 parinode, dirpos;
WCHAR* fn;
BTRFS_TIME now;
BOOL across_directories;
INODE_ITEM* ii;
+ LONG i;
// FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
+ // FIXME - don't ignore fri->RootDirectory
TRACE(" tfo = %p\n", tfo);
TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists);
TRACE(" RootDirectory = %p\n", fri->RootDirectory);
TRACE(" FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
+ if (!ccb->fileref) {
+ ERR("tried to rename file with no fileref\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ fileref = ccb->fileref;
+
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
utf8.Buffer = NULL;
- if (!fcb->par) {
+ if (!fileref->parent) {
ERR("error - tried to rename file with no parent\n");
Status = STATUS_ACCESS_DENIED;
goto end;
fnlen = fri->FileNameLength / sizeof(WCHAR);
if (!tfo) {
- parsubvol = fcb->par->subvol;
- parinode = fcb->par->inode;
+ parsubvol = fileref->parent->fcb->subvol;
+ parinode = fileref->parent->fcb->inode;
tfofcb = NULL;
across_directories = FALSE;
} else {
- LONG i;
-
tfofcb = tfo->FsContext;
parsubvol = tfofcb->subvol;
parinode = tfofcb->inode;
}
}
- across_directories = parsubvol != fcb->par->subvol || parinode != fcb->par->inode;
+ across_directories = parsubvol != fileref->parent->fcb->subvol || parinode != fileref->parent->fcb->inode;
}
fnus.Buffer = fn;
TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
+ if (!is_file_name_valid(&fnus))
+ return STATUS_OBJECT_NAME_INVALID;
+
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
goto end;
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length);
// FIXME - set to crc32 if utf8 and oldutf8 are identical
- oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
+ oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, (ULONG)fileref->utf8.Length);
// TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
+
+ if (tfo && tfo->FsContext2) {
+ struct _ccb* relatedccb = tfo->FsContext2;
+
+ related = relatedccb->fileref;
+ } else
+ related = NULL;
- oldfcb = NULL;
-
- Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE);
+// Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE, NULL);
+ Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
if (NT_SUCCESS(Status)) {
- WARN("destination file %.*S already exists\n", oldfcb->full_filename.Length / sizeof(WCHAR), oldfcb->full_filename.Buffer);
+ WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
- if (fcb != oldfcb && !(oldfcb->open_count == 0 && oldfcb->deleted)) {
+ if (fileref != oldfileref && !(oldfileref->fcb->open_count == 0 && oldfileref->deleted)) {
if (!ReplaceIfExists) {
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
- } else if (oldfcb->open_count >= 1 && !oldfcb->deleted) {
+ } else if (oldfileref->fcb->open_count >= 1 && !oldfileref->deleted) {
WARN("trying to overwrite open file\n");
Status = STATUS_ACCESS_DENIED;
goto end;
}
- if (oldfcb->type == BTRFS_TYPE_DIRECTORY) {
+ if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
WARN("trying to overwrite directory\n");
Status = STATUS_ACCESS_DENIED;
goto end;
}
}
- if (has_open_children(fcb)) {
+ if (has_open_children(fileref)) {
WARN("trying to rename file with open children\n");
Status = STATUS_ACCESS_DENIED;
goto end;
}
- if (oldfcb) {
- Status = delete_fcb(oldfcb, NULL, rollback);
+ if (oldfileref) {
+ // FIXME - check we have permission to delete oldfileref
+ Status = delete_fileref(oldfileref, NULL, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("delete_fcb returned %08x\n", Status);
+ ERR("delete_fileref returned %08x\n", Status);
goto end;
}
}
if (fcb->inode == SUBVOL_ROOT_INODE) {
- UNICODE_STRING filename;
-
- filename.Buffer = fn;
- filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
-
- Status = move_subvol(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, oldcrc32, &now, ReplaceIfExists, rollback);
+ Status = move_subvol(Vcb, fileref, tfofcb->subvol, tfofcb->inode, &utf8, crc32, oldcrc32, &now, ReplaceIfExists, rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_subvol returned %08x\n", Status);
goto end;
}
} else if (parsubvol != fcb->subvol) {
- UNICODE_STRING filename;
-
- filename.Buffer = fn;
- filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
-
- Status = move_across_subvols(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now, rollback);
+ Status = move_across_subvols(Vcb, fileref, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now, rollback);
if (!NT_SUCCESS(Status)) {
ERR("move_across_subvols returned %08x\n", Status);
// delete old DIR_ITEM entry
- Status = delete_dir_item(Vcb, fcb->subvol, fcb->par->inode, oldcrc32, &fcb->utf8, rollback);
+ Status = delete_dir_item(Vcb, fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->utf8, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_dir_item returned %08x\n", Status);
return Status;
oldindex = 0;
- Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fcb->par->inode, &fcb->utf8, &oldindex, rollback);
+ Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fileref->parent->fcb->inode, &fileref->utf8, &oldindex, rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_inode_ref returned %08x\n", Status);
return Status;
// delete old DIR_INDEX entry
if (oldindex != 0) {
- searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_id = fileref->parent->fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = oldindex;
- Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
else {
WARN("couldn't find DIR_INDEX\n");
}
-
- free_traverse_ptr(&tp);
} else {
WARN("couldn't get index from INODE_REF\n");
}
// create new DIR_INDEX entry
- if (parsubvol != fcb->par->subvol || parinode != fcb->par->inode) {
+ if (parsubvol != fileref->parent->fcb->subvol || parinode != fileref->parent->fcb->inode) {
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_INDEX + 1;
searchkey.offset = 0;
}
if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = next_tp;
} else
break;
} while (tp.item->key.obj_id >= parinode && tp.item->key.obj_type >= TYPE_DIR_INDEX);
-
- free_traverse_ptr(&tp);
} else
dirpos = oldindex;
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
delete_tree_item(Vcb, &tp, rollback);
- free_traverse_ptr(&tp);
-
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
// update directory INODE_ITEMs
- fcb->par->inode_item.transid = Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
- fcb->par->inode_item.st_mtime = now;
+ fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
+ fileref->parent->fcb->inode_item.sequence++;
+ fileref->parent->fcb->inode_item.st_ctime = now;
+ fileref->parent->fcb->inode_item.st_mtime = now;
- TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
- if (!tfofcb || (fcb->par->inode == tfofcb->inode && fcb->par->subvol == tfofcb->subvol)) {
- fcb->par->inode_item.st_size += 2 * (utf8.Length - fcb->utf8.Length);
+ TRACE("fileref->parent->fcb->inode_item.st_size was %llx\n", fileref->parent->fcb->inode_item.st_size);
+ if (!tfofcb || (fileref->parent->fcb->inode == tfofcb->inode && fileref->parent->fcb->subvol == tfofcb->subvol)) {
+ fileref->parent->fcb->inode_item.st_size += 2 * (utf8.Length - fileref->utf8.Length);
} else {
- fcb->par->inode_item.st_size -= 2 * fcb->utf8.Length;
+ fileref->parent->fcb->inode_item.st_size -= 2 * fileref->utf8.Length;
TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb->inode_item.st_size);
tfofcb->inode_item.st_size += 2 * utf8.Length;
TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb->inode_item.st_size);
tfofcb->inode_item.st_ctime = now;
tfofcb->inode_item.st_mtime = now;
}
- TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
+ TRACE("fileref->parent->fcb->inode_item.st_size now %llx\n", fileref->parent->fcb->inode_item.st_size);
- if (oldfcb && oldfcb->par != fcb->par) {
- TRACE("oldfcb->par->inode_item.st_size was %llx\n", oldfcb->par->inode_item.st_size);
- oldfcb->par->inode_item.st_size -= 2 * oldfcb->utf8.Length;
- TRACE("oldfcb->par->inode_item.st_size now %llx\n", oldfcb->par->inode_item.st_size);
+ if (oldfileref && oldfileref->fcb && oldfileref->parent->fcb != fileref->parent->fcb) {
+ TRACE("oldfileref->parent->fcb->inode_item.st_size was %llx\n", oldfileref->parent->fcb->inode_item.st_size);
+ oldfileref->parent->fcb->inode_item.st_size -= 2 * oldfileref->utf8.Length;
+ TRACE("oldfileref->parent->fcb->inode_item.st_size now %llx\n", oldfileref->parent->fcb->inode_item.st_size);
}
- searchkey.obj_id = fcb->par->inode;
+ searchkey.obj_id = fileref->parent->fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
- Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
- if (!insert_tree_item(Vcb, fcb->par->subvol, fcb->par->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback))
+ if (!insert_tree_item(Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback))
WARN("insert_tree_item failed\n");
- free_traverse_ptr(&tp);
-
- if (tfofcb && (fcb->par->inode != tfofcb->inode || fcb->par->subvol != tfofcb->subvol)) {
+ if (tfofcb && (fileref->parent->fcb->inode != tfofcb->inode || fileref->parent->fcb->subvol != tfofcb->subvol)) {
searchkey.obj_id = tfofcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, tfofcb->subvol, tfofcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback))
WARN("insert_tree_item failed\n");
-
- free_traverse_ptr(&tp);
}
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
// FIXME - handle overwrite by rename here
- FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
- fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
- across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME, NULL);
+ send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME);
// FIXME - change full_filename and name_offset of open children
- if (fnlen != fcb->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fcb->filepart.Buffer, fcb->filepart.Length) != fcb->filepart.Length) {
- RtlFreeUnicodeString(&fcb->filepart);
- fcb->filepart.Length = fcb->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR));
- fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.Length, ALLOC_TAG);
+ if (fnlen != fileref->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fileref->filepart.Buffer, fileref->filepart.Length) != fileref->filepart.Length) {
+ ExFreePool(fileref->filepart.Buffer);
+ fileref->filepart.Length = fileref->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR));
+ fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
- if (!fcb->filepart.Buffer) {
+ if (!fileref->filepart.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- RtlCopyMemory(fcb->filepart.Buffer, fn, fcb->filepart.Length);
+ RtlCopyMemory(fileref->filepart.Buffer, fn, fileref->filepart.Length);
}
- if (tfo && tfofcb != fcb->par) {
- oldparfcb = fcb->par;
- fcb->par = tfofcb;
-
- fcb->par->refcount++;
-
- RemoveEntryList(&fcb->list_entry);
- InsertTailList(&fcb->par->children, &fcb->list_entry);
+ if (related && related != (file_ref*)fileref->parent) {
+ file_ref* oldpar = (file_ref*)fileref->parent;
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc;
+#endif
+ RemoveEntryList(&fileref->list_entry);
+
+ fileref->parent = (struct _file_ref*)related;
#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fcb %p: refcount now %i (%.*S)\n", fcb->par, fcb->par->refcount, fcb->par->full_filename.Length / sizeof(WCHAR), fcb->par->full_filename.Buffer);
+ rc = InterlockedIncrement(&related->refcount);
+ WARN("fileref %p: refcount now %i (%S)\n", fileref->parent, rc, file_desc_fileref((file_ref*)fileref->parent));
+#else
+ InterlockedIncrement(&related->refcount);
#endif
- free_fcb(oldparfcb);
+
+ InsertTailList(&related->children, &fileref->list_entry);
+
+ free_fileref(oldpar);
}
- ExFreePool(fcb->utf8.Buffer);
- fcb->utf8 = utf8;
+ ExFreePool(fileref->utf8.Buffer);
+ fileref->utf8 = utf8;
utf8.Buffer = NULL;
- // change fcb->full_filename
+ // change fileref->full_filename
- fcb->full_filename.MaximumLength = fcb->par->full_filename.Length + fcb->filepart.Length;
- if (fcb->par->par) fcb->full_filename.MaximumLength += sizeof(WCHAR);
- ExFreePool(fcb->full_filename.Buffer);
+ fileref->full_filename.MaximumLength = fileref->parent->full_filename.Length + fileref->filepart.Length;
+ if (fileref->parent->parent) fileref->full_filename.MaximumLength += sizeof(WCHAR);
+ ExFreePool(fileref->full_filename.Buffer);
- fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.MaximumLength, ALLOC_TAG);
- if (!fcb->full_filename.Buffer) {
+ fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.MaximumLength, ALLOC_TAG);
+ if (!fileref->full_filename.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
- fcb->full_filename.Length = fcb->par->full_filename.Length;
+ RtlCopyMemory(fileref->full_filename.Buffer, fileref->parent->full_filename.Buffer, fileref->parent->full_filename.Length);
+ fileref->full_filename.Length = fileref->parent->full_filename.Length;
- if (fcb->par->par) {
- fcb->full_filename.Buffer[fcb->full_filename.Length / sizeof(WCHAR)] = '\\';
- fcb->full_filename.Length += sizeof(WCHAR);
+ if (fileref->parent->parent) {
+ fileref->full_filename.Buffer[fileref->full_filename.Length / sizeof(WCHAR)] = '\\';
+ fileref->full_filename.Length += sizeof(WCHAR);
}
- fcb->name_offset = fcb->full_filename.Length / sizeof(WCHAR);
+ fileref->name_offset = fileref->full_filename.Length / sizeof(WCHAR);
+
+ RtlAppendUnicodeStringToString(&fileref->full_filename, &fileref->filepart);
- RtlAppendUnicodeStringToString(&fcb->full_filename, &fcb->filepart);
+ if (fileref->debug_desc) {
+ ExFreePool(fileref->debug_desc);
+ fileref->debug_desc = NULL;
+ }
- FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
- fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
- across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME, NULL);
+ send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME);
Status = STATUS_SUCCESS;
if (utf8.Buffer)
ExFreePool(utf8.Buffer);
- if (oldfcb)
- free_fcb(oldfcb);
+ if (oldfileref)
+ free_fileref(oldfileref);
return Status;
}
-static NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) {
- FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
- fcb* fcb = FileObject->FsContext;
+NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) {
LARGE_INTEGER time;
BTRFS_TIME now;
KEY searchkey;
UINT16 datalen;
NTSTATUS Status;
- TRACE("setting new end to %llx bytes (currently %x)\n", feofi->EndOfFile.QuadPart, fcb->adssize);
+ TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adssize);
+
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
- if (feofi->EndOfFile.QuadPart < fcb->adssize) {
+ if (end < fcb->adssize) {
if (advance_only)
return STATUS_SUCCESS;
- TRACE("truncating stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+ TRACE("truncating stream to %llx bytes\n", end);
- if (feofi->EndOfFile.QuadPart > 0) {
+ if (end > 0) {
if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
ERR("get_xattr failed\n");
return STATUS_INTERNAL_ERROR;
}
}
- Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, feofi->EndOfFile.QuadPart, rollback);
+ Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, end, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
return Status;
}
- fcb->adssize = feofi->EndOfFile.QuadPart;
+ fcb->adssize = end;
if (data)
ExFreePool(data);
- } else if (feofi->EndOfFile.QuadPart > fcb->adssize) {
+ } else if (end > fcb->adssize) {
UINT16 maxlen;
UINT8* data2;
- TRACE("extending stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
+ TRACE("extending stream to %llx bytes\n", end);
// find maximum length of xattr
maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
if (keycmp(&tp.item->key, &searchkey)) {
ERR("error - could not find key for xattr\n");
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
-
-#ifdef __REACTOS__
- // BUGBUG: FIXME!!
- ERR("BUGBUG: datalen is uninitialized! Set it to zero temporarily...\n");
- datalen = 0;
-#endif
-
- if (tp.item->size < datalen) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen);
- free_traverse_ptr(&tp);
+
+ 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
- free_traverse_ptr(&tp);
-
- if (feofi->EndOfFile.QuadPart > maxlen) {
- ERR("error - xattr too long (%llu > %u)\n", feofi->EndOfFile.QuadPart, maxlen);
+ if (end > maxlen) {
+ ERR("error - xattr too long (%llu > %u)\n", end, maxlen);
return STATUS_DISK_FULL;
}
-
- if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
- ERR("get_xattr failed\n");
- return STATUS_INTERNAL_ERROR;
- }
- data2 = ExAllocatePoolWithTag(PagedPool, feofi->EndOfFile.QuadPart, ALLOC_TAG);
+ data2 = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
if (!data2) {
ERR("out of memory\n");
ExFreePool(data);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(data2, data, datalen);
- ExFreePool(data);
+ if (data) {
+ RtlCopyMemory(data2, data, datalen);
+ ExFreePool(data);
+ }
- RtlZeroMemory(&data2[datalen], feofi->EndOfFile.QuadPart - datalen);
+ RtlZeroMemory(&data2[datalen], end - datalen);
- Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, feofi->EndOfFile.QuadPart, rollback);
+ Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, end, rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
return Status;
}
- fcb->adssize = feofi->EndOfFile.QuadPart;
+ fcb->adssize = end;
ExFreePool(data2);
}
- ccfs.AllocationSize = fcb->Header.AllocationSize;
- ccfs.FileSize = fcb->Header.FileSize;
- ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+ if (FileObject) {
+ ccfs.AllocationSize = fcb->Header.AllocationSize;
+ ccfs.FileSize = fcb->Header.FileSize;
+ ccfs.ValidDataLength = fcb->Header.ValidDataLength;
- CcSetFileSizes(FileObject, &ccfs);
+ CcSetFileSizes(FileObject, &ccfs);
+ }
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
- fcb->par->inode_item.transid = Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
+ fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
+ fileref->parent->fcb->inode_item.sequence++;
+ fileref->parent->fcb->inode_item.st_ctime = now;
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
+ RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
- free_traverse_ptr(&tp);
-
- fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
- fcb->par->subvol->root_item.ctime = now;
+ fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fileref->parent->fcb->subvol->root_item.ctime = now;
return STATUS_SUCCESS;
}
static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) {
FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
NTSTATUS Status;
LARGE_INTEGER time;
KEY searchkey;
INODE_ITEM* ii;
CC_FILE_SIZES ccfs;
- if (fcb->deleted)
+ if (fileref ? fileref->deleted : fcb->deleted)
return STATUS_FILE_CLOSED;
if (fcb->ads)
- return stream_set_end_of_file_information(Vcb, Irp, FileObject, advance_only, rollback);
+ return stream_set_end_of_file_information(Vcb, feofi->EndOfFile.QuadPart, fcb, fileref, FileObject, advance_only, rollback);
- TRACE("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("file: %S\n", file_desc(FileObject));
TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
- // FIXME - pass flag to say that new extents should be prealloc rather than sparse
- Status = extend_file(fcb, feofi->EndOfFile.QuadPart, rollback);
+ Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, TRUE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - extend_file failed\n");
return Status;
ccfs.ValidDataLength = fcb->Header.ValidDataLength;
CcSetFileSizes(FileObject, &ccfs);
- TRACE("setting FileSize for %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, ccfs.FileSize);
+ TRACE("setting FileSize for %S to %llx\n", file_desc(FileObject), ccfs.FileSize);
KeQuerySystemTime(&time);
RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
-#ifdef DEBUG_LONG_MESSAGES
- fcb* fcb = FileObject->FsContext;
- TRACE("setting the position on %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fpi->CurrentByteOffset.QuadPart);
-#endif
+ TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
// FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
return STATUS_SUCCESS;
}
-NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, LIST_ENTRY* rollback) {
+ FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
+ fcb *fcb = FileObject->FsContext, *tfofcb;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related;
+ root* parsubvol;
+ UINT64 parinode, dirpos;
+ WCHAR* fn;
+ ULONG fnlen, utf8len, disize;
+ UNICODE_STRING fnus;
+ ANSI_STRING utf8;
NTSTATUS Status;
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- device_extension* Vcb = DeviceObject->DeviceExtension;
- fcb* fcb = IrpSp->FileObject->FsContext;
- BOOL top_level;
- LIST_ENTRY rollback;
+ UINT32 crc32;
+ DIR_ITEM *di, *di2;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM *ii, *fcbii;
- InitializeListHead(&rollback);
+ // FIXME - check fli length
+ // FIXME - don't ignore fli->RootDirectory
- FsRtlEnterFileSystem();
+ TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
+ TRACE("RootDirectory = %p\n", fli->RootDirectory);
+ TRACE("FileNameLength = %x\n", fli->FileNameLength);
+ TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
- top_level = is_top_level(Irp);
+ fn = fli->FileName;
+ fnlen = fli->FileNameLength / sizeof(WCHAR);
- if (Vcb->readonly) {
- Status = STATUS_MEDIA_WRITE_PROTECTED;
+ if (!tfo) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref set and no directory given\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ parsubvol = fileref->parent->fcb->subvol;
+ parinode = fileref->parent->fcb->inode;
+ tfofcb = NULL;
+ } else {
+ LONG i;
+
+ tfofcb = tfo->FsContext;
+ parsubvol = tfofcb->subvol;
+ parinode = tfofcb->inode;
+
+ for (i = fnlen - 1; i >= 0; i--) {
+ if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
+ fn = &fli->FileName[i+1];
+ fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
+ break;
+ }
+ }
+ }
+
+ utf8.Buffer = NULL;
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ WARN("tried to create hard link on directory\n");
+ Status = STATUS_FILE_IS_A_DIRECTORY;
goto end;
}
- if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
- Status = STATUS_ACCESS_DENIED;
+ if (fcb->ads) {
+ WARN("tried to create hard link on stream\n");
+ Status = STATUS_INVALID_PARAMETER;
goto end;
}
-
- Irp->IoStatus.Information = 0;
- Status = STATUS_NOT_IMPLEMENTED;
-
- TRACE("set information\n");
+ fnus.Buffer = fn;
+ fnus.Length = fnus.MaximumLength = fnlen * sizeof(WCHAR);
- acquire_tree_lock(Vcb, TRUE);
-
- switch (IrpSp->Parameters.SetFile.FileInformationClass) {
- case FileAllocationInformation:
- TRACE("FileAllocationInformation\n");
- Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, &rollback);
- break;
-
- case FileBasicInformation:
- TRACE("FileBasicInformation\n");
- Status = set_basic_information(Vcb, Irp, IrpSp->FileObject, &rollback);
- break;
-
- case FileDispositionInformation:
- TRACE("FileDispositionInformation\n");
- Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
- break;
-
- case FileEndOfFileInformation:
- TRACE("FileEndOfFileInformation\n");
- Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, &rollback);
- break;
-
- case FileLinkInformation:
- FIXME("STUB: FileLinkInformation\n");
- break;
-
- case FilePositionInformation:
- TRACE("FilePositionInformation\n");
- Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
- break;
+ TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ utf8.MaximumLength = utf8.Length = utf8len;
+ utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
+ if (!utf8.Buffer) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length);
+
+ if (tfo && tfo->FsContext2) {
+ struct _ccb* relatedccb = tfo->FsContext2;
+
+ related = relatedccb->fileref;
+ } else
+ related = NULL;
- case FileRenameInformation:
- TRACE("FileRenameInformation\n");
- // FIXME - make this work with streams
- Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists, &rollback);
- break;
+ Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
- case FileValidDataLengthInformation:
- FIXME("STUB: FileValidDataLengthInformation\n");
- break;
-
-#if (NTDDI_VERSION >= NTDDI_VISTA)
- case FileNormalizedNameInformation:
- FIXME("STUB: FileNormalizedNameInformation\n");
- break;
-#endif
+ if (NT_SUCCESS(Status)) {
+ WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
+
+ if (fileref != oldfileref && !(oldfileref->fcb->open_count == 0 && oldfileref->deleted)) {
+ if (!fli->ReplaceIfExists) {
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end;
+ } else if (oldfileref->fcb->open_count >= 1 && !oldfileref->deleted) {
+ WARN("trying to overwrite open file\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+ WARN("trying to overwrite directory\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+ }
+ }
+
+ if (fcb->subvol != parsubvol) {
+ WARN("can't create hard link over subvolume boundary\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (oldfileref) {
+ // FIXME - check we have permissions for this
+
+ Status = delete_fileref(oldfileref, NULL, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_fcb returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ // add DIR_ITEM
+
+ disize = sizeof(DIR_ITEM) - 1 + utf8len;
+
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di2) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ ExFreePool(di);
+ goto end;
+ }
+
+ di->key.obj_id = fcb->inode;
+ di->key.obj_type = TYPE_INODE_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8len;
+ di->type = fcb->type;
+ RtlCopyMemory(di->name, utf8.Buffer, di->n);
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(Vcb, fcb->subvol, parinode, crc32, di, disize, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ ExFreePool(di);
+ ExFreePool(di2);
+ goto end;
+ }
+
+ // add DIR_INDEX
+
+ dirpos = find_next_dir_index(Vcb, fcb->subvol, parinode);
+ if (dirpos == 0) {
+ ERR("find_next_dir_index failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(di2);
+ goto end;
+ }
+
+ if (!insert_tree_item(Vcb, fcb->subvol, parinode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(di2);
+ goto end;
+ }
+
+ // add INODE_REF
+
+ Status = add_inode_ref(Vcb, fcb->subvol, fcb->inode, parinode, dirpos, &utf8, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_inode_ref returned %08x\n", Status);
+ goto end;
+ }
+
+ // update inode's INODE_ITEM
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_nlink++;
+ fcb->inode_item.st_ctime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp, rollback);
+ searchkey.offset = tp.item->key.offset;
+ } else
+ searchkey.offset = 0;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ goto end;
+ }
+
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+
+ if (!insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // update parent's INODE_ITEM
+
+ searchkey.obj_id = parinode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp, rollback);
+ searchkey.offset = tp.item->key.offset;
+ } else {
+ ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", parinode, fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (tfofcb)
+ fcbii = &tfofcb->inode_item;
+ else {
+ if (tp.item->size < sizeof(INODE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ fcbii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!fcbii) {
+ ERR("out of memory\n");
+ goto end;
+ }
+
+ RtlCopyMemory(fcbii, tp.item->data, sizeof(INODE_ITEM));
+ }
+
+ fcbii->transid = Vcb->superblock.generation;
+ fcbii->st_size += 2 * utf8len;
+ fcbii->sequence++;
+ fcbii->st_ctime = now;
+
+ if (tfofcb) {
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ goto end;
+ }
+
+ RtlCopyMemory(ii, fcbii, sizeof(INODE_ITEM));
+ } else
+ ii = fcbii;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ ExFreePool(ii);
+ goto end;
+ }
+
+ // FIXME - notification
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (utf8.Buffer)
+ ExFreePool(utf8.Buffer);
+
+ if (oldfileref)
+ free_fileref(oldfileref);
+
+ return Status;
+}
+
+NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb = IrpSp->FileObject->FsContext;
+ BOOL top_level;
+ LIST_ENTRY rollback;
+
+ InitializeListHead(&rollback);
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
+ }
+
+ if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ TRACE("set information\n");
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ switch (IrpSp->Parameters.SetFile.FileInformationClass) {
+ case FileAllocationInformation:
+ TRACE("FileAllocationInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, &rollback);
+ break;
+
+ case FileBasicInformation:
+ TRACE("FileBasicInformation\n");
+ Status = set_basic_information(Vcb, Irp, IrpSp->FileObject, &rollback);
+ break;
+
+ case FileDispositionInformation:
+ TRACE("FileDispositionInformation\n");
+ Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileEndOfFileInformation:
+ TRACE("FileEndOfFileInformation\n");
+ Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, &rollback);
+ break;
+
+ case FileLinkInformation:
+ TRACE("FileLinkInformation\n");
+ Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, &rollback);
+ break;
+
+ case FilePositionInformation:
+ TRACE("FilePositionInformation\n");
+ Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
+ break;
+
+ case FileRenameInformation:
+ TRACE("FileRenameInformation\n");
+ // FIXME - make this work with streams
+ Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists, &rollback);
+ break;
+ case FileValidDataLengthInformation:
+ FIXME("STUB: FileValidDataLengthInformation\n");
+ break;
+
+#if (NTDDI_VERSION >= NTDDI_VISTA)
+ case FileNormalizedNameInformation:
+ FIXME("STUB: FileNormalizedNameInformation\n");
+ break;
+#endif
#if (NTDDI_VERSION >= NTDDI_WIN7)
case FileStandardLinkInformation:
return Status;
}
-static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb) {
+static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
*length -= sizeof(FILE_BASIC_INFORMATION);
fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
fbi->ChangeTime.QuadPart = 0;
- fbi->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
+
+ if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ } else
+ fbi->FileAttributes = fileref->parent->fcb->atts;
+ } else
+ fbi->FileAttributes = fcb->atts;
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
INODE_ITEM* ii;
if (*length < sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
*length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
- if (fcb->ads)
- ii = &fcb->par->inode_item;
- else
+ if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ii = &fileref->parent->fcb->inode_item;
+ } else
ii = &fcb->inode_item;
-
fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
if (fcb->ads) {
fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adssize;
- fnoi->FileAttributes = fcb->par->atts;
+ fnoi->FileAttributes = fileref->parent->fcb->atts;
} else {
fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
*length -= sizeof(FILE_STANDARD_INFORMATION);
if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adssize;
- fsi->NumberOfLinks = fcb->par->inode_item.st_nlink;
- fsi->Directory = S_ISDIR(fcb->par->inode_item.st_mode);
+ fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
+ fsi->Directory = S_ISDIR(fileref->parent->fcb->inode_item.st_mode);
} else {
fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
- fsi->DeletePending = fcb->delete_on_close;
+ fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE;
return STATUS_SUCCESS;
}
static NTSTATUS STDCALL fill_in_file_ea_information(FILE_EA_INFORMATION* eai, LONG* length) {
*length -= sizeof(FILE_EA_INFORMATION);
+ // FIXME - should this be the reparse tag for symlinks?
eai->EaSize = 0;
return STATUS_SUCCESS;
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
#ifdef _DEBUG
ULONG retlen = 0;
#endif
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
ULONG datasuflen = wcslen(datasuf) * sizeof(WCHAR);
+ if (!fileref) {
+ ERR("called without fileref\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION));
*length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
fni->FileName[0] = 0;
- if (*length >= (LONG)fcb->full_filename.Length) {
- RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, fcb->full_filename.Length);
+ if (*length >= (LONG)fileref->full_filename.Length) {
+ RtlCopyMemory(fni->FileName, fileref->full_filename.Buffer, fileref->full_filename.Length);
#ifdef _DEBUG
- retlen = fcb->full_filename.Length;
+ retlen = fileref->full_filename.Length;
#endif
- *length -= fcb->full_filename.Length;
+ *length -= fileref->full_filename.Length;
} else {
if (*length > 0) {
- RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, *length);
+ RtlCopyMemory(fni->FileName, fileref->full_filename.Buffer, *length);
#ifdef _DEBUG
retlen = *length;
#endif
*length = -1;
}
- fni->FileNameLength = fcb->full_filename.Length;
+ fni->FileNameLength = fileref->full_filename.Length;
if (fcb->ads) {
if (*length >= (LONG)datasuflen) {
- RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen);
+ RtlCopyMemory(&fni->FileName[fileref->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen);
#ifdef _DEBUG
retlen += datasuflen;
#endif
*length -= datasuflen;
} else {
if (*length > 0) {
- RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, *length);
+ RtlCopyMemory(&fni->FileName[fileref->full_filename.Length / sizeof(WCHAR)], datasuf, *length);
#ifdef _DEBUG
retlen += *length;
#endif
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, LONG* length) {
*length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
- ati->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
- ati->ReparseTag = 0;
+ if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ati->FileAttributes = fileref->parent->fcb->atts;
+ } else
+ ati->FileAttributes = fcb->atts;
+
+ ati->ReparseTag = 0; // FIXME
return STATUS_SUCCESS;
}
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
}
} while (b);
- free_traverse_ptr(&tp);
-
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
}
} while (b);
- free_traverse_ptr(&tp);
-
TRACE("length = %i, reqsize = %u\n", *length, reqsize);
if (reqsize > *length) {
return Status;
}
-static NTSTATUS STDCALL fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
TRACE("FileStandardLinkInformation\n");
// FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
- fsli->DeletePending = fcb->delete_on_close;
+ fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
fsli->Directory = fcb->type == BTRFS_TYPE_DIRECTORY ? TRUE : FALSE;
*length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
return STATUS_SUCCESS;
}
+typedef struct {
+ UNICODE_STRING name;
+ UINT64 inode;
+ LIST_ENTRY list_entry;
+} name_bit;
+
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us);
+
+static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
+ KEY searchkey;
+ traverse_ptr tp;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ root* parsubvol;
+ UNICODE_STRING dirpath;
+ ROOT_REF* rr;
+ ULONG namelen;
+
+ // FIXME - add subvol->parent field
+
+ if (subvol == Vcb->root_fileref->fcb->subvol) {
+ subvol->path.Length = subvol->path.MaximumLength = sizeof(WCHAR);
+ subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
+ subvol->path.Buffer[0] = '\\';
+ return STATUS_SUCCESS;
+ }
+
+ searchkey.obj_id = subvol->id;
+ searchkey.obj_type = TYPE_ROOT_BACKREF;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { // top subvol
+ subvol->path.Length = subvol->path.MaximumLength = sizeof(WCHAR);
+ subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
+ subvol->path.Buffer[0] = '\\';
+ return STATUS_SUCCESS;
+ }
+
+ if (tp.item->size < sizeof(ROOT_REF)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ rr = (ROOT_REF*)tp.item->data;
+
+ if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF) - 1 + rr->n);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ le = Vcb->roots.Flink;
+
+ parsubvol = NULL;
+
+ while (le != &Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r2->id == tp.item->key.offset) {
+ parsubvol = r2;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!parsubvol) {
+ ERR("unable to find subvol %llx\n", tp.item->key.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // FIXME - recursion
+
+ Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_inode_dir_path returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &namelen, rr->name, rr->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ if (namelen == 0) {
+ ERR("length was 0\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ subvol->path.Length = subvol->path.MaximumLength = dirpath.Length + namelen;
+ subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
+
+ if (!subvol->path.Buffer) {
+ ERR("out of memory\n");
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(subvol->path.Buffer, dirpath.Buffer, dirpath.Length);
+
+ Status = RtlUTF8ToUnicodeN(&subvol->path.Buffer[dirpath.Length / sizeof(WCHAR)], namelen, &namelen, rr->name, rr->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (dirpath.Buffer)
+ ExFreePool(dirpath.Buffer);
+
+ if (!NT_SUCCESS(Status) && subvol->path.Buffer) {
+ ExFreePool(subvol->path.Buffer);
+ subvol->path.Buffer = NULL;
+ }
+
+ return Status;
+}
+
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us) {
+ KEY searchkey;
+ NTSTATUS Status;
+ UINT64 in;
+ traverse_ptr tp;
+ LIST_ENTRY name_trail, *le;
+ UINT16 levels = 0;
+ UINT32 namelen = 0;
+ WCHAR* usbuf;
+
+ InitializeListHead(&name_trail);
+
+ in = inode;
+
+ // FIXME - start with subvol prefix
+ if (!subvol->path.Buffer) {
+ Status = get_subvol_path(Vcb, subvol);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_subvol_path returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ while (in != subvol->root_item.objid) {
+ searchkey.obj_id = in;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id) {
+ ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (tp.item->key.obj_type == TYPE_INODE_REF) {
+ INODE_REF* ir = (INODE_REF*)tp.item->data;
+ name_bit* nb;
+ ULONG len;
+
+ if (tp.item->size < sizeof(INODE_REF)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ if (tp.item->size < sizeof(INODE_REF) - 1 + ir->n) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF) - 1 + ir->n);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG);
+ if (!nb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ nb->name.Buffer = NULL;
+
+ InsertTailList(&name_trail, &nb->list_entry);
+ levels++;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &len, ir->name, ir->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ if (len == 0) {
+ ERR("length was 0\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ nb->name.Length = nb->name.MaximumLength = len;
+
+ nb->name.Buffer = ExAllocatePoolWithTag(PagedPool, nb->name.Length, ALLOC_TAG);
+ if (!nb->name.Buffer) {
+ ERR("out of memory\n");
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ Status = RtlUTF8ToUnicodeN(nb->name.Buffer, len, &len, ir->name, ir->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ in = tp.item->key.offset;
+ namelen += nb->name.Length;
+
+// } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+// // FIXME
+ } else {
+ ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ }
+
+ namelen += (levels + 1) * sizeof(WCHAR);
+
+ us->Length = us->MaximumLength = namelen;
+ us->Buffer = ExAllocatePoolWithTag(PagedPool, us->Length, ALLOC_TAG);
+
+ if (!us->Buffer) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ us->Buffer[0] = '\\';
+ usbuf = &us->Buffer[1];
+
+ le = name_trail.Blink;
+ while (le != &name_trail) {
+ name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
+
+ RtlCopyMemory(usbuf, nb->name.Buffer, nb->name.Length);
+ usbuf += nb->name.Length / sizeof(WCHAR);
+
+ usbuf[0] = '\\';
+ usbuf++;
+
+ le = le->Blink;
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ while (!IsListEmpty(&name_trail)) {
+ name_bit* nb = CONTAINING_RECORD(name_trail.Flink, name_bit, list_entry);
+
+ if (nb->name.Buffer)
+ ExFreePool(nb->name.Buffer);
+
+ RemoveEntryList(&nb->list_entry);
+
+ ExFreePool(nb);
+ }
+
+ return Status;
+}
+
+static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, fcb* fcb, LONG* length) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+ LIST_ENTRY hardlinks, *le;
+ ULONG bytes_needed, num_entries = 0;
+ FILE_LINK_ENTRY_INFORMATION* feli;
+
+ if (fcb->ads)
+ return STATUS_INVALID_PARAMETER;
+
+ if (*length < offsetof(FILE_LINKS_INFORMATION, Entry))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlZeroMemory(fli, *length);
+
+ InitializeListHead(&hardlinks);
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = 0;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode) {
+ if (tp.item->key.obj_type == TYPE_INODE_REF) {
+ ULONG len = tp.item->size;
+ INODE_REF* ir = (INODE_REF*)tp.item->data;
+
+ if (tp.item->size >= sizeof(INODE_REF)) {
+ UNICODE_STRING dirpath;
+
+ Status = get_inode_dir_path(fcb->Vcb, fcb->subvol, tp.item->key.offset, &dirpath);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_inode_dir_path returned %08x\n", Status);
+ goto end;
+ }
+
+ if (fcb->inode == fcb->subvol->root_item.objid) {
+ name_bit* nb;
+
+ nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG);
+ if (!nb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ nb->inode = tp.item->key.offset;
+ nb->name = dirpath;
+
+ InsertTailList(&hardlinks, &nb->list_entry);
+ } else {
+ while (len >= sizeof(INODE_REF) && len >= sizeof(INODE_REF) - 1 + ir->n) {
+ name_bit* nb;
+ ULONG namelen;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &namelen, ir->name, ir->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ if (namelen == 0) {
+ ERR("length was 0\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG);
+ if (!nb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ nb->inode = tp.item->key.offset;
+ nb->name.Buffer = NULL;
+
+ InsertTailList(&hardlinks, &nb->list_entry);
+
+ nb->name.Length = nb->name.MaximumLength = namelen + dirpath.Length;
+
+ nb->name.Buffer = ExAllocatePoolWithTag(PagedPool, nb->name.Length, ALLOC_TAG);
+ if (!nb->name.Buffer) {
+ ERR("out of memory\n");
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(nb->name.Buffer, dirpath.Buffer, dirpath.Length);
+
+ Status = RtlUTF8ToUnicodeN(&nb->name.Buffer[dirpath.Length / sizeof(WCHAR)], namelen, &namelen, ir->name, ir->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ goto end;
+ }
+
+ ERR("nb->name = %.*S\n", nb->name.Length / sizeof(WCHAR), nb->name.Buffer);
+
+ if (num_entries > 0)
+ bytes_needed = sector_align(bytes_needed, 8);
+
+ num_entries++;
+ bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length);
+
+ len -= sizeof(INODE_REF) - 1 + ir->n;
+ ir = (INODE_REF*)&ir->name[ir->n];
+ }
+
+ ExFreePool(dirpath.Buffer);
+ }
+ }
+ } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+ // FIXME
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ tp = next_tp;
+
+ if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_INODE_EXTREF)
+ break;
+ }
+ } while (b);
+
+ bytes_needed = sector_align(bytes_needed, 8);
+
+ fli->BytesNeeded = bytes_needed;
+ fli->EntriesReturned = 0;
+
+ *length -= offsetof(FILE_LINKS_INFORMATION, Entry);
+ feli = NULL;
+
+ le = hardlinks.Flink;
+ while (le != &hardlinks) {
+ name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
+
+ if (sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length), 8) > *length) {
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ if (feli) {
+ feli->NextEntryOffset = sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
+ feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset);
+ } else
+ feli = &fli->Entry;
+
+ feli->NextEntryOffset = 0;
+ feli->ParentFileId = nb->inode;
+ feli->FileNameLength = nb->name.Length / sizeof(WCHAR);
+ RtlCopyMemory(feli->FileName, nb->name.Buffer, nb->name.Length);
+
+ *length -= sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + max(sizeof(WCHAR), nb->name.Length), 8);
+ fli->EntriesReturned++;
+
+ le = le->Flink;
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ while (!IsListEmpty(&hardlinks)) {
+ name_bit* nb = CONTAINING_RECORD(hardlinks.Flink, name_bit, list_entry);
+
+ if (nb->name.Buffer)
+ ExFreePool(nb->name.Buffer);
+
+ RemoveEntryList(&nb->list_entry);
+
+ ExFreePool(nb);
+ }
+ return Status;
+}
+
static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
LONG length = IrpSp->Parameters.QueryFile.Length;
fcb* fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
NTSTATUS Status;
TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
TRACE("FileAllInformation\n");
- if (fcb->ads)
- ii = &fcb->par->inode_item;
- else
+ if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ ii = &fileref->parent->fcb->inode_item;
+ } else
ii = &fcb->inode_item;
if (length > 0)
- fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb);
+ fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
if (length > 0)
- fill_in_file_standard_information(&fai->StandardInformation, fcb, &length);
+ fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
if (length > 0)
fill_in_file_internal_information(&fai->InternalInformation, fcb->inode, &length);
fill_in_file_alignment_information(&fai->AlignmentInformation, Vcb, &length);
if (length > 0)
- fill_in_file_name_information(&fai->NameInformation, fcb, &length);
+ fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
Status = STATUS_SUCCESS;
TRACE("FileAttributeTagInformation\n");
- Status = fill_in_file_attribute_information(ati, fcb, &length);
+ Status = fill_in_file_attribute_information(ati, fcb, fileref, &length);
break;
}
goto exit;
}
- if (fcb->ads)
- ii = &fcb->par->inode_item;
- else
+ if (fcb->ads) {
+ if (!fileref || !fileref->parent) {
+ ERR("no fileref for stream\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ ii = &fileref->parent->fcb->inode_item;
+ } else
ii = &fcb->inode_item;
- Status = fill_in_file_basic_information(fbi, ii, &length, fcb);
+ Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
break;
}
TRACE("FileNameInformation\n");
- Status = fill_in_file_name_information(fni, fcb, &length);
+ Status = fill_in_file_name_information(fni, fcb, fileref, &length);
break;
}
TRACE("FileNetworkOpenInformation\n");
- Status = fill_in_file_network_open_information(fnoi, fcb, &length);
+ Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
break;
}
goto exit;
}
- Status = fill_in_file_standard_information(fsi, fcb, &length);
+ Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
break;
}
#if (NTDDI_VERSION >= NTDDI_VISTA)
case FileHardLinkInformation:
- FIXME("STUB: FileHardLinkInformation\n");
- Status = STATUS_INVALID_PARAMETER;
- goto exit;
+ {
+ FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileHardLinkInformation\n");
+
+ Status = fill_in_hard_link_information(fli, fcb, &length);
+
+ break;
+ }
case FileNormalizedNameInformation:
{
TRACE("FileNormalizedNameInformation\n");
- Status = fill_in_file_name_information(fni, fcb, &length);
+ Status = fill_in_file_name_information(fni, fcb, fileref, &length);
break;
}
TRACE("FileStandardLinkInformation\n");
- Status = fill_in_file_standard_link_information(fsli, fcb, &length);
+ Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
break;
}
if (Vcb->write_trees > 0)
do_write(Vcb, &rollback);
- free_tree_cache(&Vcb->tree_cache);
+ free_trees(Vcb);
clear_rollback(&rollback);
}
void STDCALL flush_thread(void* context) {
- device_extension* Vcb = context;
+ DEVICE_OBJECT* devobj = context;
+ device_extension* Vcb = devobj->DeviceExtension;
LARGE_INTEGER due_time;
+ KTIMER flush_thread_timer;
- KeInitializeTimer(&Vcb->flush_thread_timer);
+ ObReferenceObject(devobj);
+
+ KeInitializeTimer(&flush_thread_timer);
due_time.QuadPart = -INTERVAL * 10000;
- KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+ KeSetTimer(&flush_thread_timer, due_time, NULL);
while (TRUE) {
- KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
+ KeWaitForSingleObject(&flush_thread_timer, Executive, KernelMode, FALSE, NULL);
+ if (!(devobj->Vpb->Flags & VPB_MOUNTED))
+ break;
+
do_flush(Vcb);
- KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
+ KeSetTimer(&flush_thread_timer, due_time, NULL);
}
- KeCancelTimer(&Vcb->flush_thread_timer);
+ ObDereferenceObject(devobj);
+ KeCancelTimer(&flush_thread_timer);
PsTerminateSystemThread(STATUS_SUCCESS);
}
--- /dev/null
+/* Copyright (c) Mark Harmstone 2016
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "btrfs_drv.h"
+
+// Number of increments in the size of each cache inode, in sectors. Should
+// this be a constant number of sectors, a constant 256 KB, or what?
+#define CACHE_INCREMENTS 64
+
+static NTSTATUS remove_free_space_inode(device_extension* Vcb, KEY* key, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, key, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(key, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", key->obj_id, key->obj_type, key->offset);
+ return STATUS_NOT_FOUND;
+ }
+
+ if (tp.item->size < offsetof(INODE_ITEM, st_blocks)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(INODE_ITEM, st_blocks));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ii = (INODE_ITEM*)tp.item->data;
+
+ Status = excise_extents_inode(Vcb, Vcb->root_root, key->obj_id, NULL, 0, ii->st_size, NULL, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ return Status;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS clear_free_space_cache(device_extension* Vcb) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+ LIST_ENTRY rollback;
+
+ InitializeListHead(&rollback);
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ do {
+ if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
+ break;
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ delete_tree_item(Vcb, &tp, &rollback);
+
+ if (tp.item->size >= sizeof(FREE_SPACE_ITEM)) {
+ FREE_SPACE_ITEM* fsi = (FREE_SPACE_ITEM*)tp.item->data;
+
+ if (fsi->key.obj_type != TYPE_INODE_ITEM)
+ WARN("key (%llx,%x,%llx) does not point to an INODE_ITEM\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset);
+ else {
+ Status = remove_free_space_inode(Vcb, &fsi->key, &rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_free_space_inode for (%llx,%x,%llx) returned %08x\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset, Status);
+ goto end;
+ }
+ }
+ } else
+ WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b)
+ tp = next_tp;
+ } while (b);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (NT_SUCCESS(Status))
+ clear_rollback(&rollback);
+ else
+ do_rollback(Vcb, &rollback);
+
+ return Status;
+}
+
+static NTSTATUS add_space_entry(chunk* c, UINT64 offset, UINT64 size) {
+ space* s;
+
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+
+ if (!s) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ s->offset = offset;
+ s->size = size;
+ s->type = SPACE_TYPE_FREE;
+
+ if (IsListEmpty(&c->space))
+ InsertTailList(&c->space, &s->list_entry);
+ else {
+ space* s2 = CONTAINING_RECORD(c->space.Blink, space, list_entry);
+
+ if (s2->offset < offset)
+ InsertTailList(&c->space, &s->list_entry);
+ else {
+ LIST_ENTRY* le;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s2 = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s2->offset > offset) {
+ InsertTailList(le, &s->list_entry);
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void load_free_space_bitmap(device_extension* Vcb, chunk* c, UINT64 offset, void* data) {
+ RTL_BITMAP bmph;
+ UINT32 i, *dwords = data;
+ ULONG runlength, index;
+
+ // flip bits
+ for (i = 0; i < Vcb->superblock.sector_size / sizeof(UINT32); i++) {
+ dwords[i] = ~dwords[i];
+ }
+
+ RtlInitializeBitMap(&bmph, data, Vcb->superblock.sector_size * 8);
+
+ index = 0;
+ runlength = RtlFindFirstRunClear(&bmph, &index);
+
+ while (runlength != 0) {
+ UINT64 addr, length;
+
+ addr = offset + (index * Vcb->superblock.sector_size);
+ length = Vcb->superblock.sector_size * runlength;
+
+ add_space_entry(c, addr, length);
+ index += runlength;
+
+ runlength = RtlFindNextForwardRunClear(&bmph, index, &index);
+ }
+}
+
+static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+ KEY searchkey;
+ traverse_ptr tp, tp2;
+ FREE_SPACE_ITEM* fsi;
+ UINT64 inode, num_sectors, num_valid_sectors, i, *generation;
+ INODE_ITEM* ii;
+ UINT8* data;
+ NTSTATUS Status;
+ UINT32 *checksums, crc32;
+ FREE_SPACE_ENTRY* fse;
+ UINT64 size, num_entries, num_bitmaps, extent_length, bmpnum, off;
+ LIST_ENTRY* le;
+
+ // FIXME - does this break if Vcb->superblock.sector_size is not 4096?
+
+ TRACE("(%p, %llx)\n", Vcb, c->offset);
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&tp.item->key, &searchkey)) {
+ TRACE("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_NOT_FOUND;
+ }
+
+ if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
+ 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));
+ return STATUS_NOT_FOUND;
+ }
+
+ fsi = (FREE_SPACE_ITEM*)tp.item->data;
+
+ if (fsi->key.obj_type != TYPE_INODE_ITEM) {
+ WARN("cache pointed to something other than an INODE_ITEM\n");
+ return STATUS_NOT_FOUND;
+ }
+
+ inode = fsi->key.obj_id;
+
+ searchkey = fsi->key;
+
+ num_entries = fsi->num_entries;
+ num_bitmaps = fsi->num_bitmaps;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_NOT_FOUND;
+ }
+
+ if (tp2.item->size < sizeof(INODE_ITEM)) {
+ WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, sizeof(INODE_ITEM));
+ return STATUS_NOT_FOUND;
+ }
+
+ ii = (INODE_ITEM*)tp2.item->data;
+
+ if (ii->st_size == 0) {
+ ERR("inode %llx had a length of 0\n", inode);
+ return STATUS_NOT_FOUND;
+ }
+
+ c->cache_size = ii->st_size;
+ c->cache_inode = fsi->key.obj_id;
+
+ size = sector_align(ii->st_size, Vcb->superblock.sector_size);
+
+ data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+
+ if (!data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+
+ if (size > ii->st_size)
+ RtlZeroMemory(&data[ii->st_size], size - ii->st_size);
+
+ num_sectors = size / Vcb->superblock.sector_size;
+
+ generation = (UINT64*)(data + (num_sectors * sizeof(UINT32)));
+
+ if (*generation != fsi->generation) {
+ WARN("free space cache generation for %llx was %llx, expected %llx\n", c->offset, generation, fsi->generation);
+ ExFreePool(data);
+ return STATUS_NOT_FOUND;
+ }
+
+ extent_length = (num_sectors * sizeof(UINT32)) + sizeof(UINT64) + (num_entries * sizeof(FREE_SPACE_ENTRY));
+
+ num_valid_sectors = (sector_align(extent_length, Vcb->superblock.sector_size) / Vcb->superblock.sector_size) + num_bitmaps;
+
+ if (num_valid_sectors > num_sectors) {
+ ERR("free space cache for %llx was %llx sectors, expected at least %llx\n", c->offset, num_sectors, num_valid_sectors);
+ ExFreePool(data);
+ return STATUS_NOT_FOUND;
+ }
+
+ checksums = (UINT32*)data;
+
+ for (i = 0; i < num_valid_sectors; i++) {
+ if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
+ crc32 = ~calc_crc32c(0xffffffff, &data[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+ else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
+ crc32 = 0; // FIXME - test this
+ else
+ crc32 = ~calc_crc32c(0xffffffff, &data[sizeof(UINT32) * num_sectors], ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
+
+ if (crc32 != checksums[i]) {
+ WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]);
+ ExFreePool(data);
+ return STATUS_NOT_FOUND;
+ }
+ }
+
+ off = (sizeof(UINT32) * num_sectors) + sizeof(UINT64);
+
+ bmpnum = 0;
+ for (i = 0; i < num_entries; i++) {
+ if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size)
+ off = sector_align(off, Vcb->superblock.sector_size);
+
+ fse = (FREE_SPACE_ENTRY*)&data[off];
+
+ if (fse->type == FREE_SPACE_EXTENT) {
+ Status = add_space_entry(c, fse->offset, fse->size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_space_entry returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+ } else if (fse->type != FREE_SPACE_BITMAP) {
+ ERR("unknown free-space type %x\n", fse->type);
+ }
+
+ off += sizeof(FREE_SPACE_ENTRY);
+ }
+
+ if (num_bitmaps > 0) {
+ bmpnum = sector_align(off, Vcb->superblock.sector_size) / Vcb->superblock.sector_size;
+ off = (sizeof(UINT32) * num_sectors) + sizeof(UINT64);
+
+ for (i = 0; i < num_entries; i++) {
+ if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size)
+ off = sector_align(off, Vcb->superblock.sector_size);
+
+ fse = (FREE_SPACE_ENTRY*)&data[off];
+
+ if (fse->type == FREE_SPACE_BITMAP) {
+ // FIXME - make sure we don't overflow the buffer here
+ load_free_space_bitmap(Vcb, c, fse->offset, &data[bmpnum * Vcb->superblock.sector_size]);
+ bmpnum++;
+ }
+
+ off += sizeof(FREE_SPACE_ENTRY);
+ }
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ space* s = CONTAINING_RECORD(le, space, list_entry);
+ LIST_ENTRY* le2 = le->Flink;
+
+ if (le2 != &c->space) {
+ space* s2 = CONTAINING_RECORD(le2, space, list_entry);
+
+ if (s2->offset == s->offset + s->size) {
+ s->size += s2->size;
+
+ RemoveEntryList(&s2->list_entry);
+ ExFreePool(s2);
+
+ le2 = le;
+ }
+ }
+
+ le = le2;
+ }
+
+ ExFreePool(data);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT64 lastaddr;
+ BOOL b;
+ space *s, *s2;
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) {
+ Status = load_stored_free_space_cache(Vcb, c);
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
+ ERR("load_stored_free_space_cache returned %08x\n", Status);
+ return Status;
+ }
+ } else
+ Status = STATUS_NOT_FOUND;
+
+ if (Status == STATUS_NOT_FOUND) {
+ TRACE("generating free space cache for chunk %llx\n", c->offset);
+
+ searchkey.obj_id = c->offset;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ lastaddr = c->offset;
+
+ do {
+ if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
+ break;
+
+ if (tp.item->key.obj_id >= c->offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
+ if (tp.item->key.obj_id > lastaddr) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+
+ if (!s) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ s->offset = lastaddr;
+ s->size = tp.item->key.obj_id - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+
+ if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
+ lastaddr = tp.item->key.obj_id + Vcb->superblock.node_size;
+ else
+ lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b)
+ tp = next_tp;
+ } while (b);
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+
+ if (!s) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_FREE;
+ InsertTailList(&c->space, &s->list_entry);
+
+ TRACE("(%llx,%llx)\n", s->offset, s->size);
+ }
+ }
+
+ // add allocated space
+
+ lastaddr = c->offset;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset > lastaddr) {
+ s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+
+ if (!s2) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ s2->offset = lastaddr;
+ s2->size = s->offset - lastaddr;
+ s2->type = SPACE_TYPE_USED;
+
+ InsertTailList(&s->list_entry, &s2->list_entry);
+ }
+
+ lastaddr = s->offset + s->size;
+
+ le = le->Flink;
+ }
+
+ if (lastaddr < c->offset + c->chunk_item->size) {
+ s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+
+ if (!s) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ s->offset = lastaddr;
+ s->size = c->offset + c->chunk_item->size - lastaddr;
+ s->type = SPACE_TYPE_USED;
+ InsertTailList(&c->space, &s->list_entry);
+ }
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type);
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS insert_cache_extent(device_extension* Vcb, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ UINT64 flags;
+
+ // FIXME - how do we know which RAID level to put this to?
+ flags = BLOCK_FLAG_DATA; // SINGLE
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk_inode(Vcb, Vcb->root_root, inode, NULL, c, start, length, FALSE, NULL, NULL, rollback))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ if ((c = alloc_chunk(Vcb, flags, rollback))) {
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk_inode(Vcb, Vcb->root_root, inode, NULL, c, start, length, FALSE, NULL, NULL, rollback))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ 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) {
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+ UINT64 num_entries, new_cache_size, i;
+ UINT64 lastused = c->offset;
+ UINT32 num_sectors;
+
+ // FIXME - also do bitmaps
+ // FIXME - make sure this works when sector_size is not 4096
+
+ *changed = FALSE;
+
+ num_entries = 0;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ space* s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->type == SPACE_TYPE_USED || s->type == SPACE_TYPE_WRITING) {
+ if (s->offset > lastused) {
+// TRACE("free: (%llx,%llx)\n", lastused, s->offset - lastused);
+ num_entries++;
+ }
+
+ lastused = s->offset + s->size;
+ }
+
+ le = le->Flink;
+ }
+
+ if (c->offset + c->chunk_item->size > lastused) {
+// TRACE("free: (%llx,%llx)\n", lastused, c->offset + c->chunk_item->size - lastused);
+ num_entries++;
+ }
+
+ new_cache_size = sizeof(UINT64) + (num_entries * sizeof(FREE_SPACE_ENTRY));
+
+ num_sectors = sector_align(new_cache_size, Vcb->superblock.sector_size) / Vcb->superblock.sector_size;
+ num_sectors = sector_align(num_sectors, CACHE_INCREMENTS);
+
+ // adjust for padding
+ // FIXME - there must be a more efficient way of doing this
+ new_cache_size = sizeof(UINT64) + (sizeof(UINT32) * num_sectors);
+ for (i = 0; i < num_entries; i++) {
+ if ((new_cache_size / Vcb->superblock.sector_size) != ((new_cache_size + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size))
+ new_cache_size = sector_align(new_cache_size, Vcb->superblock.sector_size);
+
+ new_cache_size += sizeof(FREE_SPACE_ENTRY);
+ }
+
+ new_cache_size = sector_align(new_cache_size, CACHE_INCREMENTS * Vcb->superblock.sector_size);
+
+ TRACE("chunk %llx: cache_size = %llx, new_cache_size = %llx\n", c->offset, c->cache_size, new_cache_size);
+
+ if (new_cache_size > c->cache_size) {
+ if (c->cache_size == 0) {
+ INODE_ITEM* ii;
+ FREE_SPACE_ITEM* fsi;
+ UINT64 inode;
+
+ // create new inode
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(ii, sizeof(INODE_ITEM));
+ ii->st_size = new_cache_size;
+ ii->st_blocks = new_cache_size;
+ ii->st_nlink = 1;
+ ii->st_mode = S_IRUSR | S_IWUSR | __S_IFREG;
+ ii->flags = BTRFS_INODE_NODATASUM | BTRFS_INODE_NODATACOW | BTRFS_INODE_NOCOMPRESS | BTRFS_INODE_PREALLOC;
+
+ if (Vcb->root_root->lastinode == 0)
+ get_last_inode(Vcb, Vcb->root_root);
+
+ inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // create new free space entry
+
+ fsi = ExAllocatePoolWithTag(PagedPool, sizeof(FREE_SPACE_ITEM), ALLOC_TAG);
+ if (!fsi) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ fsi->key.obj_id = inode;
+ fsi->key.obj_type = TYPE_INODE_ITEM;
+ fsi->key.offset = 0;
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ // allocate space
+
+ Status = insert_cache_extent(Vcb, inode, 0, new_cache_size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_cache_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ Vcb->root_root->lastinode = inode;
+ c->cache_inode = inode;
+ } else {
+ KEY searchkey;
+ traverse_ptr tp;
+ INODE_ITEM* ii;
+
+ ERR("extending existing inode\n");
+
+ // FIXME - try to extend existing extent first of all
+ // Or ditch all existing extents and replace with one new one?
+
+ // add INODE_ITEM to tree cache
+
+ searchkey.obj_id = c->cache_inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(INODE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ii = (INODE_ITEM*)tp.item->data;
+
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
+
+ // add free_space entry to tree cache
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
+
+ // add new extent
+
+ Status = insert_cache_extent(Vcb, c->cache_inode, c->cache_size, new_cache_size - c->cache_size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_cache_extent returned %08x\n", Status);
+ return Status;
+ }
+
+ // modify INODE_ITEM
+
+ ii->st_size = new_cache_size;
+ ii->st_blocks = new_cache_size;
+ }
+
+ c->cache_size = new_cache_size;
+ *changed = TRUE;
+ } else {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ // add INODE_ITEM and free_space entry to tree cache, for writing later
+
+ searchkey.obj_id = c->cache_inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(INODE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
+ }
+
+ // FIXME - reduce inode allocation if cache is shrinking. Make sure to avoid infinite write loops
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ NTSTATUS Status;
+ chunk* c;
+
+ *changed = FALSE;
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->space_changed) {
+ BOOL b;
+
+ Status = allocate_cache_chunk(Vcb, c, &b, rollback);
+
+ if (b)
+ *changed = TRUE;
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("allocate_cache_chunk(%llx) returned %08x\n", c->offset, Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+ FREE_SPACE_ITEM* fsi;
+ INODE_ITEM* ii;
+ void* data;
+ FREE_SPACE_ENTRY* fse;
+ UINT64 num_entries, num_sectors, lastused, *cachegen, i, off;
+ UINT32* checksums;
+ LIST_ENTRY* le;
+ BOOL b;
+
+ data = ExAllocatePoolWithTag(NonPagedPool, c->cache_size, ALLOC_TAG);
+ if (!data) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(data, c->cache_size);
+
+ num_entries = 0;
+ num_sectors = c->cache_size / Vcb->superblock.sector_size;
+ off = (sizeof(UINT32) * num_sectors) + sizeof(UINT64);
+
+ lastused = c->offset;
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ space* s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->type == SPACE_TYPE_USED || s->type == SPACE_TYPE_WRITING) {
+ if (s->offset > lastused) {
+ if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size)
+ off = sector_align(off, Vcb->superblock.sector_size);
+
+ fse = (FREE_SPACE_ENTRY*)((UINT8*)data + off);
+
+ fse->offset = lastused;
+ fse->size = s->offset - lastused;
+ fse->type = FREE_SPACE_EXTENT;
+ num_entries++;
+
+ off += sizeof(FREE_SPACE_ENTRY);
+ }
+
+ lastused = s->offset + s->size;
+ }
+
+ le = le->Flink;
+ }
+
+ if (c->offset + c->chunk_item->size > lastused) {
+ if ((off + sizeof(FREE_SPACE_ENTRY)) / Vcb->superblock.sector_size != off / Vcb->superblock.sector_size)
+ off = sector_align(off, Vcb->superblock.sector_size);
+
+ fse = (FREE_SPACE_ENTRY*)((UINT8*)data + off);
+
+ fse->offset = lastused;
+ fse->size = c->offset + c->chunk_item->size - lastused;
+ fse->type = FREE_SPACE_EXTENT;
+ num_entries++;
+ }
+
+ // update INODE_ITEM
+
+ searchkey.obj_id = c->cache_inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(INODE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ii = (INODE_ITEM*)tp.item->data;
+
+ ii->generation = Vcb->superblock.generation;
+ ii->transid = Vcb->superblock.generation;
+ ii->sequence++;
+ ii->st_ctime = *now;
+
+ // update free_space item
+
+ searchkey.obj_id = FREE_SPACE_CACHE_ID;
+ searchkey.obj_type = 0;
+ searchkey.offset = c->offset;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ fsi = (FREE_SPACE_ITEM*)tp.item->data;
+
+ fsi->generation = Vcb->superblock.generation;
+ fsi->num_entries = num_entries;
+ fsi->num_bitmaps = 0;
+
+ // set cache generation
+
+ cachegen = (UINT64*)((UINT8*)data + (sizeof(UINT32) * num_sectors));
+ *cachegen = Vcb->superblock.generation;
+
+ // calculate cache checksums
+
+ checksums = (UINT32*)data;
+
+ // FIXME - if we know sector is fully zeroed, use cached checksum
+
+ for (i = 0; i < num_sectors; i++) {
+ if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
+ checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
+ checksums[i] = 0; // FIXME - test this
+ else
+ checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (sizeof(UINT32) * num_sectors), ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
+ }
+
+ // write cache
+
+ searchkey.obj_id = c->cache_inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ do {
+ traverse_ptr next_tp;
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->key.offset < c->cache_size) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* eds;
+
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (ed->type != EXTENT_TYPE_REGULAR) {
+ ERR("cache EXTENT_DATA type not regular\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ eds = (EXTENT_DATA2*)&ed->data[0];
+
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ ERR("not writing compressed cache\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("encryption not supported\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ed->encoding != BTRFS_ENCODING_NONE) {
+ WARN("other encodings not supported\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (eds->address == 0) {
+ ERR("not writing cache to sparse extent\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ Status = write_data(Vcb, eds->address + eds->offset, (UINT8*)data + tp.item->key.offset, min(c->cache_size - tp.item->key.offset, eds->num_bytes));
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_data returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
+ break;
+ }
+ } while (b);
+
+ ExFreePool(data);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ NTSTATUS Status;
+ chunk* c;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->space_changed) {
+ Status = update_chunk_cache(Vcb, c, &now, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status);
+ return Status;
+ }
+
+ }
+
+ le = le->Flink;
+ }
+
+ return STATUS_SUCCESS;
+}
#include "btrfs_drv.h"
#include "btrfsioctl.h"
+#ifndef __REACTOS__
+#include <winioctl.h>
+#endif
#ifndef FSCTL_CSV_CONTROL
#define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
+#define DOTDOT ".."
+
static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
btrfs_get_file_ids* bgfi;
fcb* fcb;
bgfi->subvol = fcb->subvol->id;
bgfi->inode = fcb->inode;
- bgfi->top = fcb->Vcb->root_fcb == fcb ? TRUE : FALSE;
+ bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
+
+ return STATUS_SUCCESS;
+}
+
+static void get_uuid(BTRFS_UUID* uuid) {
+ LARGE_INTEGER seed;
+ UINT8 i;
+
+ seed = KeQueryPerformanceCounter(NULL);
+
+ for (i = 0; i < 16; i+=2) {
+ ULONG rand = RtlRandomEx(&seed.LowPart);
+
+ uuid->uuid[i] = (rand & 0xff00) >> 8;
+ uuid->uuid[i+1] = rand & 0xff;
+ }
+}
+
+static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) {
+ UINT8* buf;
+ NTSTATUS Status;
+ write_tree_context* wtc;
+ LIST_ENTRY* le;
+ tree t;
+ tree_header* th;
+ chunk* c;
+
+ buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+ if (!buf) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG);
+ if (!wtc) {
+ ERR("out of memory\n");
+ ExFreePool(buf);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = read_tree(Vcb, addr, buf);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_tree returned %08x\n", Status);
+ goto end;
+ }
+
+ th = (tree_header*)buf;
+
+ RtlZeroMemory(&t, sizeof(tree));
+ t.flags = dupflags;
+ t.root = subvol;
+ t.header.level = th->level;
+ t.header.tree_id = t.root->id;
+
+ Status = get_tree_new_address(Vcb, &t, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_tree_new_address returned %08x\n", Status);
+ goto end;
+ }
+
+ if (!t.has_new_address) {
+ ERR("tree new address not set\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ c = get_chunk_from_address(Vcb, t.new_address);
+
+ if (c) {
+ increase_chunk_usage(c, Vcb->superblock.node_size);
+ } else {
+ ERR("could not find chunk for address %llx\n", t.new_address);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ th->address = t.new_address;
+ th->tree_id = subvol->id;
+ th->generation = Vcb->superblock.generation;
+
+ if (th->level == 0) {
+ UINT32 i;
+ leaf_node* ln = (leaf_node*)&th[1];
+
+ for (i = 0; i < th->num_items; i++) {
+ if (ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA) && ln[i].offset + ln[i].size <= Vcb->superblock.node_size - sizeof(tree_header)) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)(((UINT8*)&th[1]) + ln[i].offset);
+
+ // FIXME - what are we supposed to do with prealloc here? Replace it with sparse extents, or do new preallocation?
+ if (ed->type == EXTENT_TYPE_REGULAR && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+
+ if (ed2->size != 0) { // not sparse
+ Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
+ goto end;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ UINT32 i;
+ UINT64 newaddr;
+ 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);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("snapshot_tree_copy returned %08x\n", Status);
+ goto end;
+ }
+
+ in[i].generation = Vcb->superblock.generation;
+ in[i].address = newaddr;
+ }
+ }
+
+ *((UINT32*)buf) = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+
+ KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
+ InitializeListHead(&wtc->stripes);
+
+ Status = write_tree(Vcb, t.new_address, buf, wtc);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_tree returned %08x\n", Status);
+ goto end;
+ }
+
+ if (wtc->stripes.Flink != &wtc->stripes) {
+ // launch writes and wait
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ IoCallDriver(stripe->device->devobj, stripe->Irp);
+
+ le = le->Flink;
+ }
+
+ KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+
+ le = wtc->stripes.Flink;
+ while (le != &wtc->stripes) {
+ write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
+
+ if (!NT_SUCCESS(stripe->iosb.Status)) {
+ Status = stripe->iosb.Status;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ free_write_tree_stripes(wtc);
+ buf = NULL;
+ }
+
+ if (NT_SUCCESS(Status))
+ *newaddr = t.new_address;
+
+end:
+ ExFreePool(wtc);
+
+ if (buf)
+ ExFreePool(buf);
+
+ return Status;
+}
+
+static void flush_subvol_fcbs(root* subvol) {
+ LIST_ENTRY* le = subvol->fcbs.Flink;
+
+ if (IsListEmpty(&subvol->fcbs))
+ return;
+
+ while (le != &subvol->fcbs) {
+ struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
+ IO_STATUS_BLOCK iosb;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
+ CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
+
+ le = le->Flink;
+ }
+}
+
+static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8) {
+ LIST_ENTRY rollback;
+ UINT64 id;
+ NTSTATUS Status;
+ root *r, *subvol = subvol_fcb->subvol;
+ KEY searchkey;
+ traverse_ptr tp;
+ UINT64 address, dirpos, *root_num;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ fcb* fcb = parent->FsContext;
+ ULONG disize, rrsize;
+ DIR_ITEM *di, *di2;
+ ROOT_REF *rr, *rr2;
+ INODE_ITEM* ii;
+
+ InitializeListHead(&rollback);
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ // flush open files on this subvol
+
+ flush_subvol_fcbs(subvol);
+
+ // flush metadata
+
+ if (Vcb->write_trees > 0)
+ do_write(Vcb, &rollback);
+
+ free_trees(Vcb);
+
+ clear_rollback(&rollback);
+
+ InitializeListHead(&rollback);
+
+ // create new root
+
+ if (Vcb->root_root->lastinode == 0)
+ get_last_inode(Vcb, Vcb->root_root);
+
+ id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
+ Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_root returned %08x\n", Status);
+ goto end;
+ }
+
+ if (!Vcb->uuid_root) {
+ root* uuid_root;
+
+ TRACE("uuid root doesn't exist, creating it\n");
+
+ Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_root returned %08x\n", Status);
+ goto end;
+ }
+
+ Vcb->uuid_root = uuid_root;
+ }
+
+ root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
+ if (!root_num) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ tp.tree = NULL;
+
+ do {
+ get_uuid(&r->root_item.uuid);
+
+ RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
+ 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);
+ } 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)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(root_num);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ searchkey.obj_id = r->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("snapshot_tree_copy returned %08x\n", Status);
+ goto end;
+ }
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ r->root_item.inode.generation = 1;
+ r->root_item.inode.st_size = 3;
+ r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
+ r->root_item.inode.st_nlink = 1;
+ r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
+ r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
+ r->root_item.generation = Vcb->superblock.generation;
+ r->root_item.objid = subvol->root_item.objid;
+ r->root_item.block_number = address;
+ r->root_item.bytes_used = subvol->root_item.bytes_used;
+ r->root_item.last_snapshot_generation = Vcb->superblock.generation;
+ r->root_item.root_level = subvol->root_item.root_level;
+ r->root_item.generation2 = Vcb->superblock.generation;
+ r->root_item.parent_uuid = subvol->root_item.uuid;
+ r->root_item.ctransid = subvol->root_item.ctransid;
+ r->root_item.otransid = Vcb->superblock.generation;
+ r->root_item.ctime = subvol->root_item.ctime;
+ r->root_item.otime = now;
+
+ r->treeholder.address = address;
+
+ // FIXME - do we need to copy over the send and receive fields too?
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find ROOT_ITEM for subvol %llx\n", r->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM));
+ Vcb->root_root->lastinode = r->id;
+
+ // update ROOT_ITEM of original subvol
+
+ subvol->root_item.last_snapshot_generation = Vcb->superblock.generation;
+
+ // We also rewrite the top of the old subvolume tree, for some reason
+ searchkey.obj_id = 0;
+ searchkey.obj_type = 0;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (!subvol->treeholder.tree->write) {
+ subvol->treeholder.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
+
+ // add DIR_ITEM
+
+ dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode);
+ if (dirpos == 0) {
+ ERR("find_next_dir_index failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ disize = sizeof(DIR_ITEM) - 1 + utf8->Length;
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di2) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ ExFreePool(di);
+ goto end;
+ }
+
+ di->key.obj_id = id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0xffffffffffffffff;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8->Length;
+ di->type = BTRFS_TYPE_DIRECTORY;
+ RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
+
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ goto end;
+ }
+
+ // add DIR_INDEX
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add ROOT_REF
+
+ rrsize = sizeof(ROOT_REF) - 1 + utf8->Length;
+ rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ if (!rr) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ rr->dir = fcb->inode;
+ rr->index = dirpos;
+ rr->n = utf8->Length;
+ RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add ROOT_BACKREF
+
+ rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ if (!rr2) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(rr2, rr, rrsize);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // change fcb's INODE_ITEM
+
+ // unlike when we create a file normally, the seq of the parent doesn't appear to change
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_size += utf8->Length * 2;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(Vcb, &tp, &rollback);
+
+ insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback);
+
+ fcb->subvol->root_item.ctime = now;
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+
+ if (Vcb->write_trees > 0)
+ do_write(Vcb, &rollback);
+
+ free_trees(Vcb);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (NT_SUCCESS(Status))
+ clear_rollback(&rollback);
+ else
+ do_rollback(Vcb, &rollback);
+
+ release_tree_lock(Vcb, TRUE);
+
+ return Status;
+}
+
+static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
+ PFILE_OBJECT subvol_obj;
+ NTSTATUS Status;
+ btrfs_create_snapshot* bcs = data;
+ fcb* subvol_fcb;
+ ANSI_STRING utf8;
+ UNICODE_STRING nameus;
+ ULONG len;
+ UINT32 crc32;
+ fcb* fcb;
+ ccb* ccb;
+ file_ref* fileref;
+
+ if (length < offsetof(btrfs_create_snapshot, name))
+ return STATUS_INVALID_PARAMETER;
+
+ if (length < offsetof(btrfs_create_snapshot, name) + bcs->namelen)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!bcs->subvol)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!FileObject || !FileObject->FsContext)
+ return STATUS_INVALID_PARAMETER;
+
+ fcb = FileObject->FsContext;
+ ccb = FileObject->FsContext2;
+
+ if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
+ return STATUS_INVALID_PARAMETER;
+
+ fileref = ccb->fileref;
+
+ if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ nameus.Buffer = bcs->name;
+ nameus.Length = nameus.MaximumLength = bcs->namelen;
+
+ if (!is_file_name_valid(&nameus))
+ return STATUS_OBJECT_NAME_INVALID;
+
+ utf8.Buffer = NULL;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &len, bcs->name, bcs->namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ return Status;
+ }
+
+ if (len == 0) {
+ ERR("RtlUnicodeToUTF8N returned a length of 0\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ utf8.MaximumLength = utf8.Length = len;
+ utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
+
+ if (!utf8.Buffer) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, bcs->name, bcs->namelen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ goto end2;
+ }
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
+
+ if (find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) {
+ WARN("file already exists\n");
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end2;
+ }
+
+ Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("ObReferenceObjectByHandle returned %08x\n", Status);
+ goto end2;
+ }
+
+ subvol_fcb = subvol_obj->FsContext;
+ if (!subvol_fcb) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
+ WARN("handle inode was %llx, expected %llx\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ ccb = subvol_obj->FsContext2;
+
+ if (!ccb) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (!(ccb->access & FILE_TRAVERSE)) {
+ WARN("insufficient privileges\n");
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8);
+
+ if (NT_SUCCESS(Status)) {
+ UNICODE_STRING ffn;
+
+ ffn.Length = fileref->full_filename.Length + bcs->namelen;
+ if (fcb != fcb->Vcb->root_fileref->fcb)
+ ffn.Length += sizeof(WCHAR);
+
+ ffn.MaximumLength = ffn.Length;
+ ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG);
+
+ if (ffn.Buffer) {
+ ULONG i;
+
+ RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length);
+ i = fileref->full_filename.Length;
+
+ if (fcb != fcb->Vcb->root_fileref->fcb) {
+ ffn.Buffer[i / sizeof(WCHAR)] = '\\';
+ i += sizeof(WCHAR);
+ }
+
+ RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], bcs->name, bcs->namelen);
+
+ TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer);
+
+ FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL,
+ FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
+
+ ExFreePool(ffn.Buffer);
+ } else
+ ERR("out of memory\n");
+ }
+
+end:
+ ObDereferenceObject(subvol_obj);
+
+end2:
+ ExFreePool(utf8.Buffer);
+
+ return Status;
+}
+
+static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) {
+ fcb* fcb;
+ ccb* ccb;
+ file_ref* fileref;
+ NTSTATUS Status;
+ LIST_ENTRY rollback;
+ UINT64 id;
+ root* r;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ ULONG len, disize, rrsize, irsize;
+ UNICODE_STRING nameus;
+ ANSI_STRING utf8;
+ UINT64 dirpos;
+ DIR_ITEM *di, *di2;
+ UINT32 crc32;
+ ROOT_REF *rr, *rr2;
+ INODE_ITEM* ii;
+ INODE_REF* ir;
+ KEY searchkey;
+ traverse_ptr tp;
+ SECURITY_DESCRIPTOR* sd = NULL;
+ SECURITY_SUBJECT_CONTEXT subjcont;
+ PSID owner;
+ BOOLEAN defaulted;
+ UINT64* root_num;
+
+ fcb = FileObject->FsContext;
+ if (!fcb) {
+ ERR("error - fcb was NULL\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ccb = FileObject->FsContext2;
+ if (!ccb) {
+ ERR("error - ccb was NULL\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ fileref = ccb->fileref;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ ERR("parent FCB was not a directory\n");
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ if (fileref->deleted || fcb->deleted) {
+ ERR("parent has been deleted\n");
+ return STATUS_FILE_DELETED;
+ }
+
+ if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
+ WARN("insufficient privileges\n");
+ return STATUS_ACCESS_DENIED;
+ }
+
+ nameus.Length = nameus.MaximumLength = length;
+ nameus.Buffer = name;
+
+ if (!is_file_name_valid(&nameus))
+ return STATUS_OBJECT_NAME_INVALID;
+
+ utf8.Buffer = NULL;
+
+ Status = RtlUnicodeToUTF8N(NULL, 0, &len, name, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ return Status;
+ }
+
+ if (len == 0) {
+ ERR("RtlUnicodeToUTF8N returned a length of 0\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ utf8.MaximumLength = utf8.Length = len;
+ utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
+
+ if (!utf8.Buffer) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, name, length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
+ goto end2;
+ }
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ InitializeListHead(&rollback);
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
+
+ if (find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) {
+ WARN("file already exists\n");
+ Status = STATUS_OBJECT_NAME_COLLISION;
+ goto end;
+ }
+
+ if (Vcb->root_root->lastinode == 0)
+ get_last_inode(Vcb, Vcb->root_root);
+
+ // FIXME - make sure rollback removes new roots from internal structures
+
+ id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
+ Status = create_root(Vcb, id, &r, FALSE, 0, &rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_root returned %08x\n", Status);
+ goto end;
+ }
+
+ TRACE("created root %llx\n", id);
+
+ if (!Vcb->uuid_root) {
+ root* uuid_root;
+
+ TRACE("uuid root doesn't exist, creating it\n");
+
+ Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_root returned %08x\n", Status);
+ goto end;
+ }
+
+ Vcb->uuid_root = uuid_root;
+ }
+
+ root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
+ if (!root_num) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ tp.tree = NULL;
+
+ do {
+ get_uuid(&r->root_item.uuid);
+
+ RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
+ 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);
+ } 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)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ r->root_item.inode.generation = 1;
+ r->root_item.inode.st_size = 3;
+ r->root_item.inode.st_blocks = Vcb->superblock.node_size;
+ r->root_item.inode.st_nlink = 1;
+ r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
+ r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
+
+ r->root_item.objid = SUBVOL_ROOT_INODE;
+ r->root_item.bytes_used = Vcb->superblock.node_size;
+ r->root_item.ctransid = Vcb->superblock.generation;
+ r->root_item.otransid = Vcb->superblock.generation;
+ r->root_item.ctime = now;
+ r->root_item.otime = now;
+
+ // add .. inode to new subvol
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlZeroMemory(ii, sizeof(INODE_ITEM));
+ ii->generation = Vcb->superblock.generation;
+ ii->transid = Vcb->superblock.generation;
+ ii->st_nlink = 1;
+ ii->st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
+ ii->st_atime = ii->st_ctime = ii->st_mtime = ii->otime = now;
+ ii->st_gid = GID_NOBODY; // FIXME?
+
+ SeCaptureSubjectContext(&subjcont);
+
+ Status = SeAssignSecurity(fcb->sd, NULL, (void**)&sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("SeAssignSecurity returned %08x\n", Status);
+ goto end;
+ }
+
+ if (!sd) {
+ ERR("SeAssignSecurity returned NULL security descriptor\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
+ ii->st_uid = UID_NOBODY;
+ } else {
+ ii->st_uid = sid_to_uid(&owner);
+ }
+
+ if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add security.NTACL xattr
+
+ Status = set_xattr(Vcb, r, r->root_item.objid, EA_NTACL, EA_NTACL_HASH, (UINT8*)sd, RtlLengthSecurityDescriptor(fcb->sd), &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("set_xattr returned %08x\n", Status);
+ goto end;
+ }
+
+ ExFreePool(sd);
+
+ // add INODE_REF
+
+ irsize = sizeof(INODE_REF) - 1 + strlen(DOTDOT);
+ ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
+ if (!ir) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ ir->index = 0;
+ ir->n = strlen(DOTDOT);
+ RtlCopyMemory(ir->name, DOTDOT, ir->n);
+
+ if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add DIR_ITEM
+
+ dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode);
+ if (dirpos == 0) {
+ ERR("find_next_dir_index failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ disize = sizeof(DIR_ITEM) - 1 + utf8.Length;
+ di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+ if (!di2) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ ExFreePool(di);
+ goto end;
+ }
+
+ di->key.obj_id = id;
+ di->key.obj_type = TYPE_ROOT_ITEM;
+ di->key.offset = 0;
+ di->transid = Vcb->superblock.generation;
+ di->m = 0;
+ di->n = utf8.Length;
+ di->type = BTRFS_TYPE_DIRECTORY;
+ RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
+
+ RtlCopyMemory(di2, di, disize);
+
+ Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("add_dir_item returned %08x\n", Status);
+ goto end;
+ }
+
+ // add DIR_INDEX
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add ROOT_REF
+
+ rrsize = sizeof(ROOT_REF) - 1 + utf8.Length;
+ rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ if (!rr) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ rr->dir = fcb->inode;
+ rr->index = dirpos;
+ rr->n = utf8.Length;
+ RtlCopyMemory(rr->name, utf8.Buffer, utf8.Length);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // add ROOT_BACKREF
+
+ rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
+ if (!rr2) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(rr2, rr, rrsize);
+
+ if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) {
+ ERR("insert_tree_item failed\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ // change fcb->subvol's ROOT_ITEM
+
+ fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ // change fcb's INODE_ITEM
+
+ // unlike when we create a file normally, the times and seq of the parent don't appear to change
+ fcb->inode_item.transid = Vcb->superblock.generation;
+ fcb->inode_item.st_size += utf8.Length * 2;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(&searchkey, &tp.item->key)) {
+ ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
+ }
+
+ RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
+ delete_tree_item(Vcb, &tp, &rollback);
+
+ insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback);
+
+ Vcb->root_root->lastinode = id;
+
+ Status = STATUS_SUCCESS;
+
+end:
+ if (NT_SUCCESS(Status))
+ Status = consider_write(Vcb);
+
+ if (!NT_SUCCESS(Status))
+ do_rollback(Vcb, &rollback);
+ else
+ clear_rollback(&rollback);
+
+ release_tree_lock(Vcb, TRUE);
+
+ if (NT_SUCCESS(Status)) {
+ UNICODE_STRING ffn;
+
+ ffn.Length = fileref->full_filename.Length + length;
+ if (fcb != fcb->Vcb->root_fileref->fcb)
+ ffn.Length += sizeof(WCHAR);
+
+ ffn.MaximumLength = ffn.Length;
+ ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG);
+
+ if (ffn.Buffer) {
+ ULONG i;
+
+ RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length);
+ i = fileref->full_filename.Length;
+
+ if (fcb != fcb->Vcb->root_fileref->fcb) {
+ ffn.Buffer[i / sizeof(WCHAR)] = '\\';
+ i += sizeof(WCHAR);
+ }
+
+ RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], name, length);
+
+ TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer);
+
+ FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL,
+ FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
+
+ ExFreePool(ffn.Buffer);
+ } else
+ ERR("out of memory\n");
+ }
+
+end2:
+ if (utf8.Buffer)
+ ExFreePool(utf8.Buffer);
+
+ return Status;
+}
+
+static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) {
+ UINT64 i, num_devices;
+ NTSTATUS Status;
+ ULONG cc;
+ IO_STATUS_BLOCK iosb;
+ BOOL verify = FALSE;
+
+ num_devices = Vcb->superblock.num_devices;
+ for (i = 0; i < num_devices; i++) {
+ if (Vcb->devices[i].devobj && Vcb->devices[i].removable) {
+ Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), FALSE, &iosb);
+
+ if (iosb.Information != sizeof(ULONG))
+ cc = 0;
+
+ if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != Vcb->devices[i].change_count)) {
+ Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+ verify = TRUE;
+ }
+
+ if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
+ Vcb->devices[i].change_count = cc;
+
+ if (!NT_SUCCESS(Status) || verify) {
+ IoSetHardErrorOrVerifyDevice(Irp, Vcb->devices[i].devobj);
+
+ return verify ? STATUS_VERIFY_REQUIRED : Status;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
+ FILESYSTEM_STATISTICS* fss;
+
+ WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
+
+ // This is hideously wrong, but at least it stops SMB from breaking
+
+ if (buflen < sizeof(FILESYSTEM_STATISTICS))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ fss = buffer;
+ RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
+
+ fss->Version = 1;
+ fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
+ fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS);
+
+ *retlen = sizeof(FILESYSTEM_STATISTICS);
return STATUS_SUCCESS;
}
break;
case FSCTL_IS_VOLUME_MOUNTED:
- WARN("STUB: FSCTL_IS_VOLUME_MOUNTED\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp);
break;
case FSCTL_IS_PATHNAME_VALID:
break;
case FSCTL_FILESYSTEM_GET_STATISTICS:
- WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ Status = fs_get_statistics(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
+ IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
break;
case FSCTL_GET_NTFS_VOLUME_DATA:
break;
case FSCTL_DELETE_REPARSE_POINT:
- WARN("STUB: FSCTL_DELETE_REPARSE_POINT\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ Status = delete_reparse_point(DeviceObject, Irp);
break;
case FSCTL_ENUM_USN_DATA:
case FSCTL_BTRFS_GET_FILE_IDS:
Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
break;
+
+ case FSCTL_BTRFS_CREATE_SUBVOL:
+ Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
+ break;
+
+ case FSCTL_BTRFS_CREATE_SNAPSHOT:
+ Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
+ break;
default:
- WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
+ TRACE("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
(IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
+++ /dev/null
-/*
- * Adapted from code at http://support.fccps.cz/download/adv/frr/win32_ddk_mingw/win32_ddk_mingw.html - thanks!
- *
- File created by Frank Rysanek <rysanek@fccps.cz>
- Source code taken almost verbatim from "Driver Development, Part 1"
- published by Toby Opferman at CodeProject.com
-*/
-
-#include <stdio.h>
-#include <windows.h>
-/*#include <string.h>*/
-#include <unistd.h> /* getcwd() */
-
-#define MY_DRIVER_NAME "btrfs"
-#define MY_DEVICE_NAME "\\Btrfs"
-#define MY_DOSDEVICE_NAME "\\DosDevices\\" MY_DRIVER_NAME /* AKA symlink name */
-/* for the loader and app */
-#define MY_SERVICE_NAME_LONG "Driver Test2"
-#define MY_SERVICE_NAME_SHORT MY_DRIVER_NAME
-#define MY_DRIVER_FILENAME MY_DRIVER_NAME ".sys"
-
-#define MAX_CWD_LEN 1024
-static char cwd[MAX_CWD_LEN+3]; /* the XXXXX.sys filename will get appended to this as well */
-
-/* geterrstr() taken verbatim from some code snippet at www.mingw.org by Dan Osborne. */
-/* Apparently, it's a way to get a classic null-terminated string containing "last error". */
-static char errbuffer[256];
-
-static const char *geterrstr(DWORD errcode)
-{
- size_t skip = 0;
- DWORD chars;
-
- chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, errcode, 0, errbuffer, sizeof(errbuffer)-1, 0);
- errbuffer[sizeof(errbuffer)-1] = 0;
-
- if (chars)
- {
- while (errbuffer[chars-1] == '\r' || errbuffer[chars-1] == '\n')
- {
- errbuffer[--chars] = 0;
- }
- }
-
- if (chars && errbuffer[chars-1] == '.') errbuffer[--chars] = 0;
-
- if (chars >= 2 && errbuffer[0] == '%'
- && errbuffer[1] >= '0' && errbuffer[1] <= '9')
- {
- skip = 2;
-
- while (chars > skip && errbuffer[skip] == ' ') ++skip;
-
- if (chars >= skip+2 && errbuffer[skip] == 'i' && errbuffer[skip+1] == 's')
- {
- skip += 2;
- while (chars > skip && errbuffer[skip] == ' ') ++skip;
- }
- }
-
- if (chars > skip && errbuffer[skip] >= 'A' && errbuffer[skip] <= 'Z')
- {
- errbuffer[skip] += 'a' - 'A';
- }
-
- return errbuffer+skip;
-}
-
-void process_error(void)
-{
- DWORD err = GetLastError();
- printf("Error: %lu = \"%s\"\n", (unsigned long)err, geterrstr(err));
-}
-
-int main(void)
-{
- HANDLE hSCManager;
- HANDLE hService;
- SERVICE_STATUS ss;
- int retval = 0;
- BOOL amd64;
-
- /* First of all, maybe concatenate the current working directory
- with the desired driver file name - before we start messing with
- the service manager etc. */
- if (getcwd(cwd, MAX_CWD_LEN) == NULL) /* error */
- {
- printf("Failed to learn the current working directory!\n");
- retval = -8;
- goto err_out1;
- } /* else got CWD just fine */
-
- if (strlen(cwd) + strlen(MY_DRIVER_FILENAME) + 1 > MAX_CWD_LEN)
- {
- printf("Current working dir + driver filename > longer than %d ?!?\n", MAX_CWD_LEN);
- retval = -9;
- goto err_out1;
- } /* else our buffer is long enough :-) */
-
- strcat(cwd, "\\");
-
- IsWow64Process(GetCurrentProcess(),&amd64);
- strcat(cwd, amd64 ? "x64" : "x86");
-
- strcat(cwd, "\\");
- strcat(cwd, MY_DRIVER_FILENAME);
- printf("Driver path+name: %s\n", cwd);
-
-
- printf("Going to open the service manager... ");
-
- hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
- if (! hSCManager)
- {
- printf("Uh oh:\n");
- process_error();
- retval = -1;
- goto err_out1;
- }
-
- printf("okay.\n");
- printf("Going to create the service... ");
-
- hService = CreateService(hSCManager, MY_SERVICE_NAME_SHORT,
- MY_SERVICE_NAME_LONG,
- SERVICE_START | DELETE | SERVICE_STOP,
- SERVICE_KERNEL_DRIVER,
- SERVICE_DEMAND_START,
- SERVICE_ERROR_IGNORE,
- cwd,
- NULL, NULL, NULL, NULL, NULL);
-
- if(!hService)
- {
- process_error();
- printf("\n already exists? Trying to open it... ");
- hService = OpenService(hSCManager, MY_SERVICE_NAME_SHORT,
- SERVICE_START | DELETE | SERVICE_STOP);
- }
-
- if(!hService)
- {
- printf("FAILED!\n");
- process_error();
- retval = -2;
- goto err_out2;
- }
-
- printf("okay.\n");
- printf("Going to start the service... ");
-
- if (StartService(hService, 0, NULL) == 0) /* error */
- {
- printf("Uh oh:\n");
- process_error();
- retval = -3;
- goto err_out3;
- }
-
- printf("okay.\n");
-
-// TCHAR VolumeName[] = _T("Z:");
-// TCHAR DeviceName[] = _T("\\Device\\VDisk1");
-
-// printf("Mounting volume... ");
-// if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH, "T:", "\\Device\\HarddiskVolume3"))
-// {
-// printf("Uh oh:\n");
-// process_error();
-// } else {
-// printf("okay.\n");
-// }
-
-// if (!SetVolumeMountPointA("T:\\", "\\\\?\\Volume{9bd714c3-4379-11e5-b26c-806e6f6e6963}\\")) {
-// printf("Uh oh:\n");
-// process_error();
-// } else {
-// printf("okay.\n");
-// }
-
- printf("\n >>> Press Enter to unload the driver! <<<\n");
- getchar();
-
-// printf("Unmounting volume... ");
-// if (!DefineDosDeviceA(DDD_REMOVE_DEFINITION, "T:", NULL))
-// {
-// printf("Uh oh:\n");
-// process_error();
-// } else {
-// printf("okay.\n");
-// }
-
- printf("Going to stop the service... ");
- if (ControlService(hService, SERVICE_CONTROL_STOP, &ss) == 0) /* error */
- {
- printf("Uh oh:\n");
- process_error();
- retval = -4;
- }
- else printf("okay.\n");
-
-err_out3:
-
- printf("Going to close the service handle... ");
- if (CloseServiceHandle(hService) == 0) /* error */
- {
- printf("Uh oh:\n");
- process_error();
- retval = -6;
- }
- else printf("okay.\n");
-
-err_out2:
-
- printf("Going to close the service manager... ");
- if (CloseServiceHandle(hSCManager) == 0) /* error */
- {
- printf("Uh oh:\n");
- process_error();
- retval = -7;
- }
- else printf("okay.\n");
-
-err_out1:
-
- printf("Finished! :-b\n");
-
- return(retval);
-}
-
--- /dev/null
+#include "btrfs_drv.h"
+
+struct pnp_context;
+
+typedef struct {
+ struct pnp_context* context;
+ PIRP Irp;
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS Status;
+} pnp_stripe;
+
+typedef struct {
+ KEVENT Event;
+ NTSTATUS Status;
+ LONG left;
+ pnp_stripe* stripes;
+} pnp_context;
+
+static NTSTATUS STDCALL pnp_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+ pnp_stripe* stripe = conptr;
+ pnp_context* context = (pnp_context*)stripe->context;
+
+ stripe->Status = Irp->IoStatus.Status;
+
+ InterlockedDecrement(&context->left);
+
+ if (context->left == 0)
+ KeSetEvent(&context->Event, 0, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS send_disks_pnp_message(device_extension* Vcb, UCHAR minor) {
+ pnp_context* context;
+ UINT64 num_devices, i;
+ NTSTATUS Status;
+
+ context = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_context), ALLOC_TAG);
+ if (!context) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(context, sizeof(pnp_context));
+ KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+
+ num_devices = Vcb->superblock.num_devices;
+
+ context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_stripe) * num_devices, ALLOC_TAG);
+ if (!context->stripes) {
+ ERR("out of memory\n");
+ ExFreePool(context);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(context->stripes, sizeof(pnp_stripe) * num_devices);
+
+ for (i = 0; i < num_devices; i++) {
+ PIO_STACK_LOCATION IrpSp;
+
+ if (Vcb->devices[i].devobj) {
+ context->stripes[i].context = (struct pnp_context*)context;
+
+ context->stripes[i].Irp = IoAllocateIrp(Vcb->devices[i].devobj->StackSize, FALSE);
+
+ if (!context->stripes[i].Irp) {
+ UINT64 j;
+
+ ERR("IoAllocateIrp failed\n");
+
+ for (j = 0; j < i; j++) {
+ if (Vcb->devices[j].devobj) {
+ IoFreeIrp(context->stripes[j].Irp);
+ }
+ }
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+ IrpSp->MajorFunction = IRP_MJ_PNP;
+ IrpSp->MinorFunction = minor;
+
+ context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+
+ IoSetCompletionRoutine(context->stripes[i].Irp, pnp_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+
+ context->stripes[i].Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ context->left++;
+ }
+ }
+
+ if (context->left == 0) {
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+
+ for (i = 0; i < num_devices; i++) {
+ if (context->stripes[i].Irp) {
+ IoCallDriver(Vcb->devices[i].devobj, context->stripes[i].Irp);
+ }
+ }
+
+ KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
+
+ Status = STATUS_SUCCESS;
+
+ for (i = 0; i < num_devices; i++) {
+ if (context->stripes[i].Irp) {
+ if (context->stripes[i].Status != STATUS_SUCCESS)
+ Status = context->stripes[i].Status;
+ }
+ }
+
+end:
+ for (i = 0; i < num_devices; i++) {
+ if (context->stripes[i].Irp) {
+ IoFreeIrp(context->stripes[i].Irp);
+ }
+ }
+
+ ExFreePool(context->stripes);
+ ExFreePool(context);
+
+ return Status;
+}
+
+static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+
+ if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->fcb->open_count > 0 || has_open_children(Vcb->root_fileref))) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Status = send_disks_pnp_message(Vcb, IRP_MN_CANCEL_REMOVE_DEVICE);
+ if (!NT_SUCCESS(Status)) {
+ WARN("send_disks_pnp_message returned %08x\n", Status);
+ goto end;
+ }
+
+ Vcb->removing = FALSE;
+end:
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ LIST_ENTRY rollback;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+
+ if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->fcb->open_count > 0 || has_open_children(Vcb->root_fileref))) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
+ }
+
+ Status = send_disks_pnp_message(Vcb, IRP_MN_QUERY_REMOVE_DEVICE);
+ if (!NT_SUCCESS(Status)) {
+ WARN("send_disks_pnp_message returned %08x\n", Status);
+ goto end;
+ }
+
+ Vcb->removing = TRUE;
+
+ InitializeListHead(&rollback);
+
+ acquire_tree_lock(Vcb, TRUE);
+
+ if (Vcb->write_trees > 0)
+ do_write(Vcb, &rollback);
+
+ clear_rollback(&rollback);
+
+ release_tree_lock(Vcb, TRUE);
+
+ Status = STATUS_SUCCESS;
+end:
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ return Status;
+}
+
+static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+
+ Status = send_disks_pnp_message(Vcb, IRP_MN_REMOVE_DEVICE);
+ if (!NT_SUCCESS(Status)) {
+ WARN("send_disks_pnp_message returned %08x\n", Status);
+ }
+
+ if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
+ uninit(Vcb, FALSE);
+ DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS pnp_start_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ FIXME("STUB\n");
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ FIXME("STUB\n");
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ top_level = is_top_level(Irp);
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ switch (IrpSp->MinorFunction) {
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ Status = pnp_cancel_remove_device(DeviceObject, Irp);
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ Status = pnp_query_remove_device(DeviceObject, Irp);
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ Status = pnp_remove_device(DeviceObject, Irp);
+ break;
+
+ case IRP_MN_START_DEVICE:
+ Status = pnp_start_device(DeviceObject, Irp);
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ Status = pnp_surprise_removal(DeviceObject, Irp);
+ break;
+
+ default:
+ TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+ goto end;
+ }
+
+// // Irp->IoStatus.Status = Status;
+// // Irp->IoStatus.Information = 0;
+//
+// IoSkipCurrentIrpStackLocation(Irp);
+//
+// Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+//
+// // IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+end:
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) {
if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
- free_traverse_ptr(&tp);
tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- free_traverse_ptr(&tp);
ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
Status = STATUS_INTERNAL_ERROR;
goto exit;
if (tp.item->key.offset > start) {
ERR("first EXTENT_DATA was after offset\n");
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if (tp.item->size < sizeof(EXTENT_DATA)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if (tp.item->key.offset + len < start) {
ERR("Tried to read beyond end of file\n");
- free_traverse_ptr(&tp);
Status = STATUS_END_OF_FILE;
goto exit;
}
if (ed->compression != BTRFS_COMPRESSION_NONE) {
FIXME("FIXME - compression not yet supported\n");
- free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
WARN("Encryption not supported\n");
- free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (ed->encoding != BTRFS_ENCODING_NONE) {
WARN("Other encodings not supported\n");
- free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (!buf) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (!NT_SUCCESS(Status)) {
ERR("read_data returned %08x\n", Status);
ExFreePool(buf);
- free_traverse_ptr(&tp);
goto exit;
}
default:
WARN("Unsupported extent data type %u\n", ed->type);
- free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
next_tp.item->key.offset != tp.item->key.offset + len
) {
- free_traverse_ptr(&next_tp);
break;
} else {
TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset);
- free_traverse_ptr(&tp);
tp = next_tp;
}
} else
break;
} while (TRUE);
- free_traverse_ptr(&tp);
-
Status = STATUS_SUCCESS;
if (pbr)
*pbr = bytes_read;
TRACE("read\n");
- switch (IrpSp->MinorFunction) {
- case IRP_MN_COMPLETE:
- FIXME("unsupported - IRP_MN_COMPLETE\n");
- break;
-
- case IRP_MN_COMPLETE_MDL:
- FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
- break;
-
- case IRP_MN_COMPLETE_MDL_DPC:
- FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
- break;
-
- case IRP_MN_COMPRESSED:
- FIXME("unsupported - IRP_MN_COMPRESSED\n");
- break;
-
- case IRP_MN_DPC:
- FIXME("unsupported - IRP_MN_DPC\n");
- break;
-
- case IRP_MN_MDL:
- FIXME("unsupported - IRP_MN_MDL\n");
- break;
-
- case IRP_MN_MDL_DPC:
- FIXME("unsupported - IRP_MN_MDL_DPC\n");
- break;
-
- case IRP_MN_NORMAL:
- TRACE("IRP_MN_NORMAL\n");
- break;
-
- default:
- WARN("unknown minor %u\n", IrpSp->MinorFunction);
- }
-
- data = map_user_buffer(Irp);
+ Irp->IoStatus.Information = 0;
- if (Irp->MdlAddress && !data) {
- ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
+ if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
+ CcMdlReadComplete(IrpSp->FileObject, Irp->MdlAddress);
+
+ Irp->MdlAddress = NULL;
+ Status = STATUS_SUCCESS;
+ bytes_read = 0;
+
goto exit;
}
- Irp->IoStatus.Information = 0;
-
start = IrpSp->Parameters.Read.ByteOffset.QuadPart;
length = IrpSp->Parameters.Read.Length;
bytes_read = 0;
goto exit;
}
- TRACE("file = %.*S (fcb = %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
+ TRACE("file = %S (fcb = %p)\n", file_desc(FileObject), fcb);
TRACE("offset = %llx, length = %x\n", start, length);
TRACE("paging_io = %s, no cache = %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE", Irp->Flags & IRP_NOCACHE ? "TRUE" : "FALSE");
- // FIXME - shouldn't be able to read from a directory
+ if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ goto exit;
+ }
if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) {
WARN("tried to read locked region\n");
TRACE("FileObject %p fcb %p FileSize = %llx st_size = %llx (%p)\n", FileObject, fcb, fcb->Header.FileSize.QuadPart, fcb->inode_item.st_size, &fcb->inode_item.st_size);
// int3;
- if (length + start > fcb->Header.ValidDataLength.QuadPart) {
- RtlZeroMemory(data + (fcb->Header.ValidDataLength.QuadPart - start), length - (fcb->Header.ValidDataLength.QuadPart - start));
- length = fcb->Header.ValidDataLength.QuadPart - start;
+ if (Irp->Flags & IRP_NOCACHE || !(IrpSp->MinorFunction & IRP_MN_MDL)) {
+ data = map_user_buffer(Irp);
+
+ if (Irp->MdlAddress && !data) {
+ ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ if (length + start > fcb->Header.ValidDataLength.QuadPart) {
+ RtlZeroMemory(data + (fcb->Header.ValidDataLength.QuadPart - start), length - (fcb->Header.ValidDataLength.QuadPart - start));
+ length = fcb->Header.ValidDataLength.QuadPart - start;
+ }
}
if (!(Irp->Flags & IRP_NOCACHE)) {
Status = STATUS_SUCCESS;
-// try {
+ try {
if (!FileObject->PrivateCacheMap) {
CC_FILE_SIZES ccfs;
// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
wait = TRUE;
- TRACE("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus);
- TRACE("sizes = %llx, %llx, %llx\n", fcb->Header.AllocationSize, fcb->Header.FileSize, fcb->Header.ValidDataLength);
- if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) {
- TRACE("CcCopyRead failed\n");
-
- IoMarkIrpPending(Irp);
- Status = STATUS_PENDING;
- goto exit;
+ if (IrpSp->MinorFunction & IRP_MN_MDL) {
+ CcMdlRead(FileObject,&IrpSp->Parameters.Read.ByteOffset, length, &Irp->MdlAddress, &Irp->IoStatus);
+ } else {
+ TRACE("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus);
+ TRACE("sizes = %llx, %llx, %llx\n", fcb->Header.AllocationSize, fcb->Header.FileSize, fcb->Header.ValidDataLength);
+ if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) {
+ TRACE("CcCopyRead failed\n");
+
+ IoMarkIrpPending(Irp);
+ Status = STATUS_PENDING;
+ goto exit;
+ }
+ TRACE("CcCopyRead finished\n");
}
- TRACE("CcCopyRead finished\n");
-// } except (EXCEPTION_EXECUTE_HANDLER) {
-// Status = GetExceptionCode();
-// }
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
if (NT_SUCCESS(Status)) {
Status = Irp->IoStatus.Status;
if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
FileObject->CurrentByteOffset.QuadPart = start + (NT_SUCCESS(Status) ? bytes_read : 0);
+ // fastfat doesn't do this, but the Wine ntdll file test seems to think we ought to
+ if (Irp->UserIosb)
+ *Irp->UserIosb = Irp->IoStatus;
+
TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
TRACE("returning %08x\n", Status);
#include "btrfs_drv.h"
-BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject) {
+NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
+ USHORT subnamelen, printnamelen, i;
+ ULONG stringlen;
+ DWORD reqlen;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ fcb* fcb = FileObject->FsContext;
+ char* data;
NTSTATUS Status;
- ULONG len, stringlen;
- USHORT newlen;
- OBJECT_NAME_INFORMATION* oni;
- UINT8* data;
- UINT32 i;
- BOOL success = FALSE;
- WCHAR* utf16;
- UNICODE_STRING abspath;
-
- if (fcb->inode_item.st_size == 0) {
- WARN("can't follow symlink with no data\n");
- return FALSE;
- }
- Status = ObQueryNameString(FileObject->DeviceObject, NULL, 0, &len);
+ // FIXME - check permissions
- if (Status != STATUS_INFO_LENGTH_MISMATCH) {
- ERR("ObQueryNameString 1 returned %08x\n", Status);
- return FALSE;
- }
-
- oni = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
- if (!oni) {
- ERR("out of memory\n");
- goto end;
- }
-
- Status = ObQueryNameString(FileObject->DeviceObject, oni, len, &len);
-
- if (!NT_SUCCESS(Status)) {
- ERR("ObQueryNameString 2 returned %08x\n", Status);
- ExFreePool(oni);
- return FALSE;
- }
-
- data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
- if (!data) {
- ERR("out of memory\n");
- goto end;
- }
-
- Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, 0, fcb->inode_item.st_size, NULL);
-
- if (!NT_SUCCESS(Status)) {
- ERR("read_file returned %08x\n", Status);
- goto end;
- }
-
- for (i = 0; i < fcb->inode_item.st_size; i++) {
- if (data[i] == '/')
- data[i] = '\\';
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)data, fcb->inode_item.st_size);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- goto end;
- }
+ TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
- utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
- if (!utf16) {
- ERR("out of memory\n");
- goto end;
- }
-
- Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, (char*)data, fcb->inode_item.st_size);
-
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(utf16);
- goto end;
- }
+ acquire_tree_lock(fcb->Vcb, FALSE);
- if (utf16[0] == '\\') { // absolute path
- abspath.MaximumLength = abspath.Length = stringlen;
- abspath.Buffer = utf16;
- } else { // relative path
- abspath.MaximumLength = fcb->par->full_filename.Length + sizeof(WCHAR) + stringlen;
- abspath.Buffer = ExAllocatePoolWithTag(PagedPool, abspath.MaximumLength, ALLOC_TAG);
- if (!abspath.Buffer) {
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
+ if (!data) {
ERR("out of memory\n");
- ExFreePool(utf16);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
- RtlCopyMemory(abspath.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
- abspath.Length = fcb->par->full_filename.Length;
+ TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
+ Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
- if (fcb->par->par) {
- abspath.Buffer[abspath.Length / sizeof(WCHAR)] = '\\';
- abspath.Length += sizeof(WCHAR);
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
}
- RtlCopyMemory(&abspath.Buffer[abspath.Length / sizeof(WCHAR)], utf16, stringlen);
- abspath.Length += stringlen;
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
- ExFreePool(utf16);
- }
-
- Status = FsRtlRemoveDotsFromPath(abspath.Buffer, abspath.Length, &newlen);
- if (!NT_SUCCESS(Status)) {
- ERR("FsRtlRemoveDotsFromPath returned %08x\n", Status);
- ExFreePool(abspath.Buffer);
- goto end;
- }
-
- abspath.Length = newlen;
-
- TRACE("abspath = %.*S\n", abspath.Length / sizeof(WCHAR), abspath.Buffer);
-
- TRACE("filename was %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
-
- if (FileObject->FileName.MaximumLength < oni->Name.Length + abspath.Length) {
- ExFreePool(FileObject->FileName.Buffer);
- FileObject->FileName.MaximumLength = oni->Name.Length + abspath.Length;
- FileObject->FileName.Buffer = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.MaximumLength, ALLOC_TAG);
+ subnamelen = stringlen;
+ printnamelen = stringlen;
- if (!FileObject->FileName.Buffer) {
- ERR("out of memory\n");
- ExFreePool(abspath.Buffer);
+ reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
+
+ if (buflen < reqlen) {
+ Status = STATUS_BUFFER_OVERFLOW;
goto end;
}
- }
-
- RtlCopyMemory(FileObject->FileName.Buffer, oni->Name.Buffer, oni->Name.Length);
- RtlCopyMemory(&FileObject->FileName.Buffer[oni->Name.Length / sizeof(WCHAR)], abspath.Buffer, abspath.Length);
- FileObject->FileName.Length = oni->Name.Length + abspath.Length;
-
- TRACE("filename now %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
-
- ExFreePool(abspath.Buffer);
-
- success = TRUE;
-end:
- ExFreePool(data);
- ExFreePool(oni);
-
- return success;
-}
-
-NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
- USHORT subnamelen, printnamelen, i;
- ULONG stringlen;
- DWORD reqlen;
- REPARSE_DATA_BUFFER* rdb = buffer;
- fcb* fcb = FileObject->FsContext;
- char* data;
- NTSTATUS Status;
-
- TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
-
- acquire_tree_lock(fcb->Vcb, FALSE);
-
- if (fcb->type != BTRFS_TYPE_SYMLINK) {
- Status = STATUS_NOT_A_REPARSE_POINT;
- goto end;
- }
-
- data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
- if (!data) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
- Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
-
- if (!NT_SUCCESS(Status)) {
- ERR("read_file returned %08x\n", Status);
- ExFreePool(data);
- goto end;
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- ExFreePool(data);
- goto end;
- }
-
- subnamelen = stringlen;
- printnamelen = stringlen;
-
- reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
-
- if (buflen < reqlen) {
- Status = STATUS_BUFFER_OVERFLOW;
- goto end;
- }
-
- rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
- rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
- rdb->Reserved = 0;
-
- rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
- rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
- rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
- rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
- rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
-
- Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
- stringlen, &stringlen, data, fcb->inode_item.st_size);
+
+ rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+ rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+ rdb->Reserved = 0;
+
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
+ rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
+ rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+
+ Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ stringlen, &stringlen, data, fcb->inode_item.st_size);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
+
+ for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
+ if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
+ rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
+ }
+
+ RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
+ &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
+
+ *retlen = reqlen;
+
ExFreePool(data);
- goto end;
- }
-
- for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
- if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
- rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
+
+ Status = STATUS_SUCCESS;
+ } else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+ if (fcb->type == BTRFS_TYPE_FILE) {
+ Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, buffer, 0, buflen, retlen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("read_file returned %08x\n", Status);
+ }
+ } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ UINT8* data;
+ UINT16 datalen;
+
+ if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen)) {
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ goto end;
+ }
+
+ if (!data) {
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ goto end;
+ }
+
+ if (datalen < sizeof(ULONG)) {
+ ExFreePool(data);
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ goto end;
+ }
+
+ if (buflen > 0) {
+ *retlen = min(buflen, datalen);
+ RtlCopyMemory(buffer, data, *retlen);
+ } else
+ *retlen = 0;
+
+ ExFreePool(data);
+ } else
+ Status = STATUS_NOT_A_REPARSE_POINT;
+ } else {
+ Status = STATUS_NOT_A_REPARSE_POINT;
}
- RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
- &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
- rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
-
- *retlen = reqlen;
-
- ExFreePool(data);
-
- Status = STATUS_SUCCESS;
-
end:
release_tree_lock(fcb->Vcb, FALSE);
if (!di) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
WARN("search for DIR_ITEM by crc32 failed\n");
}
- free_traverse_ptr(&tp);
-
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = index;
DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!di2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
}
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
-NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- PFILE_OBJECT FileObject = IrpSp->FileObject;
- void* buffer = Irp->AssociatedIrp.SystemBuffer;
- DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
- NTSTATUS Status = STATUS_SUCCESS;
- fcb* fcb;
- ULONG tag, minlen;
+static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ ULONG minlen;
UNICODE_STRING subname;
ANSI_STRING target;
- REPARSE_DATA_BUFFER* rdb = buffer;
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
LARGE_INTEGER offset;
USHORT i;
- LIST_ENTRY rollback;
-
- // FIXME - send notification if this succeeds? The attributes will have changed.
-
- TRACE("(%p, %p)\n", DeviceObject, Irp);
-
- InitializeListHead(&rollback);
-
- if (!FileObject) {
- ERR("FileObject was NULL\n");
- return STATUS_INVALID_PARAMETER;
- }
-
- fcb = FileObject->FsContext;
-
- ERR("filename: %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-
- acquire_tree_lock(fcb->Vcb, TRUE);
-
- if (fcb->type == BTRFS_TYPE_SYMLINK) {
- WARN("tried to set a reparse point on an existing symlink\n");
- Status = STATUS_INVALID_PARAMETER;
- goto end;
- }
-
- if (!fcb->utf8.Buffer) {
- ERR("error - utf8 on FCB not set\n");
- Status = STATUS_INTERNAL_ERROR;
- goto end;
- }
-
- // FIXME - die if not file
- // FIXME - die if ADS
-
- if (buflen < sizeof(ULONG)) {
- WARN("buffer was not long enough to hold tag\n");
- Status = STATUS_INVALID_PARAMETER;
- goto end;
- }
-
- RtlCopyMemory(&tag, buffer, sizeof(ULONG));
-
- if (tag != IO_REPARSE_TAG_SYMLINK) {
- FIXME("FIXME: handle arbitrary reparse tags (%08x)\n", tag);
- Status = STATUS_NOT_IMPLEMENTED;
- goto end;
- }
minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
if (buflen < minlen) {
WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
- Status = STATUS_INVALID_PARAMETER;
- goto end;
- }
-
- if (!(rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) {
- FIXME("FIXME: support non-relative symlinks\n");
- Status = STATUS_NOT_IMPLEMENTED;
+ return STATUS_INVALID_PARAMETER;
}
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- goto end;
+ return Status;
}
do {
utf8.Buffer = ir->name;
utf8.Length = utf8.MaximumLength = ir->n;
- Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
- goto end;
+ return Status;
}
if (size > sizeof(INODE_REF) - 1 + ir->n) {
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
}
} while (b);
- free_traverse_ptr(&tp);
-
if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_EXTREF;
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- goto end;
+ return Status;
}
do {
utf8.Buffer = ier->name;
utf8.Length = utf8.MaximumLength = ier->n;
- Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
- goto end;
+ return Status;
}
if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
}
} while (b);
-
- free_traverse_ptr(&tp);
}
fcb->inode_item.st_mode |= __S_IFLNK;
- Status = truncate_file(fcb, 0, &rollback);
+ Status = truncate_file(fcb, 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
- goto end;
+ return Status;
}
Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
- goto end;
+ return Status;
}
target.MaximumLength = target.Length;
target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
if (!target.Buffer) {
ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status);
- goto end;
+ ExFreePool(target.Buffer);
+ return Status;
}
for (i = 0; i < target.Length; i++) {
}
offset.QuadPart = 0;
- Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
+ Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, rollback);
ExFreePool(target.Buffer);
+ return Status;
+}
+
+static NTSTATUS delete_symlink(fcb* fcb, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_REF;
+ searchkey.offset = 0;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) {
+ if (tp.item->size < sizeof(INODE_REF)) {
+ WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
+ } else {
+ INODE_REF* ir;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ir = (INODE_REF*)tp.item->data;
+
+ do {
+ if (size < sizeof(INODE_REF) || size < sizeof(INODE_REF) - 1 + ir->n) {
+ WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ break;
+ }
+
+ utf8.Buffer = ir->name;
+ utf8.Length = utf8.MaximumLength = ir->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_FILE, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ return Status;
+ }
+
+ if (size > sizeof(INODE_REF) - 1 + ir->n) {
+ size -= sizeof(INODE_REF) - 1 + ir->n;
+
+ ir = (INODE_REF*)&ir->name[ir->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
+ }
+ } while (b);
+
+ if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_INODE_EXTREF;
+ searchkey.offset = 0;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ do {
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+ if (tp.item->size < sizeof(INODE_EXTREF)) {
+ WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_EXTREF));
+ } else {
+ INODE_EXTREF* ier;
+ ULONG size = tp.item->size;
+ ANSI_STRING utf8;
+
+ ier = (INODE_EXTREF*)tp.item->data;
+
+ do {
+ if (size < sizeof(INODE_EXTREF) || size < sizeof(INODE_EXTREF) - 1 + ier->n) {
+ WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ break;
+ }
+
+ utf8.Buffer = ier->name;
+ utf8.Length = utf8.MaximumLength = ier->n;
+
+ Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_FILE, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - change_file_type returned %08x\n", Status);
+ return Status;
+ }
+
+ if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
+ size -= sizeof(INODE_EXTREF) - 1 + ier->n;
+
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ } else
+ break;
+ } while (TRUE);
+ }
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+ if (b) {
+ tp = next_tp;
+
+ b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
+ }
+ } while (b);
+ }
+
+ Status = truncate_file(fcb, 0, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ return Status;
+ }
+
+ KeQuerySystemTime(&time);
+
+ win_time_to_unix(time, &now);
+
+ fcb->type = BTRFS_TYPE_FILE;
+ fcb->inode_item.st_mode &= ~__S_IFLNK;
+ fcb->inode_item.st_mode |= __S_IFREG;
+ fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+
+ Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_inode_item returned %08x\n", Status);
+ return Status;
+ }
+
+ fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+
+ return Status;
+}
+
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ void* buffer = Irp->AssociatedIrp.SystemBuffer;
+ REPARSE_DATA_BUFFER* rdb = buffer;
+ DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ NTSTATUS Status = STATUS_SUCCESS;
+ fcb* fcb;
+ ULONG tag;
+ LIST_ENTRY rollback;
+
+ // FIXME - send notification if this succeeds? The attributes will have changed.
+ // FIXME - check permissions
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ InitializeListHead(&rollback);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fcb = FileObject->FsContext;
+
+ TRACE("%S\n", file_desc(FileObject));
+
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ WARN("tried to set a reparse point on an existing symlink\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
+
+ // FIXME - die if not file or directory
+ // FIXME - die if ADS
+
+ if (buflen < sizeof(ULONG)) {
+ WARN("buffer was not long enough to hold tag\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ RtlCopyMemory(&tag, buffer, sizeof(ULONG));
+
+ if (fcb->type == BTRFS_TYPE_FILE && tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) {
+ Status = set_symlink(Irp, fcb, rdb, buflen, &rollback);
+ } else {
+ LARGE_INTEGER offset;
+ char val[64];
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY) { // for directories, store as xattr
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, buffer, buflen, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("set_xattr returned %08x\n", Status);
+ goto end;
+ }
+ } else { // otherwise, store as file data
+ Status = truncate_file(fcb, 0, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ goto end;
+ }
+
+ offset.QuadPart = 0;
+
+ Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("write_file2 returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ sprintf(val, "0x%lx", fcb->atts);
+
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("set_xattr returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ if (NT_SUCCESS(Status))
+ Status = consider_write(fcb->Vcb);
+
+end:
+ if (NT_SUCCESS(Status))
+ clear_rollback(&rollback);
+ else
+ do_rollback(fcb->Vcb, &rollback);
+
+ release_tree_lock(fcb->Vcb, TRUE);
+
+ return Status;
+}
+
+NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer;
+ DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ NTSTATUS Status;
+ fcb* fcb;
+ ccb* ccb;
+ file_ref* fileref;
+ LIST_ENTRY rollback;
+
+ // FIXME - send notification if this succeeds? The attributes will have changed.
+ // FIXME - check permissions
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ InitializeListHead(&rollback);
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fcb = FileObject->FsContext;
+ ccb = FileObject->FsContext2;
+ fileref = ccb ? ccb->fileref : NULL;
+
+ TRACE("%S\n", file_desc(FileObject));
+
+ if (!fileref) {
+ ERR("fileref was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
+ ERR("buffer was too short\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (rdb->ReparseDataLength > 0) {
+ WARN("rdb->ReparseDataLength was not zero\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ acquire_tree_lock(fcb->Vcb, TRUE);
+
+ if (fcb->ads) {
+ WARN("tried to delete reparse point on ADS\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (fcb->type == BTRFS_TYPE_SYMLINK) {
+ if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ Status = delete_symlink(fcb, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_symlink returned %08x\n", Status);
+ goto end;
+ }
+ } else if (fcb->type == BTRFS_TYPE_FILE) {
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ ULONG defda;
+
+ // FIXME - do we need to check that the reparse tags match?
+
+ Status = truncate_file(fcb, 0, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+ defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+
+ if (defda != fcb->atts) {
+ char val[64];
+
+ sprintf(val, "0x%lx", fcb->atts);
+
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("set_xattr returned %08x\n", Status);
+ goto end;
+ }
+ } else
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
+
+ KeQuerySystemTime(&time);
+
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+
+ Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_inode_item returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+ } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+ ULONG defda;
+
+ // FIXME - do we need to check that the reparse tags match?
+
+ fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+ defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+ defda |= FILE_ATTRIBUTE_DIRECTORY;
+
+ if (defda != fcb->atts) {
+ char val[64];
+
+ sprintf(val, "0x%lx", fcb->atts);
+
+ Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("set_xattr returned %08x\n", Status);
+ goto end;
+ }
+ } else
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
+
+ delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &rollback);
+
+ KeQuerySystemTime(&time);
+
+ win_time_to_unix(time, &now);
+
+ fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+ fcb->inode_item.sequence++;
+ fcb->inode_item.st_ctime = now;
+ fcb->inode_item.st_mtime = now;
+
+ Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_inode_item returned %08x\n", Status);
+ goto end;
+ }
+
+ fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+ fcb->subvol->root_item.ctime = now;
+ } else {
+ ERR("unsupported file type %u\n", fcb->type);
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ Status = STATUS_SUCCESS;
+
if (NT_SUCCESS(Status))
Status = consider_write(fcb->Vcb);
*sid = sh;
}
-static UINT32 sid_to_uid(PSID sid) {
+UINT32 sid_to_uid(PSID sid) {
LIST_ENTRY* le;
uid_map* um;
sid_header* sh = sid;
return TRUE;
}
-void fcb_get_sd(fcb* fcb) {
+void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
NTSTATUS Status;
SECURITY_DESCRIPTOR sd;
ULONG buflen;
}
// }
- if (!fcb->par)
+ if (!parent)
acl = load_default_acl();
else
- acl = inherit_acl(fcb->par->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
+ acl = inherit_acl(parent->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
if (!acl) {
ERR("out of memory\n");
static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
-
- if (fcb->ads)
- fcb = fcb->par;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
+
+ if (fcb->ads) {
+ if (fileref && fileref->parent)
+ fcb = fileref->parent->fcb;
+ else {
+ ERR("could not get parent fcb for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
// TRACE("buflen = %u, fcb->sdlen = %u\n", *buflen, fcb->sdlen);
static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, SECURITY_INFORMATION flags) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
SECURITY_DESCRIPTOR* oldsd;
INODE_ITEM* ii;
KEY searchkey;
acquire_tree_lock(Vcb, TRUE);
- if (fcb->ads)
- fcb = fcb->par;
+ if (fcb->ads) {
+ if (fileref && fileref->parent)
+ fcb = fileref->parent->fcb;
+ else {
+ ERR("could not find parent fcb for stream\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
if (keycmp(&tp.item->key, &searchkey)) {
ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
Status = STATUS_INTERNAL_ERROR;
- free_traverse_ptr(&tp);
goto end;
}
delete_tree_item(Vcb, &tp, &rollback);
- free_traverse_ptr(&tp);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
return Status;
}
-NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as) {
+NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as) {
NTSTATUS Status;
PSID owner;
BOOLEAN defaulted;
- Status = SeAssignSecurity(fcb->par ? fcb->par->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
+ Status = SeAssignSecurity((fileref && fileref->parent) ? fileref->parent->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
&as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
return STATUS_MORE_PROCESSING_REQUIRED;
}
-static NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
+NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
CHUNK_ITEM* ci;
CHUNK_ITEM_STRIPE* cis;
read_tree_context* context;
// FIXME - if checksum error, write good data over bad
+ // check if any of the devices return a "user-induced" error
+
+ for (i = 0; i < ci->num_stripes; i++) {
+ if (context->stripes[i].status == ReadTreeStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
+ IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
+
+ Status = context->stripes[i].iosb.Status;
+ goto exit;
+ }
+ }
+
// check if any of the stripes succeeded
for (i = 0; i < ci->num_stripes; i++) {
RtlCopyMemory(&t->header, th, sizeof(tree_header));
// t->address = addr;
// t->level = th->level;
- t->refcount = 1;
t->has_address = TRUE;
t->Vcb = Vcb;
t->parent = NULL;
t->size = 0;
t->new_address = 0;
t->has_new_address = FALSE;
-#ifdef DEBUG_TREE_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, file, line, "loaded tree %p (%llx)\n", t, addr);
-#else
- _debug_message(func, "loaded tree %p (%llx)\n", t, addr);
-#endif
-#endif
+ t->write = FALSE;
c = get_chunk_from_address(Vcb, addr);
}
static tree* free_tree2(tree* t, const char* func, const char* file, unsigned int line) {
- LONG rc;
LIST_ENTRY* le;
tree_data* td;
tree* par;
-
-#ifdef DEBUG_TREE_REFCOUNTS
- TRACE("(%p)\n", t);
-#endif
+ root* r = t->root;
par = t->parent;
// if (par) ExAcquireResourceExclusiveLite(&par->nonpaged->load_tree_lock, TRUE);
- rc = InterlockedDecrement(&t->refcount);
-
-#ifdef DEBUG_TREE_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, file, line, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
-#else
- _debug_message(func, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
-#endif
-#endif
-
- if (rc < 0) {
- ERR("error - negative refcount (%i)\n", rc);
- int3;
- }
+ if (r && r->treeholder.tree != t)
+ r = NULL;
- if (rc == 0) {
- root* r = t->root;
- if (r && r->treeholder.tree != t)
- r = NULL;
-
// if (r) {
// FsRtlEnterFileSystem();
// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
// }
+
+ if (par) {
+ if (t->paritem)
+ t->paritem->treeholder.tree = NULL;
- if (par) {
- if (t->paritem)
- t->paritem->treeholder.tree = NULL;
-
// ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
- }
-
- if (t->parent)
- t->parent = free_tree2(t->parent, func, file, line);
-
+ }
+
// ExDeleteResourceLite(&t->nonpaged->load_tree_lock);
-
+
// ExFreePool(t->nonpaged);
+
+ while (!IsListEmpty(&t->itemlist)) {
+ le = RemoveHeadList(&t->itemlist);
+ td = CONTAINING_RECORD(le, tree_data, list_entry);
- while (!IsListEmpty(&t->itemlist)) {
- le = RemoveHeadList(&t->itemlist);
- td = CONTAINING_RECORD(le, tree_data, list_entry);
+ if (t->header.level == 0 && td->data)
+ ExFreePool(td->data);
- if (t->header.level == 0 && td->data)
- ExFreePool(td->data);
-
- ExFreePool(td);
- }
-
- InterlockedDecrement(&t->Vcb->open_trees);
- RemoveEntryList(&t->list_entry);
-
- if (r) {
- r->treeholder.tree = NULL;
+ ExFreePool(td);
+ }
+
+ InterlockedDecrement(&t->Vcb->open_trees);
+ RemoveEntryList(&t->list_entry);
+
+ if (r) {
+ r->treeholder.tree = NULL;
// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
// FsRtlExitFileSystem();
- }
-
- ExFreePool(t);
-
- return NULL;
- } else {
-// if (par) ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
}
- return t;
+ ExFreePool(t);
+
+ return NULL;
}
NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line) {
th->tree->paritem = td;
ret = TRUE;
- } else {
- _increase_tree_rc(th->tree, func, file, line);
-
+ } else
ret = FALSE;
- }
// KeReleaseSpinLock(&thnp->spin_lock, irql);
oldtp.tree = t;
oldtp.item = td;
- _increase_tree_rc(t, func, file, line);
while (_find_prev_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
- free_traverse_ptr(&oldtp);
oldtp = *tp;
}
oldtp.tree = t;
oldtp.item = td;
- _increase_tree_rc(t, func, file, line);
while (_find_next_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
- free_traverse_ptr(&oldtp);
oldtp = *tp;
}
return STATUS_INTERNAL_ERROR;
} else {
tp->tree = t;
- _increase_tree_rc(t, func, file, line);
tp->item = td;
-
- add_to_tree_cache(Vcb, t, FALSE);
}
return STATUS_SUCCESS;
return Status;
}
- if (loaded)
- _increase_tree_rc(t, func, file, line);
-
Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
- td->treeholder.tree = _free_tree(td->treeholder.tree, func, file, line);
- TRACE("tree now %p\n", td->treeholder.tree);
-
return Status;
}
}
TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
- Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
- if (!NT_SUCCESS(Status)) {
- ERR("do_load_tree returned %08x\n", Status);
- return Status;
+ if (!r->treeholder.tree) {
+ Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_load_tree returned %08x\n", Status);
+ return Status;
+ }
}
Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("find_item_in_tree returned %08x\n", Status);
}
-
- _free_tree(r->treeholder.tree, func, file, line);
// #ifdef DEBUG_PARANOID
// if (b && !ignore && tp->item->ignore) {
return Status;
}
-void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line) {
- if (tp->tree) {
- tp->tree = free_tree2(tp->tree, func, file, line);
- }
-}
-
BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
tree* t;
tree_data *td, *next;
if (next) {
next_tp->tree = tp->tree;
- _increase_tree_rc(next_tp->tree, func, file, line);
next_tp->item = next;
#ifdef DEBUG_PARANOID
return FALSE;
}
- if (loaded)
- _increase_tree_rc(t->parent, func, file, line);
-
t = td->treeholder.tree;
while (t->header.level != 0) {
BOOL b;
while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
- _free_traverse_ptr(next_tp, func, file, line);
*next_tp = ntp2;
if (!next_tp->item->ignore)
break;
}
- if (!b) {
- _free_traverse_ptr(next_tp, func, file, line);
+ if (!b)
return FALSE;
- }
}
- add_to_tree_cache(Vcb, t, FALSE);
-
#ifdef DEBUG_PARANOID
if (!ignore && next_tp->item->ignore) {
ERR("error - returning ignored item\n");
// FIXME - support ignore flag
if (prev_item(tp->tree, tp->item)) {
prev_tp->tree = tp->tree;
- _increase_tree_rc(prev_tp->tree, func, file, line);
prev_tp->item = prev_item(tp->tree, tp->item);
return TRUE;
return FALSE;
}
- if (loaded)
- _increase_tree_rc(t->parent, func, file, line);
-
t = td->treeholder.tree;
while (t->header.level != 0) {
t = li->treeholder.tree;
}
- add_to_tree_cache(Vcb, t, FALSE);
-
prev_tp->tree = t;
prev_tp->item = last_item(t);
// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
// }
-void STDCALL free_tree_cache(LIST_ENTRY* tc) {
+void free_trees_root(device_extension* Vcb, root* r) {
LIST_ENTRY* le;
- tree_cache* tc2;
- root* r;
-
- while (tc->Flink != tc) {
- le = tc->Flink;
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
- r = tc2->tree->root;
+ UINT8 level;
+
+ for (level = 0; level <= 255; level++) {
+ BOOL empty = TRUE;
- ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
+ le = Vcb->trees.Flink;
- while (le != tc) {
+ while (le != &Vcb->trees) {
LIST_ENTRY* nextle = le->Flink;
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->tree->root == r) {
- tree* nt;
- BOOL top = !tc2->tree->paritem;
+ if (t->root == r && t->header.level == level) {
+ BOOL top = !t->paritem;
- nt = free_tree2(tc2->tree, funcname, __FILE__, __LINE__);
- if (top && !nt && r->treeholder.tree == tc2->tree)
+ empty = FALSE;
+
+ free_tree2(t, funcname, __FILE__, __LINE__);
+ if (top && r->treeholder.tree == t)
r->treeholder.tree = NULL;
- RemoveEntryList(&tc2->list_entry);
- ExFreePool(tc2);
+ if (IsListEmpty(&Vcb->trees))
+ return;
}
le = nextle;
}
- ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
+ if (empty)
+ break;
}
}
-void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write) {
- LIST_ENTRY* le;
- tree_cache* tc2;
-
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+void STDCALL free_trees(device_extension* Vcb) {
+ tree* t;
+ root* r;
+
+ while (!IsListEmpty(&Vcb->trees)) {
+ t = CONTAINING_RECORD(Vcb->trees.Flink, tree, list_entry);
+ r = t->root;
- if (tc2->tree == t) {
- if (write && !tc2->write) {
- Vcb->write_trees++;
- tc2->write = TRUE;
- }
- return;
- }
+ ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
- le = le->Flink;
- }
-
- tc2 = ExAllocatePoolWithTag(PagedPool, sizeof(tree_cache), ALLOC_TAG);
- if (!tc2) {
- ERR("out of memory\n");
- return;
+ free_trees_root(Vcb, r);
+
+ ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
}
-
- TRACE("adding %p to tree cache\n", t);
-
- tc2->tree = t;
- tc2->write = write;
- increase_tree_rc(t);
- InsertTailList(&Vcb->tree_cache, &tc2->list_entry);
-
-// print_trees(tc);
}
static void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
- free_traverse_ptr(&tp);
goto end;
}
} else
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
if (!td) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
goto end;
}
// ERR("tree %p, num_items now %x\n", tp.tree, tp.tree->header.num_items);
// ERR("size now %x\n", tp.tree->size);
- add_to_tree_cache(Vcb, tp.tree, TRUE);
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
- if (!ptp)
- free_traverse_ptr(&tp);
- else
+ if (ptp)
*ptp = tp;
t = tp.tree;
tp->item->ignore = TRUE;
- add_to_tree_cache(Vcb, tp->tree, TRUE);
+ if (!tp->tree->write) {
+ tp->tree->write = TRUE;
+ Vcb->write_trees++;
+ }
tp->tree->header.num_items--;
CHUNK_ITEM_STRIPE stripes[1];
} CHUNK_ITEM2;
-typedef struct {
- LIST_ENTRY list_entry;
- UINT64 key;
-} ordered_list;
-
-typedef struct {
- ordered_list ol;
- ULONG length;
- UINT32* checksums;
- BOOL deleted;
-} changed_sector;
-
-static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
-static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
+static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data);
static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
write_context* context = conptr;
if (s->offset >= offset && s->offset + s->size <= offset + size) { // delete entirely
#ifndef __REACTOS__
RemoveEntryList(&s->list_entry);
-
#endif
+
if (s->offset + s->size == offset + size) {
insbef = s->list_entry.Flink;
RemoveEntryList(&s->list_entry);
} else if (s->offset + s->size > offset && s->offset + s->size <= offset + size) { // truncate before
s->size = offset - s->offset;
} else if (s->offset < offset + size && s->offset + s->size > offset + size) { // truncate after
- s->size -= s->offset - offset + size;
+ s->size -= offset + size - s->offset;
s->offset = offset + size;
insbef = le;
} else {
CHUNK_ITEM* ci = (CHUNK_ITEM*)tp.item->data;
- if (tp.item->key.offset >= lastaddr + size) {
- free_traverse_ptr(&tp);
+ if (tp.item->key.offset >= lastaddr + size)
return lastaddr;
- }
lastaddr = tp.item->key.offset + ci->size;
}
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
}
} while (b);
- free_traverse_ptr(&tp);
-
return lastaddr;
}
if (keycmp(&tp.item->key, &searchkey)) {
ERR("error - could not find DEV_ITEM for device %llx\n", device->devitem.dev_id);
- free_traverse_ptr(&tp);
return FALSE;
}
delete_tree_item(Vcb, &tp, rollback);
- free_traverse_ptr(&tp);
-
device->devitem.bytes_used += size;
di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
return STATUS_SUCCESS;
}
-static chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
UINT64 max_stripe_size, max_chunk_size, stripe_size;
UINT64 total_size = 0, i, j, logaddr;
int num_stripes;
c->offset = logaddr;
c->used = c->oldused = 0;
c->space_changed = FALSE;
+ c->cache_size = 0;
+ c->cache_inode = 0;
InitializeListHead(&c->space);
s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
return success ? c : NULL;
}
-static void decrease_chunk_usage(chunk* c, UINT64 delta) {
- c->used -= delta;
-
- TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
-}
-
-static void increase_chunk_usage(chunk* c, UINT64 delta) {
- c->used += delta;
-
- TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
-}
-
-static NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length) {
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length) {
KEY searchkey;
traverse_ptr tp;
CHUNK_ITEM2* ci;
}
end:
- free_traverse_ptr(&tp);
return Status;
}
}
}
-static BOOL trees_consistent(device_extension* Vcb) {
+static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) {
ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header);
LIST_ENTRY* le;
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write) {
- if (tc2->tree->header.num_items == 0 && tc2->tree->parent)
+ if (t->write) {
+ if (t->header.num_items == 0 && t->parent)
return FALSE;
- if (tc2->tree->size > maxsize)
+ if (t->size > maxsize)
return FALSE;
- if (!tc2->tree->has_new_address)
+ if (!t->has_new_address)
return FALSE;
}
LIST_ENTRY* le;
NTSTATUS Status;
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write) {
- if (tc2->tree->parent)
- add_to_tree_cache(Vcb, tc2->tree->parent, TRUE);
- else if (tc2->tree->root != Vcb->chunk_root && tc2->tree->root != Vcb->root_root) {
+ if (t->write) {
+ if (t->parent) {
+ if (!t->parent->write) {
+ t->parent->write = TRUE;
+ Vcb->write_trees++;
+ }
+ } else if (t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
KEY searchkey;
traverse_ptr tp;
- searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_id = t->root->id;
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
delete_tree_item(Vcb, &tp, rollback);
- if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
} else {
- add_to_tree_cache(Vcb, tp.tree, TRUE);
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
}
-
- free_traverse_ptr(&tp);
}
}
return STATUS_SUCCESS;
}
-void print_trees(LIST_ENTRY* tc) {
- LIST_ENTRY *le, *le2;
-
- le = tc->Flink;
- while (le != tc) {
- KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
- UINT32 num_items = 0;
-
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
- tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
- if (!td->ignore) {
- firstitem = td->key;
- num_items++;
- }
- le2 = le2->Flink;
- }
-
- ERR("tree: root %llx, first key %llx,%x,%llx, level %x, num_items %x / %x\n",
- tc2->tree->header.tree_id, firstitem.obj_id, firstitem.obj_type, firstitem.offset, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
-
- le = le->Flink;
- }
-}
-
static void add_parents_to_cache(device_extension* Vcb, tree* t) {
KEY searchkey;
traverse_ptr tp;
while (t->parent) {
t = t->parent;
- add_to_tree_cache(Vcb, t, TRUE);
+ if (!t->write) {
+ t->write = TRUE;
+ Vcb->write_trees++;
+ }
}
if (t->root == Vcb->root_root || t->root == Vcb->chunk_root)
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
- free_traverse_ptr(&tp);
return;
}
- add_to_tree_cache(Vcb, tp.tree, TRUE);
-
- free_traverse_ptr(&tp);
+ if (!tp.tree->write) {
+ tp.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
}
-static BOOL insert_tree_extent_skinny(device_extension* Vcb, tree* t, chunk* c, UINT64 address, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, LIST_ENTRY* rollback) {
EXTENT_ITEM_SKINNY_METADATA* eism;
traverse_ptr insert_tp;
eism->ei.generation = Vcb->superblock.generation;
eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
eism->type = TYPE_TREE_BLOCK_REF;
- eism->tbr.offset = t->header.tree_id;
+ eism->tbr.offset = root_id;
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, t->header.level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
+ if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eism);
return FALSE;
add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
-// add_to_tree_cache(tc, insert_tp.tree, TRUE);
add_parents_to_cache(Vcb, insert_tp.tree);
- free_traverse_ptr(&insert_tp);
-
- t->new_address = address;
- t->has_new_address = TRUE;
-
return TRUE;
}
-static BOOL insert_tree_extent(device_extension* Vcb, tree* t, chunk* c, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, LIST_ENTRY* rollback) {
UINT64 address;
EXTENT_ITEM_TREE2* eit2;
traverse_ptr insert_tp;
- TRACE("(%p, %p, %p, %p)\n", Vcb, t, c, rollback);
+ TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb, level, root_id, c, new_address, rollback);
if (!find_address_in_chunk(Vcb, c, Vcb->superblock.node_size, &address))
return FALSE;
- if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
- return insert_tree_extent_skinny(Vcb, t, c, address, rollback);
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+ BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, rollback);
+
+ if (b)
+ *new_address = address;
+
+ return b;
+ }
eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
if (!eit2) {
eit2->eit.extent_item.generation = Vcb->superblock.generation;
eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
// eit2->eit.firstitem = wt->firstitem;
- eit2->eit.level = t->header.level;
+ eit2->eit.level = level;
eit2->type = TYPE_TREE_BLOCK_REF;
- eit2->tbr.offset = t->header.tree_id;
+ eit2->tbr.offset = root_id;
// #ifdef DEBUG_PARANOID
// if (wt->firstitem.obj_type == 0xcc) { // TESTING
add_to_space_list(c, address, Vcb->superblock.node_size, SPACE_TYPE_WRITING);
-// add_to_tree_cache(tc, insert_tp.tree, TRUE);
add_parents_to_cache(Vcb, insert_tp.tree);
- free_traverse_ptr(&insert_tp);
-
- t->new_address = address;
- t->has_new_address = TRUE;
+ *new_address = address;
return TRUE;
}
-static NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
chunk *origchunk = NULL, *c;
LIST_ENTRY* le;
- UINT64 flags = t->flags;
+ UINT64 flags = t->flags, addr;
if (flags == 0)
flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE;
if (t->has_address) {
origchunk = get_chunk_from_address(Vcb, t->header.address);
- if (insert_tree_extent(Vcb, t, origchunk, rollback))
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, rollback)) {
+ t->new_address = addr;
+ t->has_new_address = TRUE;
return STATUS_SUCCESS;
+ }
}
le = Vcb->chunks.Flink;
while (le != &Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
- // FIXME - make sure to avoid superblocks
-
if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
- if (insert_tree_extent(Vcb, t, c, rollback))
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
+ t->new_address = addr;
+ t->has_new_address = TRUE;
return STATUS_SUCCESS;
+ }
}
le = le->Flink;
// allocate new chunk if necessary
if ((c = alloc_chunk(Vcb, flags, rollback))) {
if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
- if (insert_tree_extent(Vcb, t, c, rollback))
+ if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
+ t->new_address = addr;
+ t->has_new_address = TRUE;
return STATUS_SUCCESS;
+ }
}
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
return FALSE;
}
if (tp.item->size < sizeof(EXTENT_ITEM_SKINNY_METADATA)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM_SKINNY_METADATA));
- free_traverse_ptr(&tp);
return FALSE;
}
delete_tree_item(Vcb, &tp, rollback);
eism = (EXTENT_ITEM_SKINNY_METADATA*)tp.item->data;
- if (t->header.level == 0 && eism->ei.flags & EXTENT_ITEM_SHARED_BACKREFS && eism->type == TYPE_TREE_BLOCK_REF) {
+ if (t && t->header.level == 0 && eism->ei.flags & EXTENT_ITEM_SHARED_BACKREFS && eism->type == TYPE_TREE_BLOCK_REF) {
// convert shared data extents
LIST_ENTRY* le = t->itemlist.Flink;
} else
ERR("could not find chunk for address %llx\n", address);
- free_traverse_ptr(&tp);
-
return TRUE;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey.obj_id);
- free_traverse_ptr(&tp);
return;
}
Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
return;
}
if (keycmp(&searchkey, &tp2.item->key)) {
ERR("could not find %llx,%x,%llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
if (tp.item->size < sizeof(EXTENT_REF_V0)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_REF_V0));
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
if (!eism) {
ERR("out of memory\n");
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
ERR("insert_tree_item failed\n");
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
} else {
if (!eit2) {
ERR("out of memory\n");
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) {
ERR("insert_tree_item failed\n");
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
return;
}
}
-// add_to_tree_cache(tc, insert_tp.tree, TRUE);
add_parents_to_cache(Vcb, insert_tp.tree);
add_parents_to_cache(Vcb, tp.tree);
add_parents_to_cache(Vcb, tp2.tree);
-
- free_traverse_ptr(&insert_tp);
-
- free_traverse_ptr(&tp2);
- free_traverse_ptr(&tp);
}
static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) {
if (keycmp(&tp.item->key, &searchkey)) {
ERR("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
int3;
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
if (eiv0->refcount > 1) {
FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0->refcount);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
} else {
if (tp.item->size < sizeof(EXTENT_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
if (ei->refcount > 1) {
FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei->refcount);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
- if (t->header.level == 0 && ei->flags & EXTENT_ITEM_SHARED_BACKREFS) {
+ if (t && t->header.level == 0 && ei->flags & EXTENT_ITEM_SHARED_BACKREFS) {
// convert shared data extents
LIST_ENTRY* le = t->itemlist.Flink;
Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
return Status;
}
if (tp2.item->key.obj_id == searchkey.obj_id && tp2.item->key.obj_type == searchkey.obj_type) {
delete_tree_item(Vcb, &tp2, rollback);
}
- free_traverse_ptr(&tp2);
}
- if (!(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
+ if (t && !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
LIST_ENTRY* le;
// when writing old internal trees, convert related extents
} else
ERR("could not find chunk for address %llx\n", address);
- free_traverse_ptr(&tp);
-
return STATUS_SUCCESS;
}
TRACE("(%p)\n", Vcb);
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write && !tc2->tree->has_new_address) {
+ if (t->write && !t->has_new_address) {
chunk* c;
- Status = get_tree_new_address(Vcb, tc2->tree, rollback);
+ Status = get_tree_new_address(Vcb, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("get_tree_new_address returned %08x\n", Status);
return Status;
}
- TRACE("allocated extent %llx\n", tc2->tree->new_address);
+ TRACE("allocated extent %llx\n", t->new_address);
- if (tc2->tree->has_address) {
- Status = reduce_tree_extent(Vcb, tc2->tree->header.address, tc2->tree, rollback);
+ if (t->has_address) {
+ Status = reduce_tree_extent(Vcb, t->header.address, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
}
}
- c = get_chunk_from_address(Vcb, tc2->tree->new_address);
+ c = get_chunk_from_address(Vcb, t->new_address);
if (c) {
increase_chunk_usage(c, Vcb->superblock.node_size);
} else {
- ERR("could not find chunk for address %llx\n", tc2->tree->new_address);
+ ERR("could not find chunk for address %llx\n", t->new_address);
return STATUS_INTERNAL_ERROR;
}
}
TRACE("(%p)\n", Vcb);
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write && !tc2->tree->parent) {
- if (tc2->tree->root != Vcb->root_root && tc2->tree->root != Vcb->chunk_root) {
+ if (t->write && !t->parent) {
+ if (t->root != Vcb->root_root && t->root != Vcb->chunk_root) {
KEY searchkey;
traverse_ptr tp;
- searchkey.obj_id = tc2->tree->root->id;
+ searchkey.obj_id = t->root->id;
searchkey.obj_type = TYPE_ROOT_ITEM;
searchkey.offset = 0xffffffffffffffff;
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
- TRACE("updating the address for root %llx to %llx\n", searchkey.obj_id, tc2->tree->new_address);
+ TRACE("updating the address for root %llx to %llx\n", searchkey.obj_id, t->new_address);
- tc2->tree->root->root_item.block_number = tc2->tree->new_address;
- tc2->tree->root->root_item.root_level = tc2->tree->header.level;
- tc2->tree->root->root_item.generation = Vcb->superblock.generation;
- tc2->tree->root->root_item.generation2 = Vcb->superblock.generation;
+ t->root->root_item.block_number = t->new_address;
+ t->root->root_item.root_level = t->header.level;
+ t->root->root_item.generation = Vcb->superblock.generation;
+ t->root->root_item.generation2 = Vcb->superblock.generation;
if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, delete and create new entry
ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(ri, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
+ RtlCopyMemory(ri, &t->root->root_item, sizeof(ROOT_ITEM));
delete_tree_item(Vcb, &tp, rollback);
return STATUS_INTERNAL_ERROR;
}
} else
- RtlCopyMemory(tp.item->data, &tc2->tree->root->root_item, sizeof(ROOT_ITEM));
-
- free_traverse_ptr(&tp);
+ RtlCopyMemory(tp.item->data, &t->root->root_item, sizeof(ROOT_ITEM));
}
- tc2->tree->root->treeholder.address = tc2->tree->new_address;
+ t->root->treeholder.address = t->new_address;
}
le = le->Flink;
}
+ Status = update_chunk_caches(Vcb, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("update_chunk_caches returned %08x\n", Status);
+ return Status;
+ }
+
return STATUS_SUCCESS;
}
-enum write_tree_status {
- WriteTreeStatus_Pending,
- WriteTreeStatus_Success,
- WriteTreeStatus_Error,
- WriteTreeStatus_Cancelling,
- WriteTreeStatus_Cancelled
-};
-
-struct write_tree_context;
-
-typedef struct {
- struct write_tree_context* context;
- UINT8* buf;
- device* device;
- PIRP Irp;
- IO_STATUS_BLOCK iosb;
- enum write_tree_status status;
- LIST_ENTRY list_entry;
-} write_tree_stripe;
-
-typedef struct {
- KEVENT Event;
- LIST_ENTRY stripes;
-} write_tree_context;
-
static NTSTATUS STDCALL write_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
write_tree_stripe* stripe = conptr;
write_tree_context* context = (write_tree_context*)stripe->context;
return STATUS_MORE_PROCESSING_REQUIRED;
}
-static NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc) {
+NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc) {
chunk* c;
CHUNK_ITEM_STRIPE* cis;
write_tree_stripe* stripe;
return STATUS_SUCCESS;
}
-static void free_stripes(write_tree_context* wtc) {
+void free_write_tree_stripes(write_tree_context* wtc) {
LIST_ENTRY *le, *le2, *nextle;
le = wtc->stripes.Flink;
TRACE("level = %u\n", level);
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write && tc2->tree->header.level == level) {
+ if (t->write && t->header.level == level) {
KEY firstitem, searchkey;
LIST_ENTRY* le2;
traverse_ptr tp;
EXTENT_ITEM_TREE* eit;
- if (!tc2->tree->has_new_address) {
+ if (!t->has_new_address) {
ERR("error - tried to write tree with no new address\n");
int3;
}
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore) {
firstitem = td->key;
le2 = le2->Flink;
}
- if (tc2->tree->parent) {
- tc2->tree->paritem->key = firstitem;
- tc2->tree->paritem->treeholder.address = tc2->tree->new_address;
- tc2->tree->paritem->treeholder.generation = Vcb->superblock.generation;
+ if (t->parent) {
+ t->paritem->key = firstitem;
+ t->paritem->treeholder.address = t->new_address;
+ t->paritem->treeholder.generation = Vcb->superblock.generation;
}
if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) {
- searchkey.obj_id = tc2->tree->new_address;
+ searchkey.obj_id = t->new_address;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = Vcb->superblock.node_size;
// tree_data* paritem;
ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- free_traverse_ptr(&tp);
// searchkey.obj_id = 0;
// searchkey.obj_type = 0;
if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM_TREE));
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
eit = (EXTENT_ITEM_TREE*)tp.item->data;
eit->firstitem = firstitem;
-
- free_traverse_ptr(&tp);
}
nothing_found = FALSE;
KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
InitializeListHead(&wtc->stripes);
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
#ifdef DEBUG_PARANOID
UINT32 num_items = 0, size = 0;
LIST_ENTRY* le2;
BOOL crash = FALSE;
#endif
- if (tc2->write) {
+ if (t->write) {
#ifdef DEBUG_PARANOID
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore) {
num_items++;
- if (tc2->tree->header.level == 0)
+ if (t->header.level == 0)
size += td->size;
}
le2 = le2->Flink;
}
- if (tc2->tree->header.level == 0)
+ if (t->header.level == 0)
size += num_items * sizeof(leaf_node);
else
size += num_items * sizeof(internal_node);
- if (num_items != tc2->tree->header.num_items) {
- ERR("tree %llx, level %x: num_items was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, num_items, tc2->tree->header.num_items);
+ if (num_items != t->header.num_items) {
+ ERR("tree %llx, level %x: num_items was %x, expected %x\n", t->root->id, t->header.level, num_items, t->header.num_items);
crash = TRUE;
}
- if (size != tc2->tree->size) {
- ERR("tree %llx, level %x: size was %x, expected %x\n", tc2->tree->root->id, tc2->tree->header.level, size, tc2->tree->size);
+ if (size != t->size) {
+ ERR("tree %llx, level %x: size was %x, expected %x\n", t->root->id, t->header.level, size, t->size);
crash = TRUE;
}
- if (tc2->tree->header.num_items == 0 && tc2->tree->parent) {
- ERR("tree %llx, level %x: tried to write empty tree with parent\n", tc2->tree->root->id, tc2->tree->header.level);
+ if (t->header.num_items == 0 && t->parent) {
+ ERR("tree %llx, level %x: tried to write empty tree with parent\n", t->root->id, t->header.level);
crash = TRUE;
}
- if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
- ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", tc2->tree->root->id, tc2->tree->header.level, tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+ if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", t->root->id, t->header.level, t->size, Vcb->superblock.node_size - sizeof(tree_header));
crash = TRUE;
}
if (crash) {
- ERR("tree %p\n", tc2->tree);
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ ERR("tree %p\n", t);
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore) {
ERR("%llx,%x,%llx inserted=%u\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->inserted);
int3;
}
#endif
- tc2->tree->header.address = tc2->tree->new_address;
- tc2->tree->header.generation = Vcb->superblock.generation;
- tc2->tree->header.flags |= HEADER_FLAG_MIXED_BACKREF;
- tc2->tree->has_address = TRUE;
+ t->header.address = t->new_address;
+ t->header.generation = Vcb->superblock.generation;
+ t->header.flags |= HEADER_FLAG_MIXED_BACKREF;
+ t->has_address = TRUE;
data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
if (!data) {
body = data + sizeof(tree_header);
- RtlCopyMemory(data, &tc2->tree->header, sizeof(tree_header));
+ RtlCopyMemory(data, &t->header, sizeof(tree_header));
RtlZeroMemory(body, Vcb->superblock.node_size - sizeof(tree_header));
- if (tc2->tree->header.level == 0) {
+ if (t->header.level == 0) {
leaf_node* itemptr = (leaf_node*)body;
int i = 0;
LIST_ENTRY* le2;
UINT8* dataptr = data + Vcb->superblock.node_size;
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore) {
dataptr = dataptr - td->size;
int i = 0;
LIST_ENTRY* le2;
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore) {
itemptr[i].key = td->key;
*((UINT32*)data) = crc32;
TRACE("setting crc32 to %08x\n", crc32);
- Status = write_tree(Vcb, tc2->tree->new_address, data, wtc);
+ Status = write_tree(Vcb, t->new_address, data, wtc);
if (!NT_SUCCESS(Status)) {
ERR("write_tree returned %08x\n", Status);
goto end;
le = le->Flink;
}
- free_stripes(wtc);
+ free_write_tree_stripes(wtc);
}
end:
return Status;
}
+static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb) {
+ KEY searchkey;
+ traverse_ptr tp;
+
+ RtlZeroMemory(sb, sizeof(superblock_backup));
+
+ sb->root_tree_addr = Vcb->superblock.root_tree_addr;
+ sb->root_tree_generation = Vcb->superblock.generation;
+ sb->root_level = Vcb->superblock.root_level;
+
+ sb->chunk_tree_addr = Vcb->superblock.chunk_tree_addr;
+ sb->chunk_tree_generation = Vcb->superblock.chunk_root_generation;
+ sb->chunk_root_level = Vcb->superblock.chunk_root_level;
+
+ searchkey.obj_id = BTRFS_ROOT_EXTENT;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
+
+ sb->extent_tree_addr = ri->block_number;
+ sb->extent_tree_generation = ri->generation;
+ sb->extent_root_level = ri->root_level;
+ }
+ }
+
+ searchkey.obj_id = BTRFS_ROOT_FSTREE;
+
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
+
+ sb->fs_tree_addr = ri->block_number;
+ sb->fs_tree_generation = ri->generation;
+ sb->fs_root_level = ri->root_level;
+ }
+ }
+
+ searchkey.obj_id = BTRFS_ROOT_DEVTREE;
+
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
+
+ sb->dev_root_addr = ri->block_number;
+ sb->dev_root_generation = ri->generation;
+ sb->dev_root_level = ri->root_level;
+ }
+ }
+
+ searchkey.obj_id = BTRFS_ROOT_CHECKSUM;
+
+ if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
+ ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
+
+ sb->csum_root_addr = ri->block_number;
+ sb->csum_root_generation = ri->generation;
+ sb->csum_root_level = ri->root_level;
+ }
+ }
+
+ sb->total_bytes = Vcb->superblock.total_bytes;
+ sb->bytes_used = Vcb->superblock.bytes_used;
+ sb->num_devices = Vcb->superblock.num_devices;
+}
+
static NTSTATUS write_superblocks(device_extension* Vcb) {
UINT64 i;
NTSTATUS Status;
TRACE("(%p)\n", Vcb);
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
-
- if (tc2->write && !tc2->tree->parent) {
- if (tc2->tree->root == Vcb->root_root) {
- Vcb->superblock.root_tree_addr = tc2->tree->new_address;
- Vcb->superblock.root_level = tc2->tree->header.level;
- } else if (tc2->tree->root == Vcb->chunk_root) {
- Vcb->superblock.chunk_tree_addr = tc2->tree->new_address;
- Vcb->superblock.chunk_root_generation = tc2->tree->header.generation;
- Vcb->superblock.chunk_root_level = tc2->tree->header.level;
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
+
+ if (t->write && !t->parent) {
+ if (t->root == Vcb->root_root) {
+ Vcb->superblock.root_tree_addr = t->new_address;
+ Vcb->superblock.root_level = t->header.level;
+ } else if (t->root == Vcb->chunk_root) {
+ Vcb->superblock.chunk_tree_addr = t->new_address;
+ Vcb->superblock.chunk_root_generation = t->header.generation;
+ Vcb->superblock.chunk_root_level = t->header.level;
}
}
le = le->Flink;
}
+ for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS - 1; i++) {
+ RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup));
+ }
+
+ update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1]);
+
for (i = 0; i < Vcb->superblock.num_devices; i++) {
if (Vcb->devices[i].devobj) {
Status = write_superblock(Vcb, &Vcb->devices[i]);
if (keycmp(&searchkey, &tp.item->key)) {
ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
int3;
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!bgi) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
FIXME("RAID0 not yet supported\n");
ExFreePool(bgi);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
} else if (c->chunk_item->type & BLOCK_FLAG_RAID1) {
FIXME("RAID1 not yet supported\n");
ExFreePool(bgi);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
} else if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE) {
Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + (2 * (c->used - c->oldused));
} else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
FIXME("RAID10 not yet supported\n");
ExFreePool(bgi);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
} else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
FIXME("RAID5 not yet supported\n");
ExFreePool(bgi);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
} else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
FIXME("RAID6 not yet supported\n");
ExFreePool(bgi);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
} else { // SINGLE
Vcb->superblock.bytes_used = Vcb->superblock.bytes_used + c->used - c->oldused;
}
TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
-
- free_traverse_ptr(&tp);
c->oldused = c->used;
}
nt->header.num_items = t->header.num_items - numitems;
nt->header.flags = HEADER_FLAG_MIXED_BACKREF;
- nt->refcount = 0;
nt->has_address = FALSE;
nt->Vcb = Vcb;
nt->parent = t->parent;
nt->size = t->size - size;
t->size = size;
t->header.num_items = numitems;
- add_to_tree_cache(Vcb, nt, TRUE);
+ nt->write = TRUE;
+ Vcb->write_trees++;
InterlockedIncrement(&Vcb->open_trees);
-#ifdef DEBUG_TREE_REFCOUNTS
- TRACE("created new split tree %p\n", nt);
-#endif
InsertTailList(&Vcb->trees, &nt->list_entry);
// // // TESTING
while (le != &nt->itemlist) {
tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
- if (td2->treeholder.tree) {
+ if (td2->treeholder.tree)
td2->treeholder.tree->parent = nt;
- increase_tree_rc(nt);
- free_tree(t);
- }
le = le->Flink;
}
}
if (nt->parent) {
- increase_tree_rc(nt->parent);
-
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
if (!td) {
ERR("out of memory\n");
pt->header.level = nt->header.level + 1;
pt->header.flags = HEADER_FLAG_MIXED_BACKREF;
- pt->refcount = 2;
pt->has_address = FALSE;
pt->Vcb = Vcb;
pt->parent = NULL;
// ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
InterlockedIncrement(&Vcb->open_trees);
-#ifdef DEBUG_TREE_REFCOUNTS
- TRACE("created new parent tree %p\n", pt);
-#endif
InsertTailList(&Vcb->trees, &pt->list_entry);
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
InsertTailList(&pt->itemlist, &td->list_entry);
nt->paritem = td;
- add_to_tree_cache(Vcb, pt, TRUE);
+ pt->write = TRUE;
+ Vcb->write_trees++;
t->root->treeholder.tree = pt;
return Status;
}
- if (loaded)
- increase_tree_rc(t->parent);
-
// ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
next_tree = nextparitem->treeholder.tree;
while (le != &next_tree->itemlist) {
tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
- if (td2->treeholder.tree) {
+ if (td2->treeholder.tree)
td2->treeholder.tree->parent = t;
- increase_tree_rc(t);
- free_tree(next_tree);
- }
le = le->Flink;
}
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
- free_tree(next_tree);
return Status;
}
} else if (next_tree->has_address) {
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
- free_tree(next_tree);
return Status;
}
}
par = next_tree->parent;
while (par) {
- add_to_tree_cache(Vcb, par, TRUE);
+ if (!par->write) {
+ par->write = TRUE;
+ Vcb->write_trees++;
+ }
par = par->parent;
}
next_tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
free_tree(next_tree);
-
- // remove next_tree from tree cache
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
-
- if (tc2->tree == next_tree) {
- free_tree(next_tree);
- RemoveEntryList(le);
- ExFreePool(tc2);
- break;
- }
-
- le = le->Flink;
- }
} else {
// rebalance by moving items from second tree into first
ULONG avg_size = (t->size + next_tree->size) / 2;
RemoveEntryList(&td->list_entry);
InsertTailList(&t->itemlist, &td->list_entry);
- if (next_tree->header.level > 0 && td->treeholder.tree) {
+ if (next_tree->header.level > 0 && td->treeholder.tree)
td->treeholder.tree->parent = t;
- increase_tree_rc(t);
- free_tree(next_tree);
- }
if (!td->ignore) {
next_tree->size -= size;
par = next_tree;
while (par) {
- add_to_tree_cache(Vcb, par, TRUE);
+ if (!par->write) {
+ par->write = TRUE;
+ Vcb->write_trees++;
+ }
par = par->parent;
}
-
- free_tree(next_tree);
}
return STATUS_SUCCESS;
if (!eism) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eism);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
- free_traverse_ptr(&tp);
return STATUS_SUCCESS;
}
-
- free_traverse_ptr(&tp);
}
searchkey.obj_id = address;
if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM_TREE));
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
if (!eit) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(eit);
- free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
-
- free_traverse_ptr(&tp);
return STATUS_SUCCESS;
}
ERR("could not find EXTENT_ITEM for address %llx\n", address);
- free_traverse_ptr(&tp);
-
return STATUS_INTERNAL_ERROR;
}
UINT32 min_size;
BOOL empty, done_deletions = FALSE;
NTSTATUS Status;
- tree_cache* tc2;
+ tree* t;
TRACE("(%p)\n", Vcb);
TRACE("doing level %u\n", level);
- le = Vcb->tree_cache.Flink;
+ le = Vcb->trees.Flink;
- while (le != &Vcb->tree_cache) {
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ while (le != &Vcb->trees) {
+ t = CONTAINING_RECORD(le, tree, list_entry);
nextle = le->Flink;
- if (tc2->write && tc2->tree->header.level == level) {
+ if (t->write && t->header.level == level) {
empty = FALSE;
- if (tc2->tree->header.num_items == 0) {
- if (tc2->tree->parent) {
+ if (t->header.num_items == 0) {
+ if (t->parent) {
LIST_ENTRY* le2;
KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
done_deletions = TRUE;
- le2 = tc2->tree->itemlist.Flink;
- while (le2 != &tc2->tree->itemlist) {
+ le2 = t->itemlist.Flink;
+ while (le2 != &t->itemlist) {
tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
firstitem = td->key;
break;
}
+
TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
- tc2->tree->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
+ t->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
- tc2->tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
+ t->root->root_item.bytes_used -= Vcb->superblock.node_size;
- if (tc2->tree->has_new_address) { // delete associated EXTENT_ITEM
- Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree, rollback);
+ if (t->has_new_address) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
return Status;
}
- } else if (tc2->tree->has_address) {
- Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree, rollback);
+ } else if (t->has_address) {
+ Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
}
}
- if (!tc2->tree->paritem->ignore) {
- tc2->tree->paritem->ignore = TRUE;
- tc2->tree->parent->header.num_items--;
- tc2->tree->parent->size -= sizeof(internal_node);
+ if (!t->paritem->ignore) {
+ t->paritem->ignore = TRUE;
+ t->parent->header.num_items--;
+ t->parent->size -= sizeof(internal_node);
}
- RemoveEntryList(&tc2->tree->paritem->list_entry);
- ExFreePool(tc2->tree->paritem);
- tc2->tree->paritem = NULL;
+ RemoveEntryList(&t->paritem->list_entry);
+ ExFreePool(t->paritem);
+ t->paritem = NULL;
- free_tree(tc2->tree);
-
- RemoveEntryList(le);
- ExFreePool(tc2);
- } else if (tc2->tree->header.level != 0) {
- if (tc2->tree->has_new_address) {
- Status = update_extent_level(Vcb, tc2->tree->new_address, tc2->tree, 0, rollback);
+ free_tree(t);
+ } else if (t->header.level != 0) {
+ if (t->has_new_address) {
+ Status = update_extent_level(Vcb, t->new_address, t, 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_extent_level returned %08x\n", Status);
}
}
- tc2->tree->header.level = 0;
+ t->header.level = 0;
}
- } else if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
- TRACE("splitting overlarge tree (%x > %x)\n", tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
- Status = split_tree(Vcb, tc2->tree);
+ } else if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+ TRACE("splitting overlarge tree (%x > %x)\n", t->size, Vcb->superblock.node_size - sizeof(tree_header));
+ Status = split_tree(Vcb, t);
if (!NT_SUCCESS(Status)) {
ERR("split_tree returned %08x\n", Status);
for (level = 0; level <= max_level; level++) {
LIST_ENTRY* le;
- le = Vcb->tree_cache.Flink;
+ le = Vcb->trees.Flink;
- while (le != &Vcb->tree_cache) {
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ while (le != &Vcb->trees) {
+ t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write && tc2->tree->header.level == level && tc2->tree->header.num_items > 0 && tc2->tree->parent && tc2->tree->size < min_size) {
- Status = try_tree_amalgamate(Vcb, tc2->tree, rollback);
+ if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size) {
+ Status = try_tree_amalgamate(Vcb, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("try_tree_amalgamate returned %08x\n", Status);
return Status;
for (level = max_level; level > 0; level--) {
LIST_ENTRY *le, *nextle;
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
nextle = le->Flink;
- tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ t = CONTAINING_RECORD(le, tree, list_entry);
- if (tc2->write && tc2->tree->header.level == level) {
- if (!tc2->tree->parent && tc2->tree->header.num_items == 1) {
- LIST_ENTRY* le2 = tc2->tree->itemlist.Flink;
+ if (t->write && t->header.level == level) {
+ if (!t->parent && t->header.num_items == 1) {
+ LIST_ENTRY* le2 = t->itemlist.Flink;
tree_data* td;
tree* child_tree = NULL;
- while (le2 != &tc2->tree->itemlist) {
+ while (le2 != &t->itemlist) {
td = CONTAINING_RECORD(le2, tree_data, list_entry);
if (!td->ignore)
break;
le2 = le2->Flink;
}
- TRACE("deleting top-level tree in root %llx with one item\n", tc2->tree->root->id);
+ TRACE("deleting top-level tree in root %llx with one item\n", t->root->id);
- if (tc2->tree->has_new_address) { // delete associated EXTENT_ITEM
- Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree, rollback);
+ if (t->has_new_address) { // delete associated EXTENT_ITEM
+ Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
return Status;
}
- } else if (tc2->tree->has_address) {
- Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree, rollback);
+ } else if (t->has_address) {
+ Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
if (!NT_SUCCESS(Status)) {
ERR("reduce_tree_extent returned %08x\n", Status);
KEY searchkey = {0,0,0};
traverse_ptr tp;
- Status = find_item(Vcb, tc2->tree->root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
- free_traverse_ptr(&tp);
}
child_tree = td->treeholder.tree;
if (child_tree) {
child_tree->parent = NULL;
child_tree->paritem = NULL;
- free_tree(tc2->tree);
}
- tc2->tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
+ t->root->root_item.bytes_used -= Vcb->superblock.node_size;
- free_tree(tc2->tree);
+ free_tree(t);
if (child_tree)
child_tree->root->treeholder.tree = child_tree;
-
- RemoveEntryList(le);
- ExFreePool(tc2);
}
}
return STATUS_SUCCESS;
}
+static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+
+ if (level > 0) {
+ if (!th->tree) {
+ Status = load_tree(Vcb, th->address, r, &th->tree);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_tree(%llx) returned %08x\n", th->address, Status);
+ return Status;
+ }
+ }
+
+ if (th->tree->header.level > 0) {
+ LIST_ENTRY* le = th->tree->itemlist.Flink;
+
+ while (le != &th->tree->itemlist) {
+ tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+
+ if (!td->ignore) {
+ Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_root_extents returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ le = le->Flink;
+ }
+ }
+ }
+
+ if (!th->tree || th->tree->has_address) {
+ Status = reduce_tree_extent(Vcb, th->address, NULL, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("reduce_tree_extent(%llx) returned %08x\n", th->address, Status);
+ return Status;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+
+ Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("remove_root_extents returned %08x\n", Status);
+ return Status;
+ }
+
+ // remove entry in uuid root (tree 9)
+ if (Vcb->uuid_root) {
+ RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid.uuid[0], sizeof(UINT64));
+ searchkey.obj_type = TYPE_SUBVOL_UUID;
+ RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
+
+ if (searchkey.obj_id != 0 || searchkey.offset != 0) {
+ Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ WARN("find_item returned %08x\n", Status);
+ } else {
+ if (!keycmp(&tp.item->key, &searchkey))
+ delete_tree_item(Vcb, &tp, rollback);
+ else
+ WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ }
+ }
+ }
+
+ // delete ROOT_ITEM
+
+ searchkey.obj_id = r->id;
+ searchkey.obj_type = TYPE_ROOT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
+ delete_tree_item(Vcb, &tp, rollback);
+ else
+ WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+
+ // delete items in tree cache
+
+ free_trees_root(Vcb, r);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) {
+ LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2;
+ NTSTATUS Status;
+
+ while (le != &Vcb->drop_roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ le2 = le->Flink;
+
+ Status = drop_root(Vcb, r, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("drop_root(%llx) returned %08x\n", r->id, Status);
+ return Status;
+ }
+
+ le = le2;
+ }
+
+ return STATUS_SUCCESS;
+}
+
NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
NTSTATUS Status;
LIST_ENTRY* le;
+ BOOL cache_changed;
TRACE("(%p)\n", Vcb);
+ if (!IsListEmpty(&Vcb->drop_roots)) {
+ Status = drop_roots(Vcb, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("drop_roots returned %08x\n", Status);
+ return Status;
+ }
+ }
+
// If only changing superblock, e.g. changing label, we still need to rewrite
// the root tree so the generations match, otherwise you won't be able to mount on Linux.
- if (Vcb->write_trees > 0) {
+ if (Vcb->write_trees == 0) {
KEY searchkey;
traverse_ptr tp;
return Status;
}
- add_to_tree_cache(Vcb, Vcb->root_root->treeholder.tree, TRUE);
-
- free_traverse_ptr(&tp);
+ if (!Vcb->root_root->treeholder.tree->write) {
+ Vcb->root_root->treeholder.tree->write = TRUE;
+ Vcb->write_trees++;
+ }
}
do {
ERR("update_chunk_usage returned %08x\n", Status);
goto end;
}
- } while (!trees_consistent(Vcb));
+
+ Status = allocate_cache(Vcb, &cache_changed, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("allocate_cache returned %08x\n", Status);
+ goto end;
+ }
+ } while (cache_changed || !trees_consistent(Vcb, rollback));
TRACE("trees consistent\n");
goto end;
}
+ Vcb->superblock.cache_generation = Vcb->superblock.generation;
+
Status = write_superblocks(Vcb);
if (!NT_SUCCESS(Status)) {
ERR("write_superblocks returned %08x\n", Status);
Vcb->superblock.generation++;
-// print_trees(tc); // TESTING
-
Status = STATUS_SUCCESS;
- le = Vcb->tree_cache.Flink;
- while (le != &Vcb->tree_cache) {
- tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
+ le = Vcb->trees.Flink;
+ while (le != &Vcb->trees) {
+ tree* t = CONTAINING_RECORD(le, tree, list_entry);
- tc2->write = FALSE;
+ t->write = FALSE;
le = le->Flink;
}
Vcb->write_trees = 0;
+ while (!IsListEmpty(&Vcb->drop_roots)) {
+ LIST_ENTRY* le = RemoveHeadList(&Vcb->drop_roots);
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ }
+
end:
TRACE("do_write returning %08x\n", Status);
#endif
}
-static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* ins) {
- LIST_ENTRY* le = list->Flink;
- ordered_list* ol;
-
- while (le != list) {
- ol = (ordered_list*)le;
-
- if (ol->key > ins->key) {
- le->Blink->Flink = &ins->list_entry;
- ins->list_entry.Blink = le->Blink;
- le->Blink = &ins->list_entry;
- ins->list_entry.Flink = le;
- return;
- }
-
- le = le->Flink;
- }
-
- InsertTailList(list, &ins->list_entry);
-}
-
-static UINT64 get_extent_data_ref_hash(UINT64 root, UINT64 objid, UINT64 offset) {
- UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff;
-
- // FIXME - can we test this?
-
- // FIXME - make sure numbers here are little-endian
- high_crc = calc_crc32c(high_crc, (UINT8*)&root, sizeof(UINT64));
- low_crc = calc_crc32c(low_crc, (UINT8*)&objid, sizeof(UINT64));
- low_crc = calc_crc32c(low_crc, (UINT8*)&offset, sizeof(UINT64));
-
- return ((UINT64)high_crc << 31) ^ (UINT64)low_crc;
-}
-
-NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- EXTENT_ITEM* ei;
- UINT8 *siptr, *type;
- ULONG len;
- UINT64 hash;
- EXTENT_DATA_REF* edr;
- NTSTATUS Status;
-
- TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback) {
+// KEY searchkey;
+// traverse_ptr tp;
+// EXTENT_ITEM* ei;
+// UINT8 *siptr, *type;
+// ULONG len;
+// UINT64 hash;
+// EXTENT_DATA_REF* edr;
+// NTSTATUS Status;
+//
+// TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+//
+// searchkey.obj_id = address;
+// searchkey.obj_type = TYPE_EXTENT_ITEM;
+// searchkey.offset = size;
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// // create new entry
+//
+// len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+//
+// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+// if (!ei) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// ei->refcount = 1;
+// ei->generation = Vcb->superblock.generation;
+// ei->flags = EXTENT_ITEM_DATA;
+//
+// type = (UINT8*)&ei[1];
+// *type = TYPE_EXTENT_DATA_REF;
+//
+// edr = (EXTENT_DATA_REF*)&type[1];
+// edr->root = subvol->id;
+// edr->objid = inode;
+// edr->offset = offset;
+// edr->count = 1;
+//
+// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
+// ERR("error - failed to insert item\n");
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// // FIXME - update free space in superblock and CHUNK_ITEM
+//
+// return STATUS_SUCCESS;
+// }
+//
+// if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+// NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
+// if (!NT_SUCCESS(Status)) {
+// ERR("convert_old_data_extent returned %08x\n", Status);
+// return Status;
+// }
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// WARN("extent item not found for address %llx, size %llx\n", address, size);
+// return STATUS_SUCCESS;
+// }
+// }
+//
+// ei = (EXTENT_ITEM*)tp.item->data;
+//
+// if (tp.item->size < sizeof(EXTENT_ITEM)) {
+// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+// NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
+// if (!NT_SUCCESS(Status)) {
+// ERR("convert_shared_data_extent returned %08x\n", Status);
+// return Status;
+// }
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// WARN("extent item not found for address %llx, size %llx\n", address, size);
+// return STATUS_SUCCESS;
+// }
+//
+// ei = (EXTENT_ITEM*)tp.item->data;
+// }
+//
+// if (ei->flags != EXTENT_ITEM_DATA) {
+// ERR("error - flag was not EXTENT_ITEM_DATA\n");
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// hash = get_extent_data_ref_hash(subvol->id, inode, offset);
+//
+// len = tp.item->size - sizeof(EXTENT_ITEM);
+// siptr = (UINT8*)&ei[1];
+//
+// do {
+// if (*siptr == TYPE_EXTENT_DATA_REF) {
+// UINT64 sihash;
+//
+// edr = (EXTENT_DATA_REF*)&siptr[1];
+//
+// // already exists - increase refcount
+// if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
+// ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+//
+// if (!ei) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// RtlCopyMemory(ei, tp.item->data, tp.item->size);
+//
+// edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data));
+// edr->count++;
+// ei->refcount++;
+//
+// delete_tree_item(Vcb, &tp, rollback);
+//
+// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) {
+// ERR("error - failed to insert item\n");
+// ExFreePool(ei);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// return STATUS_SUCCESS;
+// }
+//
+// sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset);
+//
+// if (sihash >= hash)
+// break;
+//
+// siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+//
+// if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+// len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+// } else
+// break;
+// // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+// } else {
+// ERR("unrecognized extent subitem %x\n", *siptr);
+// return STATUS_INTERNAL_ERROR;
+// }
+// } while (len > 0);
+//
+// len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big
+// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+// if (!ei) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+// ei->refcount++;
+//
+// type = (UINT8*)ei + (siptr - tp.item->data);
+// *type = TYPE_EXTENT_DATA_REF;
+//
+// edr = (EXTENT_DATA_REF*)&type[1];
+// edr->root = subvol->id;
+// edr->objid = inode;
+// edr->offset = offset;
+// edr->count = 1;
+//
+// if (siptr < tp.item->data + tp.item->size)
+// RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr);
+//
+// delete_tree_item(Vcb, &tp, rollback);
+//
+// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
+// ERR("error - failed to insert item\n");
+// ExFreePool(ei);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// return STATUS_SUCCESS;
+// }
+
+static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len) {
+ UINT8* siptr = (UINT8*)&ei[1];
+
+ do {
+ if (*siptr == TYPE_TREE_BLOCK_REF) {
+ siptr += sizeof(TREE_BLOCK_REF) + 1;
+ len -= sizeof(TREE_BLOCK_REF) + 1;
+ } else if (*siptr == TYPE_EXTENT_DATA_REF) {
+ siptr += sizeof(EXTENT_DATA_REF) + 1;
+ len -= sizeof(EXTENT_DATA_REF) + 1;
+ } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
+ return TRUE;
+ } else if (*siptr == TYPE_SHARED_DATA_REF) {
+ return TRUE;
+ } else {
+ ERR("unrecognized extent subitem %x\n", *siptr);
+ return FALSE;
+ }
+ } while (len > 0);
+
+ return FALSE;
+}
+
+// static NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+// KEY searchkey;
+// traverse_ptr tp;
+// EXTENT_ITEM* ei;
+// UINT8* siptr;
+// ULONG len;
+// EXTENT_DATA_REF* edr;
+// BOOL found;
+// NTSTATUS Status;
+//
+// TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
+//
+// searchkey.obj_id = address;
+// searchkey.obj_type = TYPE_EXTENT_ITEM;
+// searchkey.offset = size;
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// WARN("extent item not found for address %llx, size %llx\n", address, size);
+// return STATUS_SUCCESS;
+// }
+//
+// if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+// NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
+// if (!NT_SUCCESS(Status)) {
+// ERR("convert_old_data_extent returned %08x\n", Status);
+// return Status;
+// }
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// WARN("extent item not found for address %llx, size %llx\n", address, size);
+// return STATUS_SUCCESS;
+// }
+// }
+//
+// ei = (EXTENT_ITEM*)tp.item->data;
+//
+// if (tp.item->size < sizeof(EXTENT_ITEM)) {
+// ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// if (!(ei->flags & EXTENT_ITEM_DATA)) {
+// ERR("error - EXTENT_ITEM_DATA flag not set\n");
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
+// NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
+// if (!NT_SUCCESS(Status)) {
+// ERR("convert_shared_data_extent returned %08x\n", Status);
+// return Status;
+// }
+//
+// Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+// if (!NT_SUCCESS(Status)) {
+// ERR("error - find_item returned %08x\n", Status);
+// return Status;
+// }
+//
+// if (keycmp(&tp.item->key, &searchkey)) {
+// WARN("extent item not found for address %llx, size %llx\n", address, size);
+// return STATUS_SUCCESS;
+// }
+//
+// ei = (EXTENT_ITEM*)tp.item->data;
+// }
+//
+// len = tp.item->size - sizeof(EXTENT_ITEM);
+// siptr = (UINT8*)&ei[1];
+// found = FALSE;
+//
+// do {
+// if (*siptr == TYPE_EXTENT_DATA_REF) {
+// edr = (EXTENT_DATA_REF*)&siptr[1];
+//
+// if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
+// if (edr->count > 1) {
+// ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+//
+// if (!ei) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// RtlCopyMemory(ei, tp.item->data, tp.item->size);
+//
+// edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data));
+// edr->count--;
+// ei->refcount--;
+//
+// delete_tree_item(Vcb, &tp, rollback);
+//
+// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) {
+// ERR("error - failed to insert item\n");
+// ExFreePool(ei);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// return STATUS_SUCCESS;
+// }
+//
+// found = TRUE;
+// break;
+// }
+//
+// siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
+//
+// if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
+// len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
+// } else
+// break;
+// // // FIXME - TYPE_TREE_BLOCK_REF 0xB0
+// } else {
+// ERR("unrecognized extent subitem %x\n", *siptr);
+// return STATUS_INTERNAL_ERROR;
+// }
+// } while (len > 0);
+//
+// if (!found) {
+// WARN("could not find extent data ref\n");
+// return STATUS_SUCCESS;
+// }
+//
+// // FIXME - decrease subitem refcount if there already?
+//
+// len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF);
+//
+// delete_tree_item(Vcb, &tp, rollback);
+//
+// if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed
+// chunk* c;
+// LIST_ENTRY* le2;
+//
+// if (changed_sector_list) {
+// changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+// if (!sc) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// sc->ol.key = address;
+// sc->checksums = NULL;
+// sc->length = size / Vcb->superblock.sector_size;
+//
+// sc->deleted = TRUE;
+//
+// insert_into_ordered_list(changed_sector_list, &sc->ol);
+// }
+//
+// c = NULL;
+// le2 = Vcb->chunks.Flink;
+// while (le2 != &Vcb->chunks) {
+// c = CONTAINING_RECORD(le2, chunk, list_entry);
+//
+// TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
+//
+// if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
+// break;
+//
+// le2 = le2->Flink;
+// }
+// if (le2 == &Vcb->chunks) c = NULL;
+//
+// if (c) {
+// decrease_chunk_usage(c, size);
+//
+// add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
+// }
+//
+// return STATUS_SUCCESS;
+// }
+//
+// ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+// if (!ei) {
+// ERR("out of memory\n");
+// return STATUS_INSUFFICIENT_RESOURCES;
+// }
+//
+// RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
+// ei->refcount--;
+// ei->generation = Vcb->superblock.generation;
+//
+// if (tp.item->data + len != siptr)
+// RtlCopyMemory((UINT8*)ei + (siptr - tp.item->data), siptr + sizeof(UINT8) + sizeof(EXTENT_DATA_REF), tp.item->size - (siptr - tp.item->data) - sizeof(UINT8) - sizeof(EXTENT_DATA_REF));
+//
+// if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
+// ERR("error - failed to insert item\n");
+// ExFreePool(ei);
+// return STATUS_INTERNAL_ERROR;
+// }
+//
+// return STATUS_SUCCESS;
+// }
+
+static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) {
+ LIST_ENTRY* le = list->Flink;
+ ordered_list* ol;
+
+ while (le != list) {
+ ol = (ordered_list*)le;
+
+ if (ol->key > value)
+ return FALSE;
+ else if (ol->key == value)
+ return TRUE;
+
+ le = le->Flink;
+ }
+
+ return FALSE;
+}
+
+static BOOL is_file_prealloc_inode(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 start_data, UINT64 end_data) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b;
+
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- return Status;
+ return FALSE;
}
- if (keycmp(&tp.item->key, &searchkey)) {
- // create new entry
-
- len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
- free_traverse_ptr(&tp);
+ if (tp.item->key.obj_id != inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ return FALSE;
+
+ do {
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+ UINT64 len;
- ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
- if (!ei) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ return FALSE;
}
- ei->refcount = 1;
- ei->generation = Vcb->superblock.generation;
- ei->flags = EXTENT_ITEM_DATA;
-
- type = (UINT8*)&ei[1];
- *type = TYPE_EXTENT_DATA_REF;
-
- edr = (EXTENT_DATA_REF*)&type[1];
- edr->root = subvol->id;
- edr->objid = inode;
- edr->offset = offset;
- edr->count = 1;
-
- if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- return STATUS_INTERNAL_ERROR;
+ if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ return FALSE;
}
- // FIXME - update free space in superblock and CHUNK_ITEM
-
- return STATUS_SUCCESS;
- }
-
- if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
- NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_old_data_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- free_traverse_ptr(&tp);
+ len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
+ if (tp.item->key.offset < end_data && tp.item->key.offset + len >= start_data && ed->type == EXTENT_TYPE_PREALLOC)
+ return TRUE;
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
+ break;
}
- }
+ } while (b);
- ei = (EXTENT_ITEM*)tp.item->data;
+ return FALSE;
+}
+
+NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b, deleted_prealloc = FALSE;
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
+ searchkey.obj_id = inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
- if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
- NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_shared_data_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
-
- free_traverse_ptr(&tp);
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- ei = (EXTENT_ITEM*)tp.item->data;
- }
-
- if (ei->flags != EXTENT_ITEM_DATA) {
- ERR("error - flag was not EXTENT_ITEM_DATA\n");
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
-
- // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
-
- hash = get_extent_data_ref_hash(subvol->id, inode, offset);
-
- len = tp.item->size - sizeof(EXTENT_ITEM);
- siptr = (UINT8*)&ei[1];
-
- // FIXME - increase subitem refcount if there already?
- do {
- if (*siptr == TYPE_EXTENT_DATA_REF) {
- UINT64 sihash;
-
- edr = (EXTENT_DATA_REF*)&siptr[1];
- sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset);
-
- if (sihash >= hash)
- break;
-
- siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
-
- if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
- len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
- } else
- break;
- // FIXME - TYPE_TREE_BLOCK_REF 0xB0
- } else {
- ERR("unrecognized extent subitem %x\n", *siptr);
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
- } while (len > 0);
-
- len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big
- ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
- if (!ei) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
- ei->refcount++;
-
- type = (UINT8*)ei + (siptr - tp.item->data);
- *type = TYPE_EXTENT_DATA_REF;
-
- edr = (EXTENT_DATA_REF*)&type[1];
- edr->root = subvol->id;
- edr->objid = inode;
- edr->offset = offset;
- edr->count = 1;
-
- if (siptr < tp.item->data + tp.item->size)
- RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- free_traverse_ptr(&tp);
-
- if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(ei);
- return STATUS_INTERNAL_ERROR;
- }
-
- return STATUS_SUCCESS;
-}
-
-typedef struct {
- EXTENT_DATA_REF edr;
- LIST_ENTRY list_entry;
-} data_ref;
-
-static void add_data_ref(LIST_ENTRY* data_refs, UINT64 root, UINT64 objid, UINT64 offset) {
- data_ref* dr = ExAllocatePoolWithTag(PagedPool, sizeof(data_ref), ALLOC_TAG);
-
- if (!dr) {
- ERR("out of memory\n");
- return;
- }
-
- // FIXME - increase count if entry there already
- // FIXME - put in order?
-
- dr->edr.root = root;
- dr->edr.objid = objid;
- dr->edr.offset = offset;
- dr->edr.count = 1;
-
- InsertTailList(data_refs, &dr->list_entry);
-}
-
-static void free_data_refs(LIST_ENTRY* data_refs) {
- while (!IsListEmpty(data_refs)) {
- LIST_ENTRY* le = RemoveHeadList(data_refs);
- data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
-
- ExFreePool(dr);
- }
-}
-
-static NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp, next_tp;
- BOOL b;
- LIST_ENTRY data_refs;
- LIST_ENTRY* le;
- UINT64 refcount;
- EXTENT_ITEM* ei;
- ULONG eisize;
- UINT8* type;
- NTSTATUS Status;
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- if (tp.item->size != sizeof(EXTENT_ITEM_V0)) {
- TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- delete_tree_item(Vcb, &tp, rollback);
-
- free_traverse_ptr(&tp);
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_REF_V0;
- searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
- InitializeListHead(&data_refs);
-
+
do {
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-
- if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
- tree* t;
-
- // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
-
- Status = load_tree(Vcb, tp.item->key.offset, NULL, &t);
-
- if (!NT_SUCCESS(Status)) {
- ERR("load tree for address %llx returned %08x\n", tp.item->key.offset, Status);
- free_traverse_ptr(&tp);
- free_data_refs(&data_refs);
- return Status;
- }
-
- if (t->header.level == 0) {
- le = t->itemlist.Flink;
- while (le != &t->itemlist) {
- tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
-
- if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
- EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
-
- if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
-
- if (ed2->address == address)
- add_data_ref(&data_refs, t->header.tree_id, td->key.obj_id, td->key.offset);
- }
- }
-
- le = le->Flink;
- }
- }
-
- free_tree(t);
-
- delete_tree_item(Vcb, &tp, rollback);
- }
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+ UINT64 len;
- if (b) {
- free_traverse_ptr(&tp);
- tp = next_tp;
-
- if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
- break;
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
}
- } while (b);
-
- free_traverse_ptr(&tp);
-
- if (IsListEmpty(&data_refs)) {
- WARN("no data refs found\n");
- return STATUS_SUCCESS;
- }
-
- // create new entry
-
- refcount = 0;
-
- le = data_refs.Flink;
- while (le != &data_refs) {
- refcount++;
- le = le->Flink;
- }
-
- eisize = sizeof(EXTENT_ITEM) + ((sizeof(char) + sizeof(EXTENT_DATA_REF)) * refcount);
- ei = ExAllocatePoolWithTag(PagedPool, eisize, ALLOC_TAG);
- if (!ei) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- ei->refcount = refcount;
- ei->generation = Vcb->superblock.generation;
- ei->flags = EXTENT_ITEM_DATA;
-
- type = (UINT8*)&ei[1];
-
- le = data_refs.Flink;
- while (le != &data_refs) {
- data_ref* dr = CONTAINING_RECORD(le, data_ref, list_entry);
-
- type[0] = TYPE_EXTENT_DATA_REF;
- RtlCopyMemory(&type[1], &dr->edr, sizeof(EXTENT_DATA_REF));
-
- type = &type[1 + sizeof(EXTENT_DATA_REF)];
- le = le->Flink;
- }
-
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(ei);
- return STATUS_INTERNAL_ERROR;
- }
-
- free_data_refs(&data_refs);
-
- return STATUS_SUCCESS;
-}
-
-typedef struct {
- UINT8 type;
- void* data;
- BOOL allocated;
- LIST_ENTRY list_entry;
-} extent_ref;
-
-static void free_extent_refs(LIST_ENTRY* extent_refs) {
- while (!IsListEmpty(extent_refs)) {
- LIST_ENTRY* le = RemoveHeadList(extent_refs);
- extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- if (er->allocated)
- ExFreePool(er->data);
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type)
+ goto cont;
- ExFreePool(er);
- }
-}
-
-static NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- LIST_ENTRY extent_refs;
- LIST_ENTRY *le, *next_le;
- EXTENT_ITEM *ei, *newei;
- UINT8* siptr;
- ULONG len;
- UINT64 count;
- NTSTATUS Status;
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
-
- ei = (EXTENT_ITEM*)tp.item->data;
- len = tp.item->size - sizeof(EXTENT_ITEM);
-
- InitializeListHead(&extent_refs);
-
- siptr = (UINT8*)&ei[1];
-
- do {
- extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
- if (!er) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
+ if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
}
-
- er->type = *siptr;
- er->data = siptr+1;
- er->allocated = FALSE;
- InsertTailList(&extent_refs, &er->list_entry);
-
- if (*siptr == TYPE_TREE_BLOCK_REF) {
- siptr += sizeof(TREE_BLOCK_REF);
- len -= sizeof(TREE_BLOCK_REF) + 1;
- } else if (*siptr == TYPE_EXTENT_DATA_REF) {
- siptr += sizeof(EXTENT_DATA_REF);
- len -= sizeof(EXTENT_DATA_REF) + 1;
- } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
- siptr += sizeof(SHARED_BLOCK_REF);
- len -= sizeof(SHARED_BLOCK_REF) + 1;
- } else if (*siptr == TYPE_SHARED_DATA_REF) {
- siptr += sizeof(SHARED_DATA_REF);
- len -= sizeof(SHARED_DATA_REF) + 1;
- } else {
- ERR("unrecognized extent subitem %x\n", *siptr);
- free_traverse_ptr(&tp);
- free_extent_refs(&extent_refs);
- return STATUS_INTERNAL_ERROR;
- }
- } while (len > 0);
-
- le = extent_refs.Flink;
- while (le != &extent_refs) {
- extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
- next_le = le->Flink;
+ len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
- if (er->type == TYPE_SHARED_DATA_REF) {
- // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
- SHARED_DATA_REF* sdr = er->data;
- tree* t;
-
- Status = load_tree(Vcb, sdr->offset, NULL, &t);
- if (!NT_SUCCESS(Status)) {
- ERR("load_tree for address %llx returned %08x\n", sdr->offset, Status);
- free_traverse_ptr(&tp);
- free_data_refs(&extent_refs);
- return Status;
+ if (tp.item->key.offset < end_data && tp.item->key.offset + len >= start_data) {
+ if (ed->compression != BTRFS_COMPRESSION_NONE) {
+ FIXME("FIXME - compression not supported at present\n");
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
}
- if (t->header.level == 0) {
- LIST_ENTRY* le2 = t->itemlist.Flink;
- while (le2 != &t->itemlist) {
- tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
-
- if (!td->ignore && td->key.obj_type == TYPE_EXTENT_DATA) {
- EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
-
- if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
-
- if (ed2->address == address) {
- extent_ref* er2;
- EXTENT_DATA_REF* edr;
-
- er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
- if (!er2) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- edr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA_REF), ALLOC_TAG);
- if (!edr) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- ExFreePool(er2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- edr->root = t->header.tree_id;
- edr->objid = td->key.obj_id;
- edr->offset = td->key.offset;
- edr->count = 1;
-
- er2->type = TYPE_EXTENT_DATA_REF;
- er2->data = edr;
- er2->allocated = TRUE;
-
- InsertTailList(&extent_refs, &er2->list_entry); // FIXME - list should be in order
- }
- }
- }
-
- le2 = le2->Flink;
- }
- }
-
- free_tree(t);
-
- RemoveEntryList(&er->list_entry);
-
- if (er->allocated)
- ExFreePool(er->data);
-
- ExFreePool(er);
- }
- // FIXME - also do for SHARED_BLOCK_REF?
-
- le = next_le;
- }
-
- if (IsListEmpty(&extent_refs)) {
- WARN("no extent refs found\n");
- delete_tree_item(Vcb, &tp, rollback);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- len = 0;
- count = 0;
- le = extent_refs.Flink;
- while (le != &extent_refs) {
- extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
-
- len++;
- if (er->type == TYPE_TREE_BLOCK_REF) {
- len += sizeof(TREE_BLOCK_REF);
- } else if (er->type == TYPE_EXTENT_DATA_REF) {
- len += sizeof(EXTENT_DATA_REF);
- } else {
- ERR("unexpected extent subitem %x\n", er->type);
- }
-
- count++;
-
- le = le->Flink;
- }
-
- newei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM) + len, ALLOC_TAG);
- if (!newei) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(newei, ei, sizeof(EXTENT_ITEM));
- newei->refcount = count;
-
- siptr = (UINT8*)&newei[1];
- le = extent_refs.Flink;
- while (le != &extent_refs) {
- extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
-
- *siptr = er->type;
- siptr++;
-
- if (er->type == TYPE_TREE_BLOCK_REF) {
- RtlCopyMemory(siptr, er->data, sizeof(TREE_BLOCK_REF));
- } else if (er->type == TYPE_EXTENT_DATA_REF) {
- RtlCopyMemory(siptr, er->data, sizeof(EXTENT_DATA_REF));
- } else {
- ERR("unexpected extent subitem %x\n", er->type);
- }
-
- le = le->Flink;
- }
-
- delete_tree_item(Vcb, &tp, rollback);
- free_traverse_ptr(&tp);
-
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, newei, sizeof(EXTENT_ITEM) + len, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(newei);
- free_extent_refs(&extent_refs);
- return STATUS_INTERNAL_ERROR;
- }
-
- free_extent_refs(&extent_refs);
-
- return STATUS_SUCCESS;
-}
-
-static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len) {
- UINT8* siptr = (UINT8*)&ei[1];
-
- do {
- if (*siptr == TYPE_TREE_BLOCK_REF) {
- siptr += sizeof(TREE_BLOCK_REF) + 1;
- len -= sizeof(TREE_BLOCK_REF) + 1;
- } else if (*siptr == TYPE_EXTENT_DATA_REF) {
- siptr += sizeof(EXTENT_DATA_REF) + 1;
- len -= sizeof(EXTENT_DATA_REF) + 1;
- } else if (*siptr == TYPE_SHARED_BLOCK_REF) {
- return TRUE;
- } else if (*siptr == TYPE_SHARED_DATA_REF) {
- return TRUE;
- } else {
- ERR("unrecognized extent subitem %x\n", *siptr);
- return FALSE;
- }
- } while (len > 0);
-
- return FALSE;
-}
-
-NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- EXTENT_ITEM* ei;
- UINT8* siptr;
- ULONG len;
- EXTENT_DATA_REF* edr;
- BOOL found;
- NTSTATUS Status;
-
- TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
- NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_old_data_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
-
- free_traverse_ptr(&tp);
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
- }
-
- ei = (EXTENT_ITEM*)tp.item->data;
-
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (!(ei->flags & EXTENT_ITEM_DATA)) {
- ERR("error - EXTENT_ITEM_DATA flag not set\n");
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
-
- // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
-
- if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
- NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_shared_data_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
-
- free_traverse_ptr(&tp);
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", address, size);
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- ei = (EXTENT_ITEM*)tp.item->data;
- }
-
- len = tp.item->size - sizeof(EXTENT_ITEM);
- siptr = (UINT8*)&ei[1];
- found = FALSE;
-
- do {
- if (*siptr == TYPE_EXTENT_DATA_REF) {
- edr = (EXTENT_DATA_REF*)&siptr[1];
-
- if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
- found = TRUE;
- break;
- }
-
- siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
-
- if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
- len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
- } else
- break;
-// // FIXME - TYPE_TREE_BLOCK_REF 0xB0
- } else {
- ERR("unrecognized extent subitem %x\n", *siptr);
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
- } while (len > 0);
-
- if (!found) {
- WARN("could not find extent data ref\n");
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- // FIXME - decrease subitem refcount if there already?
-
- len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed
- chunk* c;
- LIST_ENTRY* le2;
-
- if (changed_sector_list) {
- changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
- if (!sc) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sc->ol.key = address;
- sc->checksums = NULL;
- sc->length = size / Vcb->superblock.sector_size;
-
- sc->deleted = TRUE;
-
- insert_into_ordered_list(changed_sector_list, &sc->ol);
- }
-
- c = NULL;
- le2 = Vcb->chunks.Flink;
- while (le2 != &Vcb->chunks) {
- c = CONTAINING_RECORD(le2, chunk, list_entry);
-
- TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
-
- if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
- break;
-
- le2 = le2->Flink;
- }
- if (le2 == &Vcb->chunks) c = NULL;
-
- if (c) {
- decrease_chunk_usage(c, size);
-
- add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
- }
-
- free_traverse_ptr(&tp);
- return STATUS_SUCCESS;
- }
-
- ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
- if (!ei) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
- ei->refcount--;
- ei->generation = Vcb->superblock.generation;
-
- if (tp.item->data + len != siptr)
- RtlCopyMemory((UINT8*)ei + (siptr - tp.item->data), siptr + sizeof(UINT8) + sizeof(EXTENT_DATA_REF), tp.item->size - (siptr - tp.item->data) - sizeof(UINT8) - sizeof(EXTENT_DATA_REF));
-
- free_traverse_ptr(&tp);
-
- if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(ei);
- return STATUS_INTERNAL_ERROR;
- }
-
- return STATUS_SUCCESS;
-}
-
-static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) {
- LIST_ENTRY* le = list->Flink;
- ordered_list* ol;
-
- while (le != list) {
- ol = (ordered_list*)le;
-
- if (ol->key > value)
- return FALSE;
- else if (ol->key == value)
- return TRUE;
-
- le = le->Flink;
- }
-
- return FALSE;
-}
-
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp, next_tp;
- NTSTATUS Status;
- BOOL b;
-
- TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, changed_sector_list);
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_EXTENT_DATA;
- searchkey.offset = start_data;
-
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- do {
- EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
- UINT64 len;
-
- if (tp.item->size < sizeof(EXTENT_DATA)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
- Status = STATUS_INTERNAL_ERROR;
- goto end;
- }
-
- if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
- Status = STATUS_INTERNAL_ERROR;
- goto end;
- }
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-
- len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-
- if (tp.item->key.offset < end_data && tp.item->key.offset + len >= start_data) {
- if (ed->compression != BTRFS_COMPRESSION_NONE) {
- FIXME("FIXME - compression not supported at present\n");
- Status = STATUS_NOT_SUPPORTED;
- goto end;
- }
-
- if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
- WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, ed->encryption);
- Status = STATUS_NOT_SUPPORTED;
- goto end;
+ if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
+ WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", subvol->id, inode, tp.item->key.offset, ed->encryption);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
}
if (ed->encoding != BTRFS_ENCODING_NONE) {
if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all
delete_tree_item(Vcb, &tp, rollback);
- fcb->inode_item.st_blocks -= len;
+ if (ii)
+ ii->st_blocks -= len;
} else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning
EXTENT_DATA* ned;
UINT64 size;
RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
- fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ if (ii)
+ ii->st_blocks -= end_data - tp.item->key.offset;
} else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove end
EXTENT_DATA* ned;
UINT64 size;
RtlCopyMemory(&ned->data[0], &ed->data[0], size);
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
- fcb->inode_item.st_blocks -= tp.item->key.offset + len - start_data;
+ if (ii)
+ ii->st_blocks -= tp.item->key.offset + len - start_data;
} else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove middle
EXTENT_DATA* ned;
UINT64 size;
RtlCopyMemory(&ned->data[0], &ed->data[0], size);
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
RtlCopyMemory(&ned->data[0], &ed->data[end_data - tp.item->key.offset], size);
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
- fcb->inode_item.st_blocks -= end_data - start_data;
+ if (ii)
+ ii->st_blocks -= end_data - start_data;
}
} else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all
if (ed2->address != 0) {
- Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, changed_sector_list, rollback);
+ Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, inode, tp.item->key.offset - ed2->offset, 1, changed_sector_list, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("remove_extent_ref returned %08x\n", Status);
+ ERR("decrease_extent_refcount_data returned %08x\n", Status);
goto end;
}
- fcb->inode_item.st_blocks -= len;
+ if (ii)
+ ii->st_blocks -= len;
}
+ if (ed->type == EXTENT_TYPE_PREALLOC)
+ deleted_prealloc = TRUE;
+
delete_tree_item(Vcb, &tp, rollback);
} else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning
EXTENT_DATA* ned;
EXTENT_DATA2* ned2;
- if (ed2->address != 0)
- fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
+ if (ed2->address != 0 && ii)
+ ii->st_blocks -= end_data - tp.item->key.offset;
delete_tree_item(Vcb, &tp, rollback);
ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
ned2->num_bytes = ed2->num_bytes - (end_data - tp.item->key.offset);
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
EXTENT_DATA* ned;
EXTENT_DATA2* ned2;
- if (ed2->address != 0)
- fcb->inode_item.st_blocks -= tp.item->key.offset + len - start_data;
+ if (ed2->address != 0 && ii)
+ ii->st_blocks -= tp.item->key.offset + len - start_data;
delete_tree_item(Vcb, &tp, rollback);
ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
ned2->num_bytes = start_data - tp.item->key.offset;
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
EXTENT_DATA* ned;
EXTENT_DATA2* ned2;
- if (ed2->address != 0)
- fcb->inode_item.st_blocks -= end_data - start_data;
+ if (ed2->address != 0 && ii)
+ ii->st_blocks -= end_data - start_data;
delete_tree_item(Vcb, &tp, rollback);
ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
ned2->num_bytes = start_data - tp.item->key.offset;
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
ned2->num_bytes = tp.item->key.offset + len - end_data;
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ned);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
+
+ if (ed2->address != 0) {
+ Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, inode, tp.item->key.offset - ed2->offset, 1, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
+ goto end;
+ }
+ }
}
}
}
+cont:
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
- if (tp.item->key.obj_id > fcb->inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
+ if (tp.item->key.obj_id > inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
break;
}
} while (b);
// FIXME - do bitmap analysis of changed extents, and free what we can
- Status = STATUS_SUCCESS;
+ if (ii && deleted_prealloc && !is_file_prealloc_inode(Vcb, subvol, inode, 0, sector_align(ii->st_size, Vcb->superblock.sector_size)))
+ ii->flags &= ~BTRFS_INODE_PREALLOC;
end:
- free_traverse_ptr(&tp);
return Status;
}
-static BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
- UINT64 address;
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
NTSTATUS Status;
- EXTENT_ITEM_DATA_REF* eidr;
- EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
- ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
- changed_sector* sc;
- traverse_ptr tp;
- int i;
-
- TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, data, changed_sector_list);
-
- if (!find_address_in_chunk(Vcb, c, length, &address))
- return FALSE;
-
- eidr = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_DATA_REF), ALLOC_TAG);
- if (!eidr) {
- ERR("out of memory\n");
- return FALSE;
- }
- eidr->ei.refcount = 1;
- eidr->ei.generation = Vcb->superblock.generation;
- eidr->ei.flags = EXTENT_ITEM_DATA;
- eidr->type = TYPE_EXTENT_DATA_REF;
- eidr->edr.root = fcb->subvol->id;
- eidr->edr.objid = fcb->inode;
- eidr->edr.offset = start_data;
- eidr->edr.count = 1;
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, changed_sector_list);
- if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, length, eidr, sizeof(EXTENT_ITEM_DATA_REF), &tp, rollback)) {
- ERR("insert_tree_item failed\n");
- ExFreePool(eidr);
- return FALSE;
+ Status = excise_extents_inode(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, start_data, end_data, changed_sector_list, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents_inode returned %08x\n");
+ return Status;
}
- tp.tree->header.generation = eidr->ei.generation;
-
- free_traverse_ptr(&tp);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, UINT64 length, LIST_ENTRY* changed_sector_list) {
+ NTSTATUS Status;
+ changed_sector* sc;
+ int i;
Status = write_data(Vcb, address, data, length);
if (!NT_SUCCESS(Status)) {
ERR("write_data returned %08x\n", Status);
- return FALSE;
+ return Status;
}
if (changed_sector_list) {
sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
if (!sc) {
ERR("out of memory\n");
- return FALSE;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
sc->ol.key = address;
if (!sc->checksums) {
ERR("out of memory\n");
ExFreePool(sc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ for (i = 0; i < sc->length; i++) {
+ sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+ }
+
+ insert_into_ordered_list(changed_sector_list, &sc->ol);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data,
+ UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ UINT64 address;
+ NTSTATUS Status;
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+ ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb, subvol->id, inode, c->offset, start_data, length, data, changed_sector_list);
+
+ if (!find_address_in_chunk(Vcb, c, length, &address))
+ return FALSE;
+
+ Status = increase_extent_refcount_data(Vcb, address, length, subvol, inode, start_data, 1, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (data) {
+ Status = do_write_data(Vcb, address, data, length, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
return FALSE;
}
-
- for (i = 0; i < sc->length; i++) {
- sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
- }
-
- insert_into_ordered_list(changed_sector_list, &sc->ol);
}
// add extent data to inode
ed->compression = BTRFS_COMPRESSION_NONE;
ed->encryption = BTRFS_ENCRYPTION_NONE;
ed->encoding = BTRFS_ENCODING_NONE;
- ed->type = EXTENT_TYPE_REGULAR;
+ ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
ed2 = (EXTENT_DATA2*)ed->data;
ed2->address = address;
ed2->offset = 0;
ed2->num_bytes = length;
- if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, ed, edsize, NULL, rollback)) {
+ if (!insert_tree_item(Vcb, subvol, inode, TYPE_EXTENT_DATA, start_data, ed, edsize, NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ed);
return FALSE;
increase_chunk_usage(c, length);
add_to_space_list(c, address, length, SPACE_TYPE_WRITING);
- fcb->inode_item.st_blocks += length;
+ if (inode_item) {
+ inode_item->st_blocks += length;
+
+ if (prealloc)
+ inode_item->flags |= BTRFS_INODE_PREALLOC;
+ }
return TRUE;
}
+static BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ return insert_extent_chunk_inode(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, c, start_data, length, prealloc, data, changed_sector_list, rollback);
+}
+
static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
LIST_ENTRY* changed_sector_list, traverse_ptr* edtp, traverse_ptr* eitp, LIST_ENTRY* rollback) {
EXTENT_DATA* ed;
goto end;
}
- searchkey.obj_id = ed2->address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = ed2->size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
- }
+ searchkey.obj_id = ed2->address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = ed2->size;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ ERR("error - extent %llx,%llx not found in tree\n", ed2->address, ed2->size);
+ int3; // TESTING
+ goto end;
+ }
+
+ if (tp2.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
+ NTSTATUS Status = convert_old_data_extent(Vcb, ed2->address, ed2->size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_old_data_extent returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end;
+ }
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+
+ if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ goto end;
+ }
+
+ // FIXME - test this
+ if (extent_item_is_shared(ei, tp2.item->size - sizeof(EXTENT_ITEM))) {
+ NTSTATUS Status = convert_shared_data_extent(Vcb, ed2->address, ed2->size, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("convert_shared_data_extent returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(&tp2.item->key, &searchkey)) {
+ WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
+ goto end;
+ }
+
+ ei = (EXTENT_ITEM*)tp2.item->data;
+
+ if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ goto end;
+ }
+ }
+
+ if (ei->refcount != 1) {
+ TRACE("extent refcount was not 1\n");
+ goto end;
+ }
+
+ if (ei->flags != EXTENT_ITEM_DATA) {
+ ERR("error - extent was not a data extent\n");
+ goto end;
+ }
+
+ c = get_chunk_from_address(Vcb, ed2->address);
+
+ le = c->space.Flink;
+ while (le != &c->space) {
+ s = CONTAINING_RECORD(le, space, list_entry);
+
+ if (s->offset == ed2->address + ed2->size) {
+ if (s->type == SPACE_TYPE_FREE && s->size >= length) {
+ success = extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, &tp, &tp2, rollback);
+ }
+ break;
+ } else if (s->offset > ed2->address + ed2->size)
+ break;
+
+ le = le->Flink;
+ }
+
+end:
+
+ return success;
+}
+
+static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = fcb->Vcb->chunks.Flink;
+ chunk* c;
+ UINT64 flags;
+
+ // FIXME - how do we know which RAID level to put this to?
+ flags = BLOCK_FLAG_DATA; // SINGLE
+
+ // FIXME - if length is more than max chunk size, loop through and
+ // create the new chunks first
+
+ while (le != &fcb->Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, TRUE, NULL, NULL, rollback))
+ return STATUS_SUCCESS;
+ }
+
+ le = le->Flink;
+ }
+
+ if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, TRUE, NULL, NULL, rollback))
+ return STATUS_SUCCESS;
+ }
+ }
+
+ // FIXME - rebalance chunks if free space elsewhere?
+ WARN("couldn't find any data chunks with %llx bytes free\n", length);
+
+ return STATUS_DISK_FULL;
+}
+
+NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
+ EXTENT_DATA* ed;
+ EXTENT_DATA2* ed2;
+
+ TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb, r->id, inode, start, length);
+
+ ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+ if (!ed) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ed->generation = Vcb->superblock.generation;
+ ed->decoded_size = length;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_REGULAR;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ed2->address = 0;
+ ed2->size = 0;
+ ed2->offset = 0;
+ ed2->num_bytes = length;
+
+ if (!insert_tree_item(Vcb, r, inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ed);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+// static void print_tree(tree* t) {
+// LIST_ENTRY* le = t->itemlist.Flink;
+// while (le != &t->itemlist) {
+// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+// ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
+// le = le->Flink;
+// }
+// }
+
+static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ UINT64 flags;
+
+ TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
+
+ // FIXME - split data up if not enough space for just one extent
+
+ if (start_data > 0 && try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, rollback))
+ return STATUS_SUCCESS;
+
+ // if there is a gap before start_data, plug it with a sparse extent
+ if (start_data > 0) {
+ traverse_ptr tp;
+ NTSTATUS Status;
+ EXTENT_DATA* ed;
+ UINT64 len;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+// if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
+// traverse_ptr next_tp;
+//
+// ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
+// searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+// print_tree(tp.tree);
+//
+// if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+// ERR("---\n");
+// ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset);
+// print_tree(next_tp.tree);
+//
+// free_traverse_ptr(&next_tp);
+// } else
+// ERR("next item not found\n");
+//
+// int3;
+// free_traverse_ptr(&tp);
+// return STATUS_INTERNAL_ERROR;
+// }
+
+ if (tp.item->key.obj_type == TYPE_EXTENT_DATA && tp.item->size >= sizeof(EXTENT_DATA)) {
+ EXTENT_DATA2* ed2;
+
+ ed = (EXTENT_DATA*)tp.item->data;
+ ed2 = (EXTENT_DATA2*)ed->data;
+
+ len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+ } else
+ ed = NULL;
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || !ed || tp.item->key.offset + len < start_data) {
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data, rollback);
+ else if (!ed)
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ else {
+ Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + len,
+ start_data - tp.item->key.offset - len, rollback);
+ }
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+
+ // FIXME - how do we know which RAID level to put this to?
+ flags = BLOCK_FLAG_DATA; // SINGLE
- if (keycmp(&tp2.item->key, &searchkey)) {
- ERR("error - extent %llx,%llx not found in tree\n", ed2->address, ed2->size);
- int3; // TESTING
- goto end2;
- }
+// if (!chunk_test) { // TESTING
+// if ((c = alloc_chunk(Vcb, flags, NULL))) {
+// ERR("chunk_item->type = %llx\n", c->chunk_item->type);
+// ERR("size = %llx\n", c->chunk_item->size);
+// ERR("used = %llx\n", c->used);
+//
+// if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+// if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
+// // chunk_test = TRUE;
+// ERR("SUCCESS\n");
+// return STATUS_SUCCESS;
+// } else
+// ERR(":-(\n");
+// } else
+// ERR("???\n");
+// }
+// }
- if (tp2.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
- NTSTATUS Status = convert_old_data_extent(Vcb, ed2->address, ed2->size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_old_data_extent returned %08x\n", Status);
- goto end2;
- }
-
- free_traverse_ptr(&tp2);
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, FALSE, data, changed_sector_list, rollback))
+ return STATUS_SUCCESS;
}
-
- if (keycmp(&tp2.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
- goto end2;
+
+ le = le->Flink;
+ }
+
+ if ((c = alloc_chunk(Vcb, flags, rollback))) {
+ if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
+ if (insert_extent_chunk(Vcb, fcb, c, start_data, length, FALSE, data, changed_sector_list, rollback))
+ return STATUS_SUCCESS;
}
}
- ei = (EXTENT_ITEM*)tp2.item->data;
+ // FIXME - rebalance chunks if free space elsewhere?
+ WARN("couldn't find any data chunks with %llx bytes free\n", length);
+
+ return STATUS_DISK_FULL;
+}
+
+void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
+ LIST_ENTRY* le = changed_sector_list->Flink;
+ changed_sector* cs;
+ traverse_ptr tp, next_tp;
+ KEY searchkey;
+ UINT32* data;
+ NTSTATUS Status;
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- goto end2;
+ if (!Vcb->checksum_root) {
+ ERR("no checksum root\n");
+ goto exit;
}
- // FIXME - test this
- if (extent_item_is_shared(ei, tp2.item->size - sizeof(EXTENT_ITEM))) {
- NTSTATUS Status = convert_shared_data_extent(Vcb, ed2->address, ed2->size, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("convert_shared_data_extent returned %08x\n", Status);
- goto end2;
- }
-
- free_traverse_ptr(&tp2);
-
- Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
- }
+ while (le != changed_sector_list) {
+ UINT64 startaddr, endaddr;
+ ULONG len;
+ UINT32* checksums;
+ RTL_BITMAP bmp;
+ ULONG* bmparr;
+ ULONG runlength, index;
- if (keycmp(&tp2.item->key, &searchkey)) {
- WARN("extent item not found for address %llx, size %llx\n", ed2->address, ed2->size);
- goto end2;
- }
+ cs = (changed_sector*)le;
- ei = (EXTENT_ITEM*)tp2.item->data;
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- goto end2;
- }
- }
-
- if (ei->refcount != 1) {
- TRACE("extent refcount was not 1\n");
- goto end2;
- }
-
- if (ei->flags != EXTENT_ITEM_DATA) {
- ERR("error - extent was not a data extent\n");
- goto end2;
- }
-
- c = get_chunk_from_address(Vcb, ed2->address);
-
- le = c->space.Flink;
- while (le != &c->space) {
- s = CONTAINING_RECORD(le, space, list_entry);
+ // FIXME - create checksum_root if it doesn't exist at all
- if (s->offset == ed2->address + ed2->size) {
- if (s->type == SPACE_TYPE_FREE && s->size >= length) {
- success = extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, &tp, &tp2, rollback);
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) { // tree is completely empty
+ // FIXME - do proper check here that tree is empty
+ if (!cs->deleted) {
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * cs->length, ALLOC_TAG);
+ if (!checksums) {
+ ERR("out of memory\n");
+ goto exit;
+ }
+
+ RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length);
+
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(checksums);
+ goto exit;
+ }
+ }
+ } else {
+ UINT32 tplen;
+
+ // FIXME - check entry is TYPE_EXTENT_CSUM?
+
+ if (tp.item->key.offset < cs->ol.key && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key)
+ startaddr = tp.item->key.offset;
+ else
+ startaddr = cs->ol.key;
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto exit;
+ }
+
+ tplen = tp.item->size / sizeof(UINT32);
+
+ if (tp.item->key.offset + (tplen * Vcb->superblock.sector_size) >= cs->ol.key + (cs->length * Vcb->superblock.sector_size))
+ endaddr = tp.item->key.offset + (tplen * Vcb->superblock.sector_size);
+ else
+ endaddr = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
+
+ TRACE("cs starts at %llx (%x sectors)\n", cs->ol.key, cs->length);
+ TRACE("startaddr = %llx\n", startaddr);
+ TRACE("endaddr = %llx\n", endaddr);
+
+ len = (endaddr - startaddr) / Vcb->superblock.sector_size;
+
+ checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * len, ALLOC_TAG);
+ if (!checksums) {
+ ERR("out of memory\n");
+ goto exit;
+ }
+
+ bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG);
+ if (!bmparr) {
+ ERR("out of memory\n");
+ ExFreePool(checksums);
+ goto exit;
+ }
+
+ RtlInitializeBitMap(&bmp, bmparr, len);
+ RtlSetAllBits(&bmp);
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = cs->ol.key;
+
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto exit;
+ }
+
+ // set bit = free space, cleared bit = allocated sector
+
+ // ERR("start loop\n");
+ while (tp.item->key.offset < endaddr) {
+ // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ if (tp.item->key.offset >= startaddr) {
+ if (tp.item->size > 0) {
+ RtlCopyMemory(&checksums[(tp.item->key.offset - startaddr) / Vcb->superblock.sector_size], tp.item->data, tp.item->size);
+ RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, tp.item->size / sizeof(UINT32));
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+ }
+
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+ tp = next_tp;
+ } else
+ break;
+ }
+ // ERR("end loop\n");
+
+ if (cs->deleted) {
+ RtlSetBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ } else {
+ RtlCopyMemory(&checksums[(cs->ol.key - startaddr) / Vcb->superblock.sector_size], cs->checksums, cs->length * sizeof(UINT32));
+ RtlClearBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ }
+
+ runlength = RtlFindFirstRunClear(&bmp, &index);
+
+ while (runlength != 0) {
+ do {
+ ULONG rl;
+
+ if (runlength * sizeof(UINT32) > MAX_CSUM_SIZE)
+ rl = MAX_CSUM_SIZE / sizeof(UINT32);
+ else
+ rl = runlength;
+
+ data = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * rl, ALLOC_TAG);
+ if (!data) {
+ ERR("out of memory\n");
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+ goto exit;
+ }
+
+ RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
+
+ if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(data);
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+ goto exit;
+ }
+
+ runlength -= rl;
+ index += rl;
+ } while (runlength > 0);
+
+ runlength = RtlFindNextForwardRunClear(&bmp, index, &index);
}
- break;
- } else if (s->offset > ed2->address + ed2->size)
- break;
+
+ ExFreePool(bmparr);
+ ExFreePool(checksums);
+ }
le = le->Flink;
}
-end2:
- free_traverse_ptr(&tp2);
-
-end:
- free_traverse_ptr(&tp);
+exit:
+ while (!IsListEmpty(changed_sector_list)) {
+ le = RemoveHeadList(changed_sector_list);
+ cs = (changed_sector*)le;
- return success;
+ if (cs->checksums)
+ ExFreePool(cs->checksums);
+
+ ExFreePool(cs);
+ }
}
-NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
- EXTENT_DATA* ed;
- EXTENT_DATA2* ed2;
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
+ LIST_ENTRY changed_sector_list;
+ NTSTATUS Status;
+ BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
- TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb, r->id, inode, start, length);
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
- ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
- if (!ed) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ // FIXME - convert into inline extent if short enough
+
+ Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
+ sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), nocsum ? NULL : &changed_sector_list, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents failed\n");
+ return Status;
}
- ed->generation = Vcb->superblock.generation;
- ed->decoded_size = length;
- ed->compression = BTRFS_COMPRESSION_NONE;
- ed->encryption = BTRFS_ENCRYPTION_NONE;
- ed->encoding = BTRFS_ENCODING_NONE;
- ed->type = EXTENT_TYPE_REGULAR;
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ // FIXME - inform cache manager of this
- ed2 = (EXTENT_DATA2*)ed->data;
- ed2->address = 0;
- ed2->size = 0;
- ed2->offset = 0;
- ed2->num_bytes = length;
+ TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
- if (!insert_tree_item(Vcb, r, inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
- ERR("insert_tree_item failed\n");
- ExFreePool(ed);
- return STATUS_INTERNAL_ERROR;
+ if (!nocsum)
+ update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback) {
+ UINT64 oldalloc, newalloc;
+ KEY searchkey;
+ traverse_ptr tp;
+ BOOL cur_inline;
+ NTSTATUS Status;
+
+ TRACE("(%p, %x, %p)\n", fcb, end, rollback);
+
+ if (fcb->ads)
+ return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE, rollback) ;
+ else {
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ oldalloc = 0;
+ if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ oldalloc = tp.item->key.offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
+ cur_inline = ed->type == EXTENT_TYPE_INLINE;
+
+ if (cur_inline && end > fcb->Vcb->max_inline) {
+ LIST_ENTRY changed_sector_list;
+ BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
+ UINT64 origlength, length;
+ UINT8* data;
+
+ TRACE("giving inline file proper extents\n");
+
+ origlength = ed->decoded_size;
+
+ cur_inline = FALSE;
+
+ if (!nocsum)
+ InitializeListHead(&changed_sector_list);
+
+ delete_tree_item(fcb->Vcb, &tp, rollback);
+
+ length = sector_align(origlength, fcb->Vcb->superblock.sector_size);
+
+ data = ExAllocatePoolWithTag(PagedPool, length, ALLOC_TAG);
+ if (!data) {
+ ERR("could not allocate %llx bytes for data\n", length);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (length > origlength)
+ RtlZeroMemory(data + origlength, length - origlength);
+
+ RtlCopyMemory(data, ed->data, origlength);
+
+ fcb->inode_item.st_blocks -= origlength;
+
+ Status = insert_extent(fcb->Vcb, fcb, tp.item->key.offset, length, data, nocsum ? NULL : &changed_sector_list, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_extent returned %08x\n", Status);
+ ExFreePool(data);
+ return Status;
+ }
+
+ oldalloc = tp.item->key.offset + length;
+
+ ExFreePool(data);
+
+ if (!nocsum)
+ update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
+ }
+
+ if (cur_inline) {
+ ULONG edsize;
+
+ if (end > oldalloc) {
+ edsize = sizeof(EXTENT_DATA) - 1 + end - tp.item->key.offset;
+ ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
+
+ if (!ed) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(ed, edsize);
+ RtlCopyMemory(ed, tp.item->data, tp.item->size);
+
+ ed->decoded_size = end - tp.item->key.offset;
+
+ delete_tree_item(fcb->Vcb, &tp, rollback);
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ed, edsize, NULL, rollback)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ed);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end);
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ fcb->inode_item.st_blocks = end;
+
+ fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
+ } else {
+ newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
+
+ if (newalloc > oldalloc) {
+ if (prealloc) {
+ // FIXME - try and extend previous extent first
+
+ Status = insert_prealloc_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_prealloc_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else {
+ Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, oldalloc, newalloc - oldalloc, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+ }
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ TRACE("newalloc = %llx\n", newalloc);
+
+ fcb->Header.AllocationSize.QuadPart = newalloc;
+ fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
+ }
+ } else {
+ if (end > fcb->Vcb->max_inline) {
+ newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
+
+ if (prealloc) {
+ Status = insert_prealloc_extent(fcb, 0, newalloc, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_prealloc_extent returned %08x\n", Status);
+ return Status;
+ }
+ } else {
+ Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, 0, newalloc, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_sparse_extent returned %08x\n", Status);
+ return Status;
+ }
+ }
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ TRACE("newalloc = %llx\n", newalloc);
+
+ fcb->Header.AllocationSize.QuadPart = newalloc;
+ fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
+ } else {
+ EXTENT_DATA* ed;
+ ULONG edsize;
+
+ edsize = sizeof(EXTENT_DATA) - 1 + end;
+ ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
+
+ if (!ed) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ed->generation = fcb->Vcb->superblock.generation;
+ ed->decoded_size = end;
+ ed->compression = BTRFS_COMPRESSION_NONE;
+ ed->encryption = BTRFS_ENCRYPTION_NONE;
+ ed->encoding = BTRFS_ENCODING_NONE;
+ ed->type = EXTENT_TYPE_INLINE;
+
+ RtlZeroMemory(ed->data, end);
+
+ if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed, edsize, NULL, rollback)) {
+ ERR("error - failed to insert item\n");
+ ExFreePool(ed);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ fcb->inode_item.st_size = end;
+ TRACE("setting st_size to %llx\n", end);
+
+ fcb->inode_item.st_blocks = end;
+
+ fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
+ }
+ }
}
return STATUS_SUCCESS;
}
-// static void print_tree(tree* t) {
-// LIST_ENTRY* le = t->itemlist.Flink;
-// while (le != &t->itemlist) {
-// tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
-// ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
-// le = le->Flink;
-// }
-// }
-
-static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
- LIST_ENTRY* le = Vcb->chunks.Flink;
- chunk* c;
+static UINT64 get_extent_item_refcount(device_extension* Vcb, UINT64 address) {
KEY searchkey;
- UINT64 flags;
-
- TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
-
- // FIXME - split data up if not enough space for just one extent
+ traverse_ptr tp;
+ EXTENT_ITEM* ei;
+ UINT64 rc;
+ NTSTATUS Status;
- if (start_data > 0 && try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, rollback))
- return STATUS_SUCCESS;
+ searchkey.obj_id = address;
+ searchkey.obj_type = TYPE_EXTENT_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
- // if there is a gap before start_data, plug it with a sparse extent
- if (start_data > 0) {
- traverse_ptr tp;
- NTSTATUS Status;
- EXTENT_DATA* ed;
- UINT64 len;
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_EXTENT_DATA;
- searchkey.offset = start_data;
-
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
-// if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
-// traverse_ptr next_tp;
-//
-// ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
-// searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-// print_tree(tp.tree);
-//
-// if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
-// ERR("---\n");
-// ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset);
-// print_tree(next_tp.tree);
-//
-// free_traverse_ptr(&next_tp);
-// } else
-// ERR("next item not found\n");
-//
-// int3;
-// free_traverse_ptr(&tp);
-// return STATUS_INTERNAL_ERROR;
-// }
-
- if (tp.item->key.obj_type == TYPE_EXTENT_DATA && tp.item->size >= sizeof(EXTENT_DATA)) {
- EXTENT_DATA2* ed2;
-
- ed = (EXTENT_DATA*)tp.item->data;
- ed2 = (EXTENT_DATA2*)ed->data;
-
- len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
- } else
- ed = NULL;
-
- if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || !ed || tp.item->key.offset + len < start_data) {
- if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
- Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data, rollback);
- else if (!ed)
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
- else {
- Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + len,
- start_data - tp.item->key.offset - len, rollback);
- }
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
- }
-
- free_traverse_ptr(&tp);
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return 0;
}
- // FIXME - how do we know which RAID level to put this to?
- flags = BLOCK_FLAG_DATA; // SINGLE
-
-// if (!chunk_test) { // TESTING
-// if ((c = alloc_chunk(Vcb, flags, NULL))) {
-// ERR("chunk_item->type = %llx\n", c->chunk_item->type);
-// ERR("size = %llx\n", c->chunk_item->size);
-// ERR("used = %llx\n", c->used);
-//
-// if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
-// if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
-// // chunk_test = TRUE;
-// ERR("SUCCESS\n");
-// return STATUS_SUCCESS;
-// } else
-// ERR(":-(\n");
-// } else
-// ERR("???\n");
-// }
-// }
-
- while (le != &Vcb->chunks) {
- c = CONTAINING_RECORD(le, chunk, list_entry);
-
- if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
- if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list, rollback))
- return STATUS_SUCCESS;
- }
-
- le = le->Flink;
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("error - could not find EXTENT_ITEM for %llx\n", address);
+ return 0;
}
- if ((c = alloc_chunk(Vcb, flags, rollback))) {
- if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
- if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list, rollback))
- return STATUS_SUCCESS;
- }
+ if (tp.item->size < sizeof(EXTENT_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
+ return 0;
}
- // FIXME - rebalance chunks if free space elsewhere?
- WARN("couldn't find any data chunks with %llx bytes free\n", length);
+ ei = (EXTENT_ITEM*)tp.item->data;
+ rc = ei->refcount;
+
+ return rc;
+}
- return STATUS_DISK_FULL;
+static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data) {
+ return is_file_prealloc_inode(fcb->Vcb, fcb->subvol, fcb->inode, start_data, end_data);
}
-void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
- LIST_ENTRY* le = changed_sector_list->Flink;
- changed_sector* cs;
- traverse_ptr tp, next_tp;
- KEY searchkey;
- UINT32* data;
+static NTSTATUS do_cow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
NTSTATUS Status;
- if (!Vcb->checksum_root) {
- ERR("no checksum root\n");
- goto exit;
+ Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, changed_sector_list, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - excise_extents returned %08x\n", Status);
+ goto end;
}
- while (le != changed_sector_list) {
- UINT64 startaddr, endaddr;
- ULONG len;
- UINT32* checksums;
- RTL_BITMAP bmp;
- ULONG* bmparr;
- ULONG runlength, index;
-
- cs = (changed_sector*)le;
-
- searchkey.obj_id = EXTENT_CSUM_ID;
- searchkey.obj_type = TYPE_EXTENT_CSUM;
- searchkey.offset = cs->ol.key;
-
- // FIXME - create checksum_root if it doesn't exist at all
-
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) { // tree is completely empty
- // FIXME - do proper check here that tree is empty
- if (!cs->deleted) {
- checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * cs->length, ALLOC_TAG);
- if (!checksums) {
- ERR("out of memory\n");
- goto exit;
- }
-
- RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length);
-
- if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, rollback)) {
- ERR("insert_tree_item failed\n");
- ExFreePool(checksums);
- goto exit;
- }
- }
- } else {
- UINT32 tplen;
-
- // FIXME - check entry is TYPE_EXTENT_CSUM?
-
- if (tp.item->key.offset < cs->ol.key && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key)
- startaddr = tp.item->key.offset;
- else
- startaddr = cs->ol.key;
-
- free_traverse_ptr(&tp);
-
- searchkey.obj_id = EXTENT_CSUM_ID;
- searchkey.obj_type = TYPE_EXTENT_CSUM;
- searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
-
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto exit;
- }
-
- tplen = tp.item->size / sizeof(UINT32);
-
- if (tp.item->key.offset + (tplen * Vcb->superblock.sector_size) >= cs->ol.key + (cs->length * Vcb->superblock.sector_size))
- endaddr = tp.item->key.offset + (tplen * Vcb->superblock.sector_size);
- else
- endaddr = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
-
- free_traverse_ptr(&tp);
-
- TRACE("cs starts at %llx (%x sectors)\n", cs->ol.key, cs->length);
- TRACE("startaddr = %llx\n", startaddr);
- TRACE("endaddr = %llx\n", endaddr);
-
- len = (endaddr - startaddr) / Vcb->superblock.sector_size;
-
- checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * len, ALLOC_TAG);
- if (!checksums) {
- ERR("out of memory\n");
- goto exit;
- }
+ Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, changed_sector_list, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - insert_extent returned %08x\n", Status);
+ goto end;
+ }
+
+ Status = STATUS_SUCCESS;
+
+end:
+ return Status;
+}
+
+static NTSTATUS merge_data_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+ BOOL b;
+ EXTENT_DATA* ed;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA) {
+ ERR("error - EXTENT_DATA not found\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (tp.item->key.offset > 0) {
+ traverse_ptr tp2, prev_tp;
+
+ tp2 = tp;
+ do {
+ b = find_prev_item(Vcb, &tp2, &prev_tp, FALSE);
- bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG);
- if (!bmparr) {
- ERR("out of memory\n");
- ExFreePool(checksums);
- goto exit;
- }
+ if (b) {
+ if (!prev_tp.item->ignore)
+ break;
- RtlInitializeBitMap(&bmp, bmparr, len);
- RtlSetAllBits(&bmp);
-
- searchkey.obj_id = EXTENT_CSUM_ID;
- searchkey.obj_type = TYPE_EXTENT_CSUM;
- searchkey.offset = cs->ol.key;
-
- Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto exit;
+ tp2 = prev_tp;
}
+ } while (b);
+
+ if (b) {
+ if (prev_tp.item->key.obj_id == fcb->inode && prev_tp.item->key.obj_type == TYPE_EXTENT_DATA)
+ tp = prev_tp;
+ }
+ }
+
+ ed = (EXTENT_DATA*)tp.item->data;
+ if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ do {
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+
+ if (b) {
+ EXTENT_DATA* ned;
- // set bit = free space, cleared bit = allocated sector
+ if (next_tp.item->key.obj_id != fcb->inode || next_tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ return STATUS_SUCCESS;
- // ERR("start loop\n");
- while (tp.item->key.offset < endaddr) {
- // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- if (tp.item->key.offset >= startaddr) {
- if (tp.item->size > 0) {
- RtlCopyMemory(&checksums[(tp.item->key.offset - startaddr) / Vcb->superblock.sector_size], tp.item->data, tp.item->size);
- RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, tp.item->size / sizeof(UINT32));
- }
-
- delete_tree_item(Vcb, &tp, rollback);
- }
-
- if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
- free_traverse_ptr(&tp);
- tp = next_tp;
- } else
- break;
+ if (next_tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset, next_tp.item->size, sizeof(EXTENT_DATA));
+ return STATUS_INTERNAL_ERROR;
}
- // ERR("end loop\n");
- free_traverse_ptr(&tp);
-
- if (cs->deleted) {
- RtlSetBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
- } else {
- RtlCopyMemory(&checksums[(cs->ol.key - startaddr) / Vcb->superblock.sector_size], cs->checksums, cs->length * sizeof(UINT32));
- RtlClearBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
+ ned = (EXTENT_DATA*)next_tp.item->data;
+ if ((ned->type == EXTENT_TYPE_REGULAR || ned->type == EXTENT_TYPE_PREALLOC) && next_tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset, next_tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ return STATUS_INTERNAL_ERROR;
}
- runlength = RtlFindFirstRunClear(&bmp, &index);
-
- while (runlength != 0) {
- do {
- ULONG rl;
-
- if (runlength * sizeof(UINT32) > MAX_CSUM_SIZE)
- rl = MAX_CSUM_SIZE / sizeof(UINT32);
- else
- rl = runlength;
+ if (ed->type == ned->type && (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC)) {
+ EXTENT_DATA2 *ed2, *ned2;
+
+ ed2 = (EXTENT_DATA2*)ed->data;
+ ned2 = (EXTENT_DATA2*)ned->data;
+
+ if (next_tp.item->key.offset == tp.item->key.offset + ed2->num_bytes && ed2->address == ned2->address && ed2->size == ned2->size && ned2->offset == ed2->offset + ed2->num_bytes) {
+ EXTENT_DATA* buf = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+ EXTENT_DATA2* buf2;
+ traverse_ptr tp2;
- data = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * rl, ALLOC_TAG);
- if (!data) {
+ if (!buf) {
ERR("out of memory\n");
- ExFreePool(bmparr);
- ExFreePool(checksums);
- goto exit;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
+ RtlCopyMemory(buf, tp.item->data, tp.item->size);
+ buf->generation = Vcb->superblock.generation;
- if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, rollback)) {
+ buf2 = (EXTENT_DATA2*)buf->data;
+ buf2->num_bytes += ned2->num_bytes;
+
+ delete_tree_item(Vcb, &tp, rollback);
+ delete_tree_item(Vcb, &next_tp, rollback);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, buf, tp.item->size, &tp2, rollback)) {
ERR("insert_tree_item failed\n");
- ExFreePool(data);
- ExFreePool(bmparr);
- ExFreePool(checksums);
- goto exit;
+ ExFreePool(buf);
+ return STATUS_INTERNAL_ERROR;
}
- runlength -= rl;
- index += rl;
- } while (runlength > 0);
-
- runlength = RtlFindNextForwardRunClear(&bmp, index, &index);
+ Status = decrease_extent_refcount_data(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - buf2->offset, 1, NULL, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("decrease_extent_refcount_data returned %08x\n", Status);
+ return Status;
+ }
+
+ tp = tp2;
+
+ continue;
+ }
}
-
- ExFreePool(bmparr);
- ExFreePool(checksums);
+
+ tp = next_tp;
+ ed = ned;
}
-
- le = le->Flink;
- }
+ } while (b);
-exit:
- while (!IsListEmpty(changed_sector_list)) {
- le = RemoveHeadList(changed_sector_list);
- cs = (changed_sector*)le;
-
- if (cs->checksums)
- ExFreePool(cs->checksums);
-
- ExFreePool(cs);
- }
+ return STATUS_SUCCESS;
}
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
- LIST_ENTRY changed_sector_list;
+static NTSTATUS do_prealloc_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
NTSTATUS Status;
- BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
-
- if (!nocsum)
- InitializeListHead(&changed_sector_list);
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ BOOL b, deleted_prealloc = FALSE;
+ UINT64 last_written = start_data;
- // FIXME - convert into inline extent if short enough
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_EXTENT_DATA;
+ searchkey.offset = start_data;
- Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
- sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), nocsum ? NULL : &changed_sector_list, rollback);
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
- ERR("error - excise_extents failed\n");
+ ERR("error - find_item returned %08x\n", Status);
return Status;
}
- fcb->inode_item.st_size = end;
- TRACE("setting st_size to %llx\n", end);
-
- fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
- fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
- fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
- // FIXME - inform cache manager of this
-
- TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
-
- if (!nocsum)
- update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
-
- return STATUS_SUCCESS;
-}
-
-NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
- UINT64 oldalloc, newalloc;
- KEY searchkey;
- traverse_ptr tp;
- BOOL cur_inline;
- NTSTATUS Status;
+ if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
+ return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, rollback);
- TRACE("(%p, %x, %p)\n", fcb, end, rollback);
-
- if (fcb->ads) {
- FIXME("FIXME - support streams here\n"); // FIXME
- return STATUS_NOT_IMPLEMENTED;
- } else {
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_EXTENT_DATA;
- searchkey.offset = 0xffffffffffffffff;
+ do {
+ EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+ return STATUS_INTERNAL_ERROR;
}
- oldalloc = 0;
- if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
- EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
-
- if (tp.item->size < sizeof(EXTENT_DATA)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
+ if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+
+ if (ed->type == EXTENT_TYPE_PREALLOC) {
+ if (tp.item->key.offset > last_written) {
+ Status = do_cow_write(Vcb, fcb, last_written, tp.item->key.offset, (UINT8*)data + last_written - start_data, changed_sector_list, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_cow_write returned %08x\n", Status);
+
+ return Status;
+ }
+
+ last_written = tp.item->key.offset;
}
- oldalloc = tp.item->key.offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
- cur_inline = ed->type == EXTENT_TYPE_INLINE;
-
- if (cur_inline && end > fcb->Vcb->max_inline) {
- LIST_ENTRY changed_sector_list;
- BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
- UINT64 origlength, length;
- UINT8* data;
+ if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed2->num_bytes) { // replace all
+ EXTENT_DATA* ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
- TRACE("giving inline file proper extents\n");
+ if (!ned) {
+ ERR("out of memory\n");
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- origlength = ed->decoded_size;
+ RtlCopyMemory(ned, tp.item->data, tp.item->size);
- cur_inline = FALSE;
+ ned->type = EXTENT_TYPE_REGULAR;
- if (!nocsum)
- InitializeListHead(&changed_sector_list);
+ delete_tree_item(Vcb, &tp, rollback);
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + tp.item->key.offset - start_data, ed2->num_bytes, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+
+ return Status;
+ }
+
+ deleted_prealloc = TRUE;
+
+ last_written = tp.item->key.offset + ed2->num_bytes;
+ } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed2->num_bytes) { // replace beginning
+ EXTENT_DATA *ned, *nedb;
+ EXTENT_DATA2* ned2;
+
+ ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+
+ if (!ned) {
+ ERR("out of memory\n");
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- delete_tree_item(fcb->Vcb, &tp, rollback);
+ delete_tree_item(Vcb, &tp, rollback);
- length = sector_align(origlength, fcb->Vcb->superblock.sector_size);
+ RtlCopyMemory(ned, tp.item->data, tp.item->size);
- data = ExAllocatePoolWithTag(PagedPool, length, ALLOC_TAG);
- if (!data) {
- ERR("could not allocate %llx bytes for data\n", length);
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
+ ned->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = end_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+
+ return STATUS_INTERNAL_ERROR;
}
- if (length > origlength)
- RtlZeroMemory(data + origlength, length - origlength);
+ RtlCopyMemory(nedb, tp.item->data, tp.item->size);
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += end_data - tp.item->key.offset;
+ ned2->num_bytes -= end_data - tp.item->key.offset;
- RtlCopyMemory(data, ed->data, origlength);
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, nedb, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(nedb);
+
+ return STATUS_INTERNAL_ERROR;
+ }
- fcb->inode_item.st_blocks -= origlength;
+ Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + tp.item->key.offset - start_data, end_data - tp.item->key.offset, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+
+ return Status;
+ }
- Status = insert_extent(fcb->Vcb, fcb, tp.item->key.offset, length, data, nocsum ? NULL : &changed_sector_list, rollback);
+ Status = increase_extent_refcount_data(Vcb, ned2->address, ned2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 1, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("insert_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- ExFreePool(data);
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
return Status;
}
- oldalloc = tp.item->key.offset + length;
+ last_written = end_data;
+ } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed2->num_bytes) { // replace end
+ EXTENT_DATA *ned, *nedb;
+ EXTENT_DATA2* ned2;
- ExFreePool(data);
+ // FIXME - test this
- if (!nocsum)
- update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
- }
-
- if (cur_inline) {
- ULONG edsize;
+ ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
- if (end > oldalloc) {
- edsize = sizeof(EXTENT_DATA) - 1 + end - tp.item->key.offset;
- ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
-
- if (!ed) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlZeroMemory(ed, edsize);
- RtlCopyMemory(ed, tp.item->data, tp.item->size);
-
- ed->decoded_size = end - tp.item->key.offset;
+ if (!ned) {
+ ERR("out of memory\n");
- delete_tree_item(fcb->Vcb, &tp, rollback);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
- if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ed, edsize, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(ed);
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end);
+ delete_tree_item(Vcb, &tp, rollback);
- fcb->inode_item.st_size = end;
- TRACE("setting st_size to %llx\n", end);
+ RtlCopyMemory(ned, tp.item->data, tp.item->size);
- fcb->inode_item.st_blocks = end;
-
- fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
- } else {
- newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
-
- if (newalloc > oldalloc) {
- Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, oldalloc, newalloc - oldalloc, rollback);
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
- if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return Status;
- }
+ return STATUS_INTERNAL_ERROR;
}
- fcb->inode_item.st_size = end;
- TRACE("setting st_size to %llx\n", end);
+ RtlCopyMemory(nedb, tp.item->data, tp.item->size);
- TRACE("newalloc = %llx\n", newalloc);
+ nedb->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += start_data - tp.item->key.offset;
+ ned2->num_bytes = tp.item->key.offset + ed2->num_bytes - start_data;
- fcb->Header.AllocationSize.QuadPart = newalloc;
- fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
- }
- } else {
- if (end > fcb->Vcb->max_inline) {
- newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
-
- Status = insert_sparse_extent(fcb->Vcb, fcb->subvol, fcb->inode, 0, newalloc, rollback);
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, nedb, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(nedb);
+
+ return STATUS_INTERNAL_ERROR;
+ }
+ Status = do_write_data(Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list);
if (!NT_SUCCESS(Status)) {
- ERR("insert_sparse_extent returned %08x\n", Status);
- free_traverse_ptr(&tp);
+ ERR("do_write_data returned %08x\n", Status);
+
return Status;
}
- fcb->inode_item.st_size = end;
- TRACE("setting st_size to %llx\n", end);
+ Status = increase_extent_refcount_data(Vcb, ned2->address, ned2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 1, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
+
+ return Status;
+ }
- TRACE("newalloc = %llx\n", newalloc);
+ last_written = start_data + ned2->num_bytes;
+ } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed2->num_bytes) { // replace middle
+ EXTENT_DATA *ned, *nedb, *nedc;
+ EXTENT_DATA2* ned2;
- fcb->Header.AllocationSize.QuadPart = newalloc;
- fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
- } else {
- EXTENT_DATA* ed;
- ULONG edsize;
+ // FIXME - test this
- edsize = sizeof(EXTENT_DATA) - 1 + end;
- ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
+ ned = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
- if (!ed) {
+ if (!ned) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
- ed->generation = fcb->Vcb->superblock.generation;
- ed->decoded_size = end;
- ed->compression = BTRFS_COMPRESSION_NONE;
- ed->encryption = BTRFS_ENCRYPTION_NONE;
- ed->encoding = BTRFS_ENCODING_NONE;
- ed->type = EXTENT_TYPE_INLINE;
+ nedb = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
- RtlZeroMemory(ed->data, end);
-
- if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed, edsize, NULL, rollback)) {
- ERR("error - failed to insert item\n");
- ExFreePool(ed);
- free_traverse_ptr(&tp);
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(ned);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nedc = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+
+ if (!nedb) {
+ ERR("out of memory\n");
+ ExFreePool(nedb);
+ ExFreePool(ned);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ delete_tree_item(Vcb, &tp, rollback);
+
+ RtlCopyMemory(ned, tp.item->data, tp.item->size);
+ RtlCopyMemory(nedb, tp.item->data, tp.item->size);
+ RtlCopyMemory(nedc, tp.item->data, tp.item->size);
+
+ ned2 = (EXTENT_DATA2*)ned->data;
+ ned2->num_bytes = start_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, tp.item->key.offset, ned, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(ned);
+ ExFreePool(nedb);
+ ExFreePool(nedc);
+
return STATUS_INTERNAL_ERROR;
}
- fcb->inode_item.st_size = end;
- TRACE("setting st_size to %llx\n", end);
+ nedb->type = EXTENT_TYPE_REGULAR;
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ ned2->offset += start_data - tp.item->key.offset;
+ ned2->num_bytes = end_data - start_data;
- fcb->inode_item.st_blocks = end;
-
- fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start_data, nedb, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(nedb);
+ ExFreePool(nedc);
+
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ned2 = (EXTENT_DATA2*)nedc->data;
+ ned2->offset += end_data - tp.item->key.offset;
+ ned2->num_bytes -= end_data - tp.item->key.offset;
+
+ if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, nedc, tp.item->size, NULL, rollback)) {
+ ERR("insert_tree_item failed\n");
+ ExFreePool(nedc);
+
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ned2 = (EXTENT_DATA2*)nedb->data;
+ Status = do_write_data(Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list);
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_write_data returned %08x\n", Status);
+
+ return Status;
+ }
+
+ Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, 2, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("increase_extent_refcount_data returned %08x\n", Status);
+ return Status;
+ }
+
+ last_written = end_data;
}
}
- free_traverse_ptr(&tp);
- }
-
- return STATUS_SUCCESS;
-}
-
-static UINT64 get_extent_item_refcount(device_extension* Vcb, UINT64 address) {
- KEY searchkey;
- traverse_ptr tp;
- EXTENT_ITEM* ei;
- UINT64 rc;
- NTSTATUS Status;
-
- searchkey.obj_id = address;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return 0;
- }
+ if (b) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > fcb->inode || tp.item->key.obj_type > TYPE_EXTENT_DATA || tp.item->key.offset >= end_data)
+ break;
+ }
+ } while (b);
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("error - could not find EXTENT_ITEM for %llx\n", address);
- free_traverse_ptr(&tp);
- return 0;
+ if (last_written < end_data) {
+ Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("do_cow_write returned %08x\n", Status);
+ return Status;
+ }
}
- if (tp.item->size < sizeof(EXTENT_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
- free_traverse_ptr(&tp);
- return 0;
+ Status = merge_data_extents(Vcb, fcb, start_data, end_data, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("merge_data_extents returned %08x\n", Status);
+ return Status;
}
- ei = (EXTENT_ITEM*)tp.item->data;
- rc = ei->refcount;
-
- free_traverse_ptr(&tp);
+ if (deleted_prealloc && !is_file_prealloc(fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size)))
+ fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
- return rc;
+ return STATUS_SUCCESS;
}
static NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
last_write = new_end;
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data + length)
Status = STATUS_SUCCESS;
end:
- free_traverse_ptr(&tp);
return Status;
}
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- goto failure2;
+ goto failure;
}
if (keycmp(&searchkey, &tp.item->key)) {
}
while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
- if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type) {
- free_traverse_ptr(&next_tp);
+ if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type)
break;
- }
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->size < sizeof(EXTENT_DATA)) {
// goto failure;
// }
- free_traverse_ptr(&tp);
-
return;
failure:
- free_traverse_ptr(&tp);
-
-failure2:
if (fcb->subvol->treeholder.tree)
print_loaded_trees(fcb->subvol->treeholder.tree, 0);
LARGE_INTEGER time;
BTRFS_TIME now;
fcb* fcb;
+ ccb* ccb;
+ file_ref* fileref;
BOOL paging_lock = FALSE;
TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
}
fcb = FileObject->FsContext;
+ ccb = FileObject->FsContext2;
+ fileref = ccb ? ccb->fileref : NULL;
if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb);
- return STATUS_ACCESS_DENIED;
+ return STATUS_INVALID_DEVICE_REQUEST;
}
if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1) {
if (paging_io) {
if (offset.QuadPart >= newlength) {
TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, offset.QuadPart, *length);
- TRACE("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ TRACE("filename %S\n", file_desc(FileObject));
TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
Status = STATUS_SUCCESS;
if (changed_length) {
if (newlength > fcb->Header.AllocationSize.QuadPart) {
- Status = extend_file(fcb, newlength, rollback);
+ Status = extend_file(fcb, fileref, newlength, FALSE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("extend_file returned %08x\n", Status);
goto end;
// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
wait = TRUE;
- TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf);
- if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
- TRACE("CcCopyWrite failed.\n");
-
- IoMarkIrpPending(Irp);
- Status = STATUS_PENDING;
+ if (IrpSp->MinorFunction & IRP_MN_MDL) {
+ CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus);
+
+ Status = Irp->IoStatus.Status;
goto end;
+ } else {
+ TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf);
+ if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
+ TRACE("CcCopyWrite failed.\n");
+
+ IoMarkIrpPending(Irp);
+ Status = STATUS_PENDING;
+ goto end;
+ }
+ TRACE("CcCopyWrite finished\n");
}
- TRACE("CcCopyWrite finished\n");
Status = STATUS_SUCCESS;
goto end;
if (keycmp(&tp.item->key, &searchkey)) {
ERR("error - could not find key for xattr\n");
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
if (tp.item->size < datalen) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen);
- free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto end;
}
maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
- free_traverse_ptr(&tp);
-
if (newlength > maxlen) {
ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen);
Status = STATUS_DISK_FULL;
if (!nocsum)
InitializeListHead(&changed_sector_list);
- if (make_inline || !nocow) {
+ if (make_inline) {
Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, nocsum ? NULL : &changed_sector_list, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - excise_extents returned %08x\n", Status);
goto end;
}
- if (!make_inline) {
- Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list, rollback);
+ ed2 = (EXTENT_DATA*)data;
+ ed2->generation = fcb->Vcb->superblock.generation;
+ ed2->decoded_size = newlength;
+ ed2->compression = BTRFS_COMPRESSION_NONE;
+ ed2->encryption = BTRFS_ENCRYPTION_NONE;
+ ed2->encoding = BTRFS_ENCODING_NONE;
+ ed2->type = EXTENT_TYPE_INLINE;
+
+ insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, NULL, rollback);
+
+ fcb->inode_item.st_blocks += newlength;
+ } else if (!nocow) {
+ if (is_file_prealloc(fcb, start_data, end_data)) {
+ Status = do_prealloc_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("error - insert_extent returned %08x\n", Status);
+ ERR("error - do_prealloc_write returned %08x\n", Status);
ExFreePool(data);
goto end;
}
-
- ExFreePool(data);
} else {
- ed2 = (EXTENT_DATA*)data;
- ed2->generation = fcb->Vcb->superblock.generation;
- ed2->decoded_size = newlength;
- ed2->compression = BTRFS_COMPRESSION_NONE;
- ed2->encryption = BTRFS_ENCRYPTION_NONE;
- ed2->encoding = BTRFS_ENCODING_NONE;
- ed2->type = EXTENT_TYPE_INLINE;
-
- insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, NULL, rollback);
+ Status = do_cow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, rollback);
- fcb->inode_item.st_blocks += newlength;
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - do_cow_write returned %08x\n", Status);
+ ExFreePool(data);
+ goto end;
+ }
}
+
+ ExFreePool(data);
} else {
Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data - start_data, data, nocsum ? NULL : &changed_sector_list, rollback);
// }
// }
- if (fcb->ads)
- origii = &fcb->par->inode_item;
- else
+ if (fcb->ads) {
+ if (fileref && fileref->parent)
+ origii = &fileref->parent->fcb->inode_item;
+ else {
+ ERR("no parent fcb found for stream\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
+ }
+ } else
origii = &fcb->inode_item;
origii->transid = Vcb->superblock.generation;
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
RtlCopyMemory(ii, origii, sizeof(INODE_ITEM));
insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
- free_traverse_ptr(&tp);
-
// FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
if (!nocsum)
Irp->IoStatus.Information = 0;
- switch (IrpSp->MinorFunction) {
- case IRP_MN_COMPLETE:
- FIXME("unsupported - IRP_MN_COMPLETE\n");
- break;
-
- case IRP_MN_COMPLETE_MDL:
- FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
- break;
-
- case IRP_MN_COMPLETE_MDL_DPC:
- FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
- break;
-
- case IRP_MN_COMPRESSED:
- FIXME("unsupported - IRP_MN_COMPRESSED\n");
- break;
-
- case IRP_MN_DPC:
- FIXME("unsupported - IRP_MN_DPC\n");
- break;
-
- case IRP_MN_MDL:
- FIXME("unsupported - IRP_MN_MDL\n");
- break;
-
- case IRP_MN_MDL_DPC:
- FIXME("unsupported - IRP_MN_MDL_DPC\n");
- break;
-
- case IRP_MN_NORMAL:
- TRACE("IRP_MN_NORMAL\n");
- break;
-
- default:
- WARN("unknown minor function %x\n", IrpSp->MinorFunction);
- break;
- }
-
TRACE("offset = %llx\n", offset.QuadPart);
TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
TRACE("buf = %p\n", buf);
- acquire_tree_lock(Vcb, TRUE);
- locked = TRUE;
+ if (Irp->Flags & IRP_NOCACHE) {
+ acquire_tree_lock(Vcb, TRUE);
+ locked = TRUE;
+ }
if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
WARN("tried to write to locked region\n");
goto exit;
}
- Status = consider_write(Vcb);
+ if (locked)
+ Status = consider_write(Vcb);
if (NT_SUCCESS(Status)) {
Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
#ifdef DEBUG_PARANOID
- check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
+ if (locked)
+ check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
// check_extent_tree_consistent(Vcb);
#endif