static WCHAR datastring[] = L"::$DATA";
-static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
- UINT64 index;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_INODE_REF;
- searchkey.offset = parinode;
-
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(tp.item->key, searchkey)) {
- INODE_REF* ir;
- ULONG len;
-
- index = 0;
-
- ir = (INODE_REF*)tp.item->data;
- len = tp.item->size;
-
- do {
- ULONG itemlen;
-
- if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
-
- if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
- index = ir->index;
- break;
- }
-
- if (len > itemlen) {
- len -= itemlen;
- ir = (INODE_REF*)&ir->name[ir->n];
- } else
- break;
- } while (len > 0);
-
- if (index == 0)
- return STATUS_NOT_FOUND;
-
- *pindex = index;
-
- return STATUS_SUCCESS;
- } else
- return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
- UINT64 index;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_INODE_EXTREF;
- searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
-
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(tp.item->key, searchkey)) {
- INODE_EXTREF* ier;
- ULONG len;
-
- index = 0;
-
- ier = (INODE_EXTREF*)tp.item->data;
- len = tp.item->size;
-
- do {
- ULONG itemlen;
-
- if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
-
- if (ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
- index = ier->index;
- break;
- }
-
- if (len > itemlen) {
- len -= itemlen;
- ier = (INODE_EXTREF*)&ier->name[ier->n];
- } else
- break;
- } while (len > 0);
-
- if (index == 0)
- return STATUS_NOT_FOUND;
-
- *pindex = index;
-
- return STATUS_SUCCESS;
- } else
- return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
- ROOT_REF* rr;
-
- searchkey.obj_id = r->id;
- searchkey.obj_type = TYPE_ROOT_REF;
- searchkey.offset = subvolid;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(tp.item->key, searchkey)) {
- ERR("couldn't find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (tp.item->size < sizeof(ROOT_REF)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
- tp.item->size, sizeof(ROOT_REF));
- return STATUS_INTERNAL_ERROR;
- }
-
- rr = (ROOT_REF*)tp.item->data;
-
- if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) {
- ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
- tp.item->size, sizeof(ROOT_REF) - 1 + rr->n);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(utf8->Buffer, rr->name, rr->n) == rr->n) {
- *pindex = rr->index;
- return STATUS_SUCCESS;
- } else
- return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) {
- KEY searchkey;
- traverse_ptr tp, next_tp;
- NTSTATUS Status;
- BOOL b;
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_DIR_INDEX;
- searchkey.offset = 2;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (keycmp(tp.item->key, searchkey) == -1) {
- if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
- tp = next_tp;
-
- TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- }
- }
-
- if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
- Status = STATUS_SUCCESS;
- goto end;
- }
-
- do {
- DIR_ITEM* di;
-
- TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- di = (DIR_ITEM*)tp.item->data;
-
- if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
- WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- } else {
- index_entry* ie;
- ULONG stringlen;
- UNICODE_STRING us;
- LIST_ENTRY* le;
- BOOL inserted;
-
- ie = ExAllocatePoolWithTag(PagedPool, sizeof(index_entry), ALLOC_TAG);
- if (!ie) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- ie->utf8.Length = ie->utf8.MaximumLength = di->n;
-
- if (di->n > 0) {
- ie->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, ie->utf8.MaximumLength, ALLOC_TAG);
- if (!ie->utf8.Buffer) {
- ERR("out of memory\n");
- ExFreePool(ie);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- RtlCopyMemory(ie->utf8.Buffer, di->name, di->n);
- } else
- ie->utf8.Buffer = NULL;
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- ExFreePool(ie);
- goto nextitem;
- }
-
- if (stringlen == 0) {
- ERR("UTF8 length was 0\n");
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- ExFreePool(ie);
- goto nextitem;
- }
-
- us.Length = us.MaximumLength = stringlen;
- us.Buffer = ExAllocatePoolWithTag(PagedPool, us.MaximumLength, ALLOC_TAG);
-
- if (!us.Buffer) {
- ERR("out of memory\n");
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- ExFreePool(ie);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(us.Buffer, stringlen, &stringlen, di->name, di->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(us.Buffer);
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- ExFreePool(ie);
- goto nextitem;
- }
-
- Status = RtlUpcaseUnicodeString(&ie->filepart_uc, &us, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- ExFreePool(us.Buffer);
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- ExFreePool(ie);
- goto nextitem;
- }
-
- ie->key = di->key;
- ie->type = di->type;
- ie->index = tp.item->key.offset;
-
- ie->hash = calc_crc32c(0xfffffffe, (UINT8*)ie->filepart_uc.Buffer, (ULONG)ie->filepart_uc.Length);
- inserted = FALSE;
-
- le = fcb->index_list.Flink;
- while (le != &fcb->index_list) {
- index_entry* ie2 = CONTAINING_RECORD(le, index_entry, list_entry);
-
- if (ie2->hash >= ie->hash) {
- InsertHeadList(le->Blink, &ie->list_entry);
- inserted = TRUE;
- break;
- }
-
- le = le->Flink;
- }
-
- if (!inserted)
- InsertTailList(&fcb->index_list, &ie->list_entry);
- }
-
-nextitem:
- b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
-
- if (b) {
- tp = next_tp;
-
- b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX;
- }
- } while (b);
-
- Status = STATUS_SUCCESS;
-
-end:
- if (!NT_SUCCESS(Status)) {
- while (!IsListEmpty(&fcb->index_list)) {
- LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
- index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
-
- if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
- if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
- ExFreePool(ie);
- }
- } else
- mark_fcb_dirty(fcb); // It's not necessarily dirty, but this is an easy way of making sure
- // the list remains in memory until the next flush.
-
- return Status;
-}
-
-static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
- UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
- LIST_ENTRY* le;
- NTSTATUS Status;
- UNICODE_STRING us;
- UINT32 hash;
-
- Status = RtlUpcaseUnicodeString(&us, filename, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- return Status;
- }
-
- hash = calc_crc32c(0xfffffffe, (UINT8*)us.Buffer, (ULONG)us.Length);
-
- ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
-
- if (!fr->fcb->index_loaded) {
- Status = load_index_list(fr->fcb, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("load_index_list returned %08x\n", Status);
- goto end;
- }
-
- fr->fcb->index_loaded = TRUE;
- }
-
- ExConvertExclusiveToSharedLite(&fr->fcb->nonpaged->index_lock);
-
- le = fr->fcb->index_list.Flink;
- while (le != &fr->fcb->index_list) {
- index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
-
- if (ie->hash == hash && ie->filepart_uc.Length == us.Length && RtlCompareMemory(ie->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) {
- LIST_ENTRY* le;
- BOOL ignore_entry = FALSE;
-
- ExAcquireResourceSharedLite(&fr->nonpaged->children_lock, TRUE);
-
- le = fr->children.Flink;
- while (le != &fr->children) {
- file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
-
- if (fr2->index == ie->index) {
- if (fr2->deleted || fr2->filepart_uc.Length != us.Length ||
- RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) != us.Length) {
- ignore_entry = TRUE;
- break;
- }
- break;
- } else if (fr2->index > ie->index)
- break;
-
- le = le->Flink;
- }
-
- ExReleaseResourceLite(&fr->nonpaged->children_lock);
-
- if (ignore_entry)
- goto nextitem;
-
- if (ie->key.obj_type == TYPE_ROOT_ITEM) {
- if (subvol) {
- *subvol = NULL;
-
- le = fr->fcb->Vcb->roots.Flink;
- while (le != &fr->fcb->Vcb->roots) {
- root* r2 = CONTAINING_RECORD(le, root, list_entry);
-
- if (r2->id == ie->key.obj_id) {
- *subvol = r2;
- break;
- }
-
- le = le->Flink;
- }
- }
-
- if (inode)
- *inode = SUBVOL_ROOT_INODE;
-
- if (type)
- *type = BTRFS_TYPE_DIRECTORY;
- } else {
- if (subvol)
- *subvol = fr->fcb->subvol;
-
- if (inode)
- *inode = ie->key.obj_id;
-
- if (type)
- *type = ie->type;
- }
-
- if (utf8) {
- utf8->MaximumLength = utf8->Length = ie->utf8.Length;
- utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
- if (!utf8->Buffer) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- RtlCopyMemory(utf8->Buffer, ie->utf8.Buffer, ie->utf8.Length);
- }
-
- if (pindex)
- *pindex = ie->index;
-
- Status = STATUS_SUCCESS;
- goto end;
- } else if (ie->hash > hash) {
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
- goto end;
- }
-
-nextitem:
- le = le->Flink;
- }
-
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
-
-end:
- ExReleaseResourceLite(&fr->fcb->nonpaged->index_lock);
-
- ExFreePool(us.Buffer);
-
- return Status;
-}
-
-static NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
- root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8,
- BOOL case_sensitive, PIRP Irp) {
- DIR_ITEM* di;
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
- ULONG stringlen;
-
- TRACE("(%p, %.*S, %08x, (%llx, %llx), %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32,
- fr->fcb->subvol->id, fr->fcb->inode, subvol, inode, type);
-
- searchkey.obj_id = fr->fcb->inode;
- searchkey.obj_type = TYPE_DIR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if (!keycmp(searchkey, tp.item->key)) {
- UINT32 size = tp.item->size;
-
- // found by hash
-
- if (tp.item->size < sizeof(DIR_ITEM)) {
- WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", fr->fcb->subvol->id, tp.item->key.obj_id, tp.item->key.obj_type,
- tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
- } else {
- di = (DIR_ITEM*)tp.item->data;
-
- while (size > 0) {
- if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
- WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- size -= sizeof(DIR_ITEM) - sizeof(char);
- size -= di->n;
- size -= di->m;
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- } else {
- WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
- UNICODE_STRING us;
-
- if (!utf16) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
-
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- } else {
- ANSI_STRING nutf8;
-
- us.Buffer = utf16;
- us.Length = us.MaximumLength = (USHORT)stringlen;
-
- if (FsRtlAreNamesEqual(filename, &us, !case_sensitive, NULL)) {
- UINT64 index;
-
- if (di->key.obj_type == TYPE_ROOT_ITEM) {
- LIST_ENTRY* le = Vcb->roots.Flink;
-
- if (subvol) {
- *subvol = NULL;
-
- while (le != &Vcb->roots) {
- root* r2 = CONTAINING_RECORD(le, root, list_entry);
-
- if (r2->id == di->key.obj_id) {
- *subvol = r2;
- break;
- }
-
- le = le->Flink;
- }
- }
-
- if (inode)
- *inode = SUBVOL_ROOT_INODE;
-
- if (type)
- *type = BTRFS_TYPE_DIRECTORY;
- } else {
- if (subvol)
- *subvol = fr->fcb->subvol;
-
- if (inode)
- *inode = di->key.obj_id;
-
- if (type)
- *type = di->type;
- }
-
- if (utf8) {
- utf8->MaximumLength = di->n;
- utf8->Length = utf8->MaximumLength;
- utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
- if (!utf8->Buffer) {
- ERR("out of memory\n");
- ExFreePool(utf16);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(utf8->Buffer, di->name, di->n);
- }
-
- ExFreePool(utf16);
-
- index = 0;
-
- if (fr->fcb->subvol != Vcb->root_root) {
- nutf8.Buffer = di->name;
- nutf8.Length = nutf8.MaximumLength = di->n;
-
- if (di->key.obj_type == TYPE_ROOT_ITEM) {
- Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("find_subvol_dir_index returned %08x\n", Status);
- return Status;
- }
- } else {
- Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
- if (!NT_SUCCESS(Status)) {
- if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
- Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
-
- if (!NT_SUCCESS(Status)) {
- ERR("find_file_dir_index_extref returned %08x\n", Status);
- return Status;
- }
- } else {
- ERR("find_file_dir_index returned %08x\n", Status);
- return Status;
- }
- }
- }
- }
-
- if (index != 0) {
- LIST_ENTRY* le = fr->children.Flink;
-
- while (le != &fr->children) {
- file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
-
- if (fr2->index == index) {
- if (fr2->deleted || !FsRtlAreNamesEqual(&fr2->filepart, filename, !case_sensitive, NULL)) {
- goto byindex;
- }
- break;
- } else if (fr2->index > index)
- break;
-
- le = le->Flink;
- }
- }
-
-// TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
-
- if (pindex)
- *pindex = index;
-
- return STATUS_SUCCESS;
- }
- }
-
- ExFreePool(utf16);
- }
-
- di = (DIR_ITEM*)&di->name[di->n + di->m];
- }
- }
- }
-
-byindex:
- if (case_sensitive)
- return STATUS_OBJECT_NAME_NOT_FOUND;
-
- Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8, Irp);
- if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
- ERR("find_file_in_dir_index returned %08x\n", Status);
- return Status;
- }
-
- return Status;
-}
-
fcb* create_fcb(POOL_TYPE pool_type) {
fcb* fcb;
ExInitializeResourceLite(&fcb->nonpaged->resource);
fcb->Header.Resource = &fcb->nonpaged->resource;
- ExInitializeResourceLite(&fcb->nonpaged->index_lock);
+ ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
InitializeListHead(&fcb->extents);
- InitializeListHead(&fcb->index_list);
InitializeListHead(&fcb->hardlinks);
+ InitializeListHead(&fcb->dir_children_index);
+ InitializeListHead(&fcb->dir_children_hash);
+ InitializeListHead(&fcb->dir_children_hash_uc);
+
return fcb;
}
return NULL;
}
- fr->refcount = 1;
-
-#ifdef DEBUG_FCB_REFCOUNTS
- WARN("fileref %p: refcount now 1\n", fr);
-#endif
-
- InitializeListHead(&fr->children);
-
- ExInitializeResourceLite(&fr->nonpaged->children_lock);
-
- return fr;
-}
+ fr->refcount = 1;
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ WARN("fileref %p: refcount now 1\n", fr);
+#endif
+
+ InitializeListHead(&fr->children);
+
+ ExInitializeResourceLite(&fr->nonpaged->children_lock);
+
+ return fr;
+}
+
+static NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, fcb* fcb,
+ root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive, PIRP Irp) {
+ NTSTATUS Status;
+ UNICODE_STRING fnus;
+ UINT32 hash;
+ LIST_ENTRY* le;
+ UINT8 c;
+
+ if (!case_sensitive) {
+ Status = RtlUpcaseUnicodeString(&fnus, filename, TRUE);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ return Status;
+ }
+ } else
+ fnus = *filename;
+
+ hash = calc_crc32c(0xffffffff, (UINT8*)fnus.Buffer, fnus.Length);
+
+ c = hash >> 24;
+
+ ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
+
+ if (case_sensitive) {
+ if (!fcb->hash_ptrs[c]) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ }
+
+ le = fcb->hash_ptrs[c];
+ while (le != &fcb->dir_children_hash) {
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
+
+ if (dc->hash == hash) {
+ if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
+ if (dc->key.obj_type == TYPE_ROOT_ITEM) {
+ LIST_ENTRY* le2;
+
+ *subvol = NULL;
+
+ le2 = fcb->Vcb->roots.Flink;
+ while (le2 != &fcb->Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le2, root, list_entry);
+
+ if (r2->id == dc->key.obj_id) {
+ *subvol = r2;
+ break;
+ }
+
+ le2 = le2->Flink;
+ }
+
+ *inode = SUBVOL_ROOT_INODE;
+ } else {
+ *subvol = fcb->subvol;
+ *inode = dc->key.obj_id;
+ }
+
+ *pdc = dc;
+
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+ } else if (dc->hash > hash) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ }
+
+ le = le->Flink;
+ }
+ } else {
+ if (!fcb->hash_ptrs_uc[c]) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ }
+
+ le = fcb->hash_ptrs_uc[c];
+ while (le != &fcb->dir_children_hash_uc) {
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
+
+ if (dc->hash_uc == hash) {
+ if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
+ if (dc->key.obj_type == TYPE_ROOT_ITEM) {
+ LIST_ENTRY* le2;
+
+ *subvol = NULL;
+
+ le2 = fcb->Vcb->roots.Flink;
+ while (le2 != &fcb->Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le2, root, list_entry);
+
+ if (r2->id == dc->key.obj_id) {
+ *subvol = r2;
+ break;
+ }
+
+ le2 = le2->Flink;
+ }
+
+ *inode = SUBVOL_ROOT_INODE;
+ } else {
+ *subvol = fcb->subvol;
+ *inode = dc->key.obj_id;
+ }
+
+ *pdc = dc;
+
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+ } else if (dc->hash_uc > hash) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto end;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
-NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
- root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8,
- BOOL case_sensitive, PIRP Irp) {
- char* fn;
- UINT32 crc32;
- ULONG utf8len;
- NTSTATUS Status;
-
- Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
- return Status;
- }
-
- fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
- if (!fn) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
- if (!NT_SUCCESS(Status)) {
- ExFreePool(fn);
- ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
- return Status;
- }
-
- TRACE("%.*s\n", utf8len, fn);
+end:
+ ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
- TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
+ if (!case_sensitive)
+ ExFreePool(fnus.Buffer);
- return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, case_sensitive, Irp);
+ return Status;
}
static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
return deleted;
}
+NTSTATUS load_csum(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ UINT64 i, j;
+ BOOL b;
+
+ searchkey.obj_id = EXTENT_CSUM_ID;
+ searchkey.obj_type = TYPE_EXTENT_CSUM;
+ searchkey.offset = start;
+
+ Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ i = 0;
+ do {
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ ULONG readlen;
+
+ if (start < tp.item->key.offset)
+ j = 0;
+ else
+ j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i;
+
+ if (j * sizeof(UINT32) > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
+ ERR("checksum not found for %llx\n", start + (i * Vcb->superblock.sector_size));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ readlen = min((tp.item->size / sizeof(UINT32)) - j, length - i);
+ RtlCopyMemory(&csum[i], tp.item->data + (j * sizeof(UINT32)), readlen * sizeof(UINT32));
+ i += readlen;
+
+ if (i == length)
+ break;
+ }
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
+ if (b)
+ tp = next_tp;
+ } while (b);
+
+ if (i < length) {
+ ERR("could not read checksums: offset %llx, length %llx sectors\n", start, length);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS load_dir_children(fcb* fcb, BOOL ignore_size, PIRP Irp) {
+ KEY searchkey;
+ traverse_ptr tp, next_tp;
+ NTSTATUS Status;
+
+ fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!fcb->hash_ptrs) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+
+ fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!fcb->hash_ptrs_uc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+
+ if (!ignore_size && fcb->inode_item.st_size == 0)
+ return STATUS_SUCCESS;
+
+ searchkey.obj_id = fcb->inode;
+ searchkey.obj_type = TYPE_DIR_INDEX;
+ searchkey.offset = 2;
+
+ Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (keycmp(tp.item->key, searchkey) == -1) {
+ if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
+ tp = next_tp;
+ TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ }
+ }
+
+ while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+ dir_child* dc;
+ ULONG utf16len;
+
+ if (tp.item->size < sizeof(DIR_ITEM)) {
+ 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(DIR_ITEM));
+ goto cont;
+ }
+
+ if (di->n == 0) {
+ WARN("(%llx,%x,%llx): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+ goto cont;
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ goto cont;
+ }
+
+ dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+ if (!dc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dc->key = di->key;
+ dc->index = tp.item->key.offset;
+ dc->type = di->type;
+ dc->fileref = NULL;
+
+ dc->utf8.MaximumLength = dc->utf8.Length = di->n;
+ dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
+ if (!dc->utf8.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
+
+ dc->name.MaximumLength = dc->name.Length = utf16len;
+ dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
+ if (!dc->name.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlUTF8ToUnicodeN(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ goto cont;
+ }
+
+ Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ goto cont;
+ }
+
+ dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
+ dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
+
+ InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+
+ insert_dir_child_into_hash_lists(fcb, dc);
+
+cont:
+ if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp))
+ tp = next_tp;
+ else
+ break;
+ }
+
+ return STATUS_SUCCESS;
+}
+
NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
KEY searchkey;
- traverse_ptr tp;
+ traverse_ptr tp, next_tp;
NTSTATUS Status;
fcb* fcb;
- BOOL b;
- UINT8* eadata;
- UINT16 ealen;
+ BOOL atts_set = FALSE, sd_set = FALSE, no_data;
+ LIST_ENTRY* lastle = NULL;
+ EXTENT_DATA* ed = NULL;
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) {
+ if (fcb->inode == inode) {
+ if (!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);
+ 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);
+ InterlockedIncrement(&fcb->refcount);
#endif
- *pfcb = fcb;
- return STATUS_SUCCESS;
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+ }
+ } else if (fcb->inode > inode) {
+ lastle = le->Blink;
+ break;
}
le = le->Flink;
else if (fcb->inode_item.st_mode & __S_IFBLK)
fcb->type = BTRFS_TYPE_BLOCKDEV;
else if (fcb->inode_item.st_mode & __S_IFIFO)
- fcb->type = BTRFS_TYPE_FIFO;
- else if (fcb->inode_item.st_mode & __S_IFLNK)
- fcb->type = BTRFS_TYPE_SYMLINK;
- else if (fcb->inode_item.st_mode & __S_IFSOCK)
- fcb->type = BTRFS_TYPE_SOCKET;
- else
- fcb->type = BTRFS_TYPE_FILE;
- }
-
- fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp);
-
- fcb_get_sd(fcb, parent, Irp);
-
- if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
- UINT8* xattrdata;
- UINT16 xattrlen;
-
- if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen, Irp)) {
- fcb->reparse_xattr.Buffer = (char*)xattrdata;
- fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
- } else {
- fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-
- if (!Vcb->readonly && !(subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
- fcb->atts_changed = TRUE;
- mark_fcb_dirty(fcb);
- }
- }
- }
-
- fcb->ealen = 0;
-
- if (get_xattr(Vcb, subvol, inode, EA_EA, EA_EA_HASH, &eadata, &ealen, Irp)) {
- ULONG offset;
-
- Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
-
- if (!NT_SUCCESS(Status)) {
- WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
- ExFreePool(eadata);
- } else {
- FILE_FULL_EA_INFORMATION* eainfo;
- fcb->ea_xattr.Buffer = (char*)eadata;
- fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
-
- fcb->ealen = 4;
-
- // calculate ealen
- eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
- do {
- fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-
- if (eainfo->NextEntryOffset == 0)
- break;
-
- eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
- } while (TRUE);
- }
- }
-
- InsertTailList(&subvol->fcbs, &fcb->list_entry);
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
- 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 = NULL;
- traverse_ptr next_tp;
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_EXTENT_DATA;
- searchkey.offset = 0;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_fcb(fcb);
- return Status;
- }
-
- do {
- if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
- extent* ext;
- BOOL unique = FALSE;
-
- ed = (EXTENT_DATA*)tp.item->data;
-
- if (tp.item->size < sizeof(EXTENT_DATA)) {
- ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
- tp.item->size, sizeof(EXTENT_DATA));
-
- free_fcb(fcb);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
-
- if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
- tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-
- free_fcb(fcb);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (ed2->address == 0 && ed2->size == 0) // sparse
- goto nextitem;
-
- if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
- unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
- }
-
- ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
- if (!ext) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
- if (!ext->data) {
- ERR("out of memory\n");
- ExFreePool(ext);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- ext->offset = tp.item->key.offset;
- RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
- ext->datalen = tp.item->size;
- ext->unique = unique;
- ext->ignore = FALSE;
-
- InsertTailList(&fcb->extents, &ext->list_entry);
- }
-
-nextitem:
- b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
-
- 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 (ed && ed->type == EXTENT_TYPE_INLINE)
- fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
- else
- fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
-
- fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
- fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
- }
-
- // FIXME - only do if st_nlink > 1?
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_INODE_REF;
- searchkey.offset = 0;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_fcb(fcb);
- return Status;
+ fcb->type = BTRFS_TYPE_FIFO;
+ else if (fcb->inode_item.st_mode & __S_IFLNK)
+ fcb->type = BTRFS_TYPE_SYMLINK;
+ else if (fcb->inode_item.st_mode & __S_IFSOCK)
+ fcb->type = BTRFS_TYPE_SOCKET;
+ else
+ fcb->type = BTRFS_TYPE_FILE;
}
- do {
- traverse_ptr next_tp;
+ no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
+
+ while (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
+ tp = next_tp;
+
+ if (tp.item->key.obj_id > inode)
+ break;
+
+ if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
+ break;
- if (tp.item->key.obj_id == searchkey.obj_id) {
- if (tp.item->key.obj_type == TYPE_INODE_REF) {
- ULONG len;
- INODE_REF* ir;
+ if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_REF) {
+ ULONG len;
+ INODE_REF* ir;
+
+ len = tp.item->size;
+ ir = (INODE_REF*)tp.item->data;
+
+ while (len >= sizeof(INODE_REF) - 1) {
+ hardlink* hl;
+ ULONG stringlen;
+
+ hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
+ if (!hl) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ hl->parent = tp.item->key.offset;
+ hl->index = ir->index;
+
+ hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
+
+ if (hl->utf8.Length > 0) {
+ hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
+ }
- len = tp.item->size;
- ir = (INODE_REF*)tp.item->data;
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(hl);
+ free_fcb(fcb);
+ return Status;
+ }
+
+ hl->name.Length = hl->name.MaximumLength = stringlen;
- while (len >= sizeof(INODE_REF) - 1) {
- hardlink* hl;
- ULONG stringlen;
+ if (stringlen == 0)
+ hl->name.Buffer = NULL;
+ else {
+ hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
- hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
- if (!hl) {
+ if (!hl->name.Buffer) {
ERR("out of memory\n");
+ ExFreePool(hl);
free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
- hl->parent = tp.item->key.offset;
- hl->index = ir->index;
-
- hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
-
- if (hl->utf8.Length > 0) {
- hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
- RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
+ Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(hl->name.Buffer);
ExFreePool(hl);
free_fcb(fcb);
return Status;
}
-
- hl->name.Length = hl->name.MaximumLength = stringlen;
-
- if (stringlen == 0)
- hl->name.Buffer = NULL;
- else {
- hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
-
- if (!hl->name.Buffer) {
- ERR("out of memory\n");
- ExFreePool(hl);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(hl->name.Buffer);
- ExFreePool(hl);
- free_fcb(fcb);
- return Status;
- }
- }
-
- InsertTailList(&fcb->hardlinks, &hl->list_entry);
-
- len -= sizeof(INODE_REF) - 1 + ir->n;
- ir = (INODE_REF*)&ir->name[ir->n];
}
- } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
- ULONG len;
- INODE_EXTREF* ier;
- len = tp.item->size;
- ier = (INODE_EXTREF*)tp.item->data;
+ InsertTailList(&fcb->hardlinks, &hl->list_entry);
+
+ len -= sizeof(INODE_REF) - 1 + ir->n;
+ ir = (INODE_REF*)&ir->name[ir->n];
+ }
+ } else if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+ ULONG len;
+ INODE_EXTREF* ier;
+
+ len = tp.item->size;
+ ier = (INODE_EXTREF*)tp.item->data;
+
+ while (len >= sizeof(INODE_EXTREF) - 1) {
+ hardlink* hl;
+ ULONG stringlen;
+
+ hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
+ if (!hl) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ hl->parent = ier->dir;
+ hl->index = ier->index;
+
+ hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
+
+ if (hl->utf8.Length > 0) {
+ hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
+ RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
+ }
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ExFreePool(hl);
+ free_fcb(fcb);
+ return Status;
+ }
+
+ hl->name.Length = hl->name.MaximumLength = stringlen;
- while (len >= sizeof(INODE_EXTREF) - 1) {
- hardlink* hl;
- ULONG stringlen;
+ if (stringlen == 0)
+ hl->name.Buffer = NULL;
+ else {
+ hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
- hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
- if (!hl) {
+ if (!hl->name.Buffer) {
ERR("out of memory\n");
+ ExFreePool(hl);
free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
- hl->parent = ier->dir;
- hl->index = ier->index;
-
- hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
-
- if (hl->utf8.Length > 0) {
- hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
- RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
+ Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(hl->name.Buffer);
ExFreePool(hl);
free_fcb(fcb);
return Status;
}
+ }
+
+ InsertTailList(&fcb->hardlinks, &hl->list_entry);
+
+ len -= sizeof(INODE_EXTREF) - 1 + ier->n;
+ ier = (INODE_EXTREF*)&ier->name[ier->n];
+ }
+ } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+ if (tp.item->size < sizeof(DIR_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+ continue;
+ }
+
+ if (tp.item->key.offset == EA_REPARSE_HASH) {
+ UINT8* xattrdata;
+ UINT16 xattrlen;
+
+ if (extract_xattr(tp.item->data, tp.item->size, EA_REPARSE, &xattrdata, &xattrlen)) {
+ fcb->reparse_xattr.Buffer = (char*)xattrdata;
+ fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
+ }
+ } else if (tp.item->key.offset == EA_EA_HASH) {
+ UINT8* eadata;
+ UINT16 ealen;
+
+ if (extract_xattr(tp.item->data, tp.item->size, EA_EA, &eadata, &ealen)) {
+ ULONG offset;
- hl->name.Length = hl->name.MaximumLength = stringlen;
+ Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
- if (stringlen == 0)
- hl->name.Buffer = NULL;
- else {
- hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
+ if (!NT_SUCCESS(Status)) {
+ WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+ ExFreePool(eadata);
+ } else {
+ FILE_FULL_EA_INFORMATION* eainfo;
+ fcb->ea_xattr.Buffer = (char*)eadata;
+ fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
- if (!hl->name.Buffer) {
- ERR("out of memory\n");
- ExFreePool(hl);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
+ fcb->ealen = 4;
- Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(hl->name.Buffer);
- ExFreePool(hl);
- free_fcb(fcb);
- return Status;
- }
+ // calculate ealen
+ eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
+ do {
+ fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+
+ if (eainfo->NextEntryOffset == 0)
+ break;
+
+ eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+ } while (TRUE);
+ }
+ }
+ } else if (tp.item->key.offset == EA_DOSATTRIB_HASH) {
+ UINT8* xattrdata;
+ UINT16 xattrlen;
+
+ if (extract_xattr(tp.item->data, tp.item->size, EA_DOSATTRIB, &xattrdata, &xattrlen)) {
+ if (get_file_attributes_from_xattr((char*)xattrdata, xattrlen, &fcb->atts)) {
+ atts_set = TRUE;
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY)
+ fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (fcb->type == BTRFS_TYPE_SYMLINK)
+ fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
}
- InsertTailList(&fcb->hardlinks, &hl->list_entry);
-
- len -= sizeof(INODE_EXTREF) - 1 + ier->n;
- ier = (INODE_EXTREF*)&ier->name[ier->n];
+ ExFreePool(xattrdata);
+ }
+ } else if (tp.item->key.offset == EA_NTACL_HASH) {
+ UINT16 buflen;
+
+ if (extract_xattr(tp.item->data, tp.item->size, EA_NTACL, (UINT8**)&fcb->sd, &buflen)) {
+ if (get_sd_from_xattr(fcb, buflen)) {
+ sd_set = TRUE;
+ } else
+ ExFreePool(fcb->sd);
+ }
+ }
+ } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+ extent* ext;
+ BOOL unique = FALSE;
+
+ ed = (EXTENT_DATA*)tp.item->data;
+
+ if (tp.item->size < sizeof(EXTENT_DATA)) {
+ ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+ tp.item->size, sizeof(EXTENT_DATA));
+
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+
+ if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+ ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+ tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
}
+
+ if (ed2->address == 0 && ed2->size == 0) // sparse
+ continue;
+
+ if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
+ unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
+ }
+
+ ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
+ if (!ext) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
+ if (!ext->data) {
+ ERR("out of memory\n");
+ ExFreePool(ext);
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
+
+ ext->offset = tp.item->key.offset;
+ RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
+ ext->datalen = tp.item->size;
+ ext->unique = unique;
+ ext->ignore = FALSE;
+ ext->inserted = FALSE;
+
+ if (ed->type == EXTENT_TYPE_REGULAR && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+ UINT64 len;
+
+ len = (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
+
+ ext->csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
+ if (!ext->csum) {
+ ERR("out of memory\n");
+ ExFreePool(ext);
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = load_csum(Vcb, ext->csum, ed2->address + (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_csum returned %08x\n", Status);
+ ExFreePool(ext);
+ free_fcb(fcb);
+ return Status;
+ }
+ } else
+ ext->csum = NULL;
+
+ InsertTailList(&fcb->extents, &ext->list_entry);
+ }
+ }
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ Status = load_dir_children(fcb, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_dir_children returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
}
+ }
+
+ if (no_data) {
+ fcb->Header.AllocationSize.QuadPart = 0;
+ fcb->Header.FileSize.QuadPart = 0;
+ fcb->Header.ValidDataLength.QuadPart = 0;
+ } else {
+ if (ed && ed->type == EXTENT_TYPE_INLINE)
+ fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
+ else
+ fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
- b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+ fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+ fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+ }
+
+ if (!atts_set)
+ fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
+
+ if (!sd_set)
+ fcb_get_sd(fcb, parent, FALSE, Irp);
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
+ fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- if (b) {
- tp = next_tp;
-
- if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > TYPE_INODE_EXTREF))
- break;
+ if (!Vcb->readonly && !(subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
+ fcb->atts_changed = TRUE;
+ mark_fcb_dirty(fcb);
}
- } while (b);
+ }
+
+ if (lastle)
+ InsertHeadList(lastle, &fcb->list_entry);
+ else
+ InsertTailList(&subvol->fcbs, &fcb->list_entry);
+
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
*pfcb = fcb;
return STATUS_SUCCESS;
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
+ LIST_ENTRY* lastle = NULL;
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) {
+ if (fcb->inode == inode) {
+ if (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);
+ 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);
+ InterlockedIncrement(&fcb->refcount);
#endif
- *pfcb = fcb;
- return STATUS_SUCCESS;
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+ }
+ } else if (fcb->inode > inode) {
+ lastle = le->Blink;
+ break;
}
le = le->Flink;
TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
- InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+ if (lastle)
+ InsertHeadList(lastle, &fcb->list_entry);
+ else
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
*pfcb = fcb;
}
}
- if (do_lock)
- ExReleaseResourceLite(&parent->nonpaged->children_lock);
+ if (do_lock)
+ ExReleaseResourceLite(&parent->nonpaged->children_lock);
+}
+
+static NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
+ POOL_TYPE pooltype, file_ref** psf2, PIRP Irp) {
+ NTSTATUS Status;
+ file_ref* sf2;
+
+ if (streampart) {
+ UNICODE_STRING streamname;
+ ANSI_STRING xattr;
+ UINT32 streamhash;
+
+ sf2 = search_fileref_children(sf, name, case_sensitive);
+
+ if (sf2) {
+ if (sf2->deleted) {
+ TRACE("element in path has been deleted\n");
+ free_fileref(sf2);
+ return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ *psf2 = sf2;
+
+ return STATUS_SUCCESS;
+ }
+
+ streamname.Buffer = NULL;
+ streamname.Length = streamname.MaximumLength = 0;
+ xattr.Buffer = NULL;
+ xattr.Length = xattr.MaximumLength = 0;
+
+ // FIXME - check if already opened
+
+ if (!find_stream(Vcb, sf->fcb, name, &streamname, &streamhash, &xattr, Irp)) {
+ TRACE("could not find stream %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
+
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ } else {
+ fcb* fcb;
+
+ if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
+ RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
+ WARN("not allowing user.DOSATTRIB to be opened as stream\n");
+
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb_stream returned %08x\n", Status);
+ return Status;
+ }
+
+ sf2 = create_fileref();
+ if (!sf2) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->fcb = fcb;
+
+ if (streamname.Buffer) // case has changed
+ sf2->filepart = streamname;
+ else {
+ sf2->filepart.MaximumLength = sf2->filepart.Length = name->Length;
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+ if (!sf2->filepart.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(sf2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(sf2->filepart.Buffer, name->Buffer, name->Length);
+ }
+
+ Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ free_fileref(sf2);
+ return Status;
+ }
+
+ // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+
+ sf2->parent = (struct _file_ref*)sf;
+ insert_fileref_child(sf, sf2, TRUE);
+
+ increase_fileref_refcount(sf);
+ }
+ } else {
+ root* subvol;
+ UINT64 inode;
+ dir_child* dc;
+
+ Status = find_file_in_dir(Vcb, name, sf->fcb, &subvol, &inode, &dc, case_sensitive, Irp);
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ TRACE("could not find %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
+
+ return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("find_file_in_dir returned %08x\n", Status);
+ return Status;
+ } else {
+ fcb* fcb;
+
+ if (dc->fileref) {
+ if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
+ WARN("passed path including file as subdirectory\n");
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ InterlockedIncrement(&dc->fileref->refcount);
+ *psf2 = dc->fileref;
+ return STATUS_SUCCESS;
+ }
+
+ Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb returned %08x\n", Status);
+ return Status;
+ }
+
+ if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ WARN("passed path including file as subdirectory\n");
+ free_fcb(fcb);
+ return STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ sf2 = create_fileref();
+ if (!sf2) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->fcb = fcb;
+
+ if (dc->type == BTRFS_TYPE_DIRECTORY)
+ fcb->fileref = sf2;
+
+ sf2->index = dc->index;
+ sf2->dc = dc;
+ dc->fileref = sf2;
+
+ sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.Length, ALLOC_TAG);
+ if (!sf2->utf8.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(sf2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.Length, ALLOC_TAG);
+ if (!sf2->filepart.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(sf2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->filepart_uc.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name_uc.Length, ALLOC_TAG);
+ if (!sf2->filepart_uc.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(sf2);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->utf8.Length = sf2->utf8.MaximumLength = dc->utf8.Length;
+ RtlCopyMemory(sf2->utf8.Buffer, dc->utf8.Buffer, dc->utf8.Length);
+
+ sf2->filepart.Length = sf2->filepart.MaximumLength = dc->name.Length;
+ RtlCopyMemory(sf2->filepart.Buffer, dc->name.Buffer, dc->name.Length);
+
+ sf2->filepart_uc.Length = sf2->filepart_uc.MaximumLength = dc->name_uc.Length;
+ RtlCopyMemory(sf2->filepart_uc.Buffer, dc->name_uc.Buffer, dc->name_uc.Length);
+
+ sf2->parent = (struct _file_ref*)sf;
+
+ insert_fileref_child(sf, sf2, TRUE);
+
+ increase_fileref_refcount(sf);
+ }
+ }
+
+ *psf2 = sf2;
+
+ return STATUS_SUCCESS;
}
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset,
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* parsed, ULONG* fn_offset,
POOL_TYPE pooltype, BOOL case_sensitive, PIRP Irp) {
UNICODE_STRING fnus2;
file_ref *dir, *sf, *sf2;
BOOL has_stream;
NTSTATUS Status;
- TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
+ TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
#ifdef DEBUG
if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
ULONG cc;
IO_STATUS_BLOCK iosb;
- Status = dev_ioctl(Vcb->devices[0].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+ Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
if (!NT_SUCCESS(Status))
return Status;
increase_fileref_refcount(Vcb->root_fileref);
*pfr = Vcb->root_fileref;
+
+ if (fn_offset)
+ *fn_offset = 0;
+
return STATUS_SUCCESS;
}
if (num_parts == 0) {
Status = STATUS_SUCCESS;
*pfr = dir;
+
+ if (fn_offset)
+ *fn_offset = 0;
+
goto end2;
}
for (i = 0; i < num_parts; i++) {
BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
- sf2 = search_fileref_children(sf, &parts[i], case_sensitive);
-
- if (sf2 && sf2->fcb->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
- WARN("passed path including file as subdirectory\n");
- free_fileref(sf2);
+ Status = open_fileref_child(Vcb, sf, &parts[i], case_sensitive, lastpart, has_stream && i == num_parts - 1, pooltype, &sf2, Irp);
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND)
+ TRACE("open_fileref_child returned %08x\n", Status);
+ else
+ ERR("open_fileref_child returned %08x\n", Status);
- Status = STATUS_OBJECT_PATH_NOT_FOUND;
- goto end;
- }
-
- if (sf2 && sf2->deleted) {
- TRACE("element in path has been deleted\n");
- free_fileref(sf2);
- Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
goto end;
}
- if (!sf2) {
- if (has_stream && i == num_parts - 1) {
- UNICODE_STRING streamname;
- ANSI_STRING xattr;
- UINT32 streamhash;
-
- streamname.Buffer = NULL;
- streamname.Length = streamname.MaximumLength = 0;
- xattr.Buffer = NULL;
- xattr.Length = xattr.MaximumLength = 0;
-
- // FIXME - check if already opened
-
- if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr, Irp)) {
- TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
-
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
- goto end;
- } else {
- fcb* fcb;
-
- if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
- RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
- WARN("not allowing user.DOSATTRIB to be opened as stream\n");
-
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
- goto end;
- }
-
- Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fcb_stream returned %08x\n", Status);
- goto end;
- }
-
- sf2 = create_fileref();
- if (!sf2) {
- ERR("out of memory\n");
- free_fcb(fcb);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- sf2->fcb = fcb;
-
- if (streamname.Buffer) // case has changed
- sf2->filepart = streamname;
- else {
- sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
- 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;
- }
-
- RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
- }
-
- Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(sf2);
- goto end;
- }
-
- // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
-
- sf2->parent = (struct _file_ref*)sf;
- insert_fileref_child(sf, sf2, TRUE);
-
- increase_fileref_refcount(sf);
- }
- } else {
- root* subvol;
- UINT64 inode, index;
- UINT8 type;
- ANSI_STRING utf8;
-
- Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, case_sensitive, Irp);
- if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
-
- Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
- goto end;
- } else if (!NT_SUCCESS(Status)) {
- ERR("find_file_in_dir returned %08x\n", Status);
- goto end;
- } else {
- fcb* fcb;
- ULONG strlen;
-
- Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, pooltype, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fcb returned %08x\n", Status);
- goto end;
- }
-
- if (type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
- WARN("passed path including file as subdirectory\n");
- free_fcb(fcb);
- Status = STATUS_OBJECT_PATH_NOT_FOUND;
- goto end;
- }
-
- sf2 = create_fileref();
- if (!sf2) {
- ERR("out of memory\n");
- free_fcb(fcb);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto end;
- }
-
- sf2->fcb = fcb;
-
- if (type == BTRFS_TYPE_DIRECTORY)
- fcb->fileref = sf2;
-
- sf2->index = index;
- sf2->utf8 = utf8;
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fileref(sf2);
- goto end;
- }
-
- 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;
- }
-
- 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;
- }
-
- Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(sf2);
- goto end;
- }
-
- sf2->parent = (struct _file_ref*)sf;
-
- insert_fileref_child(sf, sf2, TRUE);
-
- increase_fileref_refcount(sf);
- }
- }
- }
-
if (i == num_parts - 1) {
if (fn_offset)
*fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
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));
+ if (parsed)
+ *parsed = (parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
break;
}
return Status;
}
+NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, UINT64 index, PANSI_STRING utf8, PUNICODE_STRING name, PUNICODE_STRING name_uc, UINT8 type, dir_child** pdc) {
+ dir_child* dc;
+
+ dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+ if (!dc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
+ if (!dc->utf8.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
+ if (!dc->name.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, name_uc->Length, ALLOC_TAG);
+ if (!dc->name_uc.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dc->key.obj_id = inode;
+ dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
+ dc->key.offset = 0;
+ dc->index = index;
+ dc->type = type;
+ dc->fileref = NULL;
+
+ dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length;
+ RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
+
+ dc->name.Length = dc->name.MaximumLength = name->Length;
+ RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
+
+ dc->name_uc.Length = dc->name_uc.MaximumLength = name_uc->Length;
+ RtlCopyMemory(dc->name_uc.Buffer, name_uc->Buffer, name_uc->Length);
+
+ dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
+ dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
+
+ ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
+
+ InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+
+ insert_dir_child_into_hash_lists(fcb, dc);
+
+ ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+
+ *pdc = dc;
+
+ return STATUS_SUCCESS;
+}
+
static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options,
FILE_FULL_EA_INFORMATION* ea, ULONG ealen, file_ref** pfr, LIST_ENTRY* rollback) {
NTSTATUS Status;
POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
ULONG defda;
file_ref* fileref;
- hardlink* hl;
+ dir_child* dc;
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
#endif
fcb->ea_changed = TRUE;
}
- hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG);
- if (!hl) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- hl->parent = parfileref->fcb->inode;
- hl->index = dirpos;
-
- hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
- hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG);
-
- if (!hl->utf8.Buffer) {
- ERR("out of memory\n");
- ExFreePool(hl);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
-
- hl->name.Length = hl->name.MaximumLength = fpus->Length;
- hl->name.Buffer = ExAllocatePoolWithTag(pool_type, fpus->Length, ALLOC_TAG);
-
- if (!hl->name.Buffer) {
- ERR("out of memory\n");
- ExFreePool(hl->utf8.Buffer);
- ExFreePool(hl);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(hl->name.Buffer, fpus->Buffer, fpus->Length);
-
- InsertTailList(&fcb->hardlinks, &hl->list_entry);
-
fileref = create_fileref();
if (!fileref) {
ERR("out of memory\n");
free_fileref(fileref);
return Status;
}
-
+
if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
insert_fileref_child(parfileref, fileref, TRUE);
+ Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, fileref->index, &fileref->utf8, &fileref->filepart, &fileref->filepart_uc, fcb->type, &dc);
+ if (!NT_SUCCESS(Status))
+ WARN("add_dir_child returned %08x\n", Status);
+
+ fileref->dc = dc;
+ dc->fileref = fileref;
+
increase_fileref_refcount(parfileref);
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+ fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!fcb->hash_ptrs) {
+ ERR("out of memory\n");
+ free_fileref(fileref);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+
+ fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!fcb->hash_ptrs_uc) {
+ ERR("out of memory\n");
+ free_fileref(fileref);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+ }
InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
mark_fcb_dirty(fcb);
mark_fileref_dirty(fileref);
- InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+ InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
KeQuerySystemTime(&time);
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) {
+static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related,
+ PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
NTSTATUS Status;
// fcb *fcb, *parfcb = NULL;
- file_ref *fileref, *parfileref = NULL, *related;
+ file_ref *fileref, *parfileref = NULL;
ULONG i, j, fn_offset;
// ULONG utf8len;
ccb* ccb;
dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
fpus.Buffer = NULL;
- if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
- struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+ if (!loaded_related) {
+ Status = open_fileref(Vcb, &parfileref, fnus, related, TRUE, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
- related = relatedccb->fileref;
+ if (!NT_SUCCESS(Status))
+ goto end;
} else
- related = NULL;
-
- Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
-
- if (!NT_SUCCESS(Status))
- goto end;
+ parfileref = related;
if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
Status = STATUS_OBJECT_PATH_NOT_FOUND;
ExFreePool(fpus.Buffer);
end2:
- if (parfileref)
+ if (parfileref && !loaded_related)
free_fileref(parfileref);
return Status;
return STATUS_INSUFFICIENT_RESOURCES;
}
- Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
+ Status = read_file(fcb, *data, 0, size, &bytes_read, NULL, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("read_file_fcb returned %08x\n", Status);
ExFreePool(*data);
ccb* ccb;
device_extension* Vcb = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
- USHORT unparsed;
+ USHORT parsed;
ULONG fn_offset = 0;
file_ref *related, *fileref;
POOL_TYPE pool_type = Stack->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
ACCESS_MASK granted_access;
+ BOOL loaded_related = FALSE;
+ UNICODE_STRING fn;
#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
#endif
+#ifdef DEBUG_STATS
+ LARGE_INTEGER time1, time2;
+ UINT8 open_type = 0;
+
+ time1 = KeQueryPerformanceCounter(NULL);
+#endif
Irp->IoStatus.Information = 0;
goto exit;
}
- TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+ fn = FileObject->FileName;
+
+ TRACE("(%.*S)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
TRACE("FileObject = %p\n", FileObject);
if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
if (options & FILE_OPEN_BY_FILE_ID) {
- if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+ if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
UINT64 inode;
- RtlCopyMemory(&inode, FileObject->FileName.Buffer, sizeof(UINT64));
+ RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
inode = Vcb->root_fileref->fcb->inode;
goto exit;
}
} else {
- if (related && FileObject->FileName.Length != 0 && FileObject->FileName.Buffer[0] == '\\') {
+ if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
Status = STATUS_OBJECT_NAME_INVALID;
goto exit;
}
- Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset,
- pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+ if (!related && RequestedDisposition != FILE_OPEN && !(Stack->Flags & SL_OPEN_TARGET_DIRECTORY)) {
+ ULONG fnoff;
+
+ Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
+ pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+ Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ else if (Status == STATUS_REPARSE)
+ fileref = related;
+ else if (NT_SUCCESS(Status)) {
+ fnoff *= sizeof(WCHAR);
+ fnoff += related->filepart.Length + sizeof(WCHAR);
+
+ if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+ Status = STATUS_REPARSE;
+ fileref = related;
+ parsed = fnoff - sizeof(WCHAR);
+ } else {
+ fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
+ fn.Length -= fnoff;
+
+ Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+
+ loaded_related = TRUE;
+ }
+
+ }
+ } else {
+ Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+ }
}
if (Status == STATUS_REPARSE) {
Status = STATUS_REPARSE;
RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
- data->Reserved = unparsed;
+ data->Reserved = FileObject->FileName.Length - parsed;
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
file_ref* sf;
if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
+ LARGE_INTEGER zero;
+
+#ifdef DEBUG_STATS
+ open_type = 1;
+#endif
if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
free_fileref(fileref);
free_fileref(fileref);
goto exit;
}
+
+ zero.QuadPart = 0;
+ if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
+ Status = STATUS_USER_MAPPED_FILE;
+ free_fileref(fileref);
+ goto exit;
+ }
}
SeLockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
SeUnlockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && granted_access &
- (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+ (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC) &&
+ fileref->fcb->inode != SUBVOL_ROOT_INODE) {
Status = STATUS_ACCESS_DENIED;
free_fileref(fileref);
goto exit;
Status = STATUS_REPARSE;
Irp->IoStatus.Information = data->ReparseTag;
- if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\')
+ if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
data->Reserved = sizeof(WCHAR);
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
FileObject->FsContext2 = ccb;
if (fn_offset > 0) {
- FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
- RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+ fn.Length -= fn_offset * sizeof(WCHAR);
+ RtlMoveMemory(&fn.Buffer[0], &fn.Buffer[fn_offset], fn.Length);
}
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
#endif
InterlockedIncrement(&Vcb->open_files);
} else {
- Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
+#ifdef DEBUG_STATS
+ open_type = 2;
+#endif
+ Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
}
ExReleaseResourceLite(&Vcb->fcb_lock);
exit2:
+ if (loaded_related)
+ free_fileref(related);
+
if (NT_SUCCESS(Status)) {
if (!FileObject->Vpb)
FileObject->Vpb = DeviceObject->Vpb;
TRACE("returning %08x\n", Status);
}
+#ifdef DEBUG_STATS
+ time2 = KeQueryPerformanceCounter(NULL);
+
+ if (open_type == 0) {
+ Vcb->stats.open_total_time += time2.QuadPart - time1.QuadPart;
+ Vcb->stats.num_opens++;
+ } else if (open_type == 1) {
+ Vcb->stats.overwrite_total_time += time2.QuadPart - time1.QuadPart;
+ Vcb->stats.num_overwrites++;
+ } else if (open_type == 2) {
+ Vcb->stats.create_total_time += time2.QuadPart - time1.QuadPart;
+ Vcb->stats.num_creates++;
+ }
+#endif
+
return Status;
}
NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
- UINT64 i;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
- for (i = 0; i < Vcb->devices_loaded; i++) {
- if (Vcb->devices[i].removable) {
- NTSTATUS Status;
+ ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+ if (dev->removable) {
ULONG cc;
IO_STATUS_BLOCK iosb;
- Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+ Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
if (!NT_SUCCESS(Status)) {
ERR("dev_ioctl returned %08x\n", Status);
- return Status;
+ goto end;
}
if (iosb.Information < sizeof(ULONG)) {
ERR("iosb.Information was too short\n");
- return STATUS_INTERNAL_ERROR;
+ Status = STATUS_INTERNAL_ERROR;
+ goto end;
}
- if (cc != Vcb->devices[i].change_count) {
- PDEVICE_OBJECT dev;
+ if (cc != dev->change_count) {
+ PDEVICE_OBJECT devobj;
- Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+ dev->devobj->Flags |= DO_VERIFY_VOLUME;
- dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+ devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
- if (!dev) {
- dev = IoGetDeviceToVerify(PsGetCurrentThread());
+ if (!devobj) {
+ devobj = IoGetDeviceToVerify(PsGetCurrentThread());
IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
}
- dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+ devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
- if (dev)
- IoVerifyVolume(dev, FALSE);
+ if (devobj)
+ IoVerifyVolume(devobj, FALSE);
- return STATUS_VERIFY_REQUIRED;
+ Status = STATUS_VERIFY_REQUIRED;
+ goto end;
}
}
+
+ le = le->Flink;
}
- return STATUS_SUCCESS;
+ Status = STATUS_SUCCESS;
+
+end:
+ ExReleaseResourceLite(&Vcb->tree_lock);
+
+ return Status;
+}
+
+static BOOL has_manage_volume_privilege(ACCESS_STATE* access_state, KPROCESSOR_MODE processor_mode) {
+ PRIVILEGE_SET privset;
+
+ privset.PrivilegeCount = 1;
+ privset.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ privset.Privilege[0].Luid = RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE);
+ privset.Privilege[0].Attributes = 0;
+
+ return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? TRUE : FALSE;
}
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc, oc;
#endif
+ ccb* ccb;
TRACE("open operation for volume\n");
- if (RequestedDisposition != FILE_OPEN &&
- RequestedDisposition != FILE_OPEN_IF)
- {
+ if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) {
Status = STATUS_ACCESS_DENIED;
goto exit;
}
- if (RequestedOptions & FILE_DIRECTORY_FILE)
- {
+ if (RequestedOptions & FILE_DIRECTORY_FILE) {
Status = STATUS_NOT_A_DIRECTORY;
goto exit;
}
Status = STATUS_ACCESS_DENIED;
goto exit;
}
+
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ if (!ccb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ RtlZeroMemory(ccb, sizeof(*ccb));
+
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(ccb);
+ ccb->disposition = RequestedDisposition;
+ ccb->options = RequestedOptions;
+ ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
+ ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState,
+ IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode);
#ifdef DEBUG_FCB_REFCOUNTS
rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
InterlockedIncrement(&Vcb->volume_fcb->refcount);
#endif
IrpSp->FileObject->FsContext = Vcb->volume_fcb;
+ IrpSp->FileObject->FsContext2 = ccb;
IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
if (!skip_lock)
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-// ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
- // Status = NtfsCreateFile(DeviceObject,
- // Irp);
Status = open_file(DeviceObject, Irp, &rollback);
-// ExReleaseResourceLite(&Vpb->DirResource);
if (!NT_SUCCESS(Status))
do_rollback(Vcb, &rollback);