InitializeListHead(&fr->children);
ExInitializeResourceLite(&fr->nonpaged->fileref_lock);
- ExInitializeResourceLite(&fr->nonpaged->children_lock);
return fr;
}
BOOL has_stream;
WCHAR* buf;
name_bit* nb;
+ NTSTATUS Status;
len = path->Length / sizeof(WCHAR);
if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
len--;
+ if (len == 0 || (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) {
+ WARN("zero-length filename part\n");
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
has_stream = FALSE;
for (i = 0; i < len; i++) {
if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
for (i = 0; i < len; i++) {
if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
+ if (buf[0] == '/' || buf[0] == '\\') {
+ WARN("zero-length filename part\n");
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto cleanup;
+ }
+
nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
if (!nb) {
ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto cleanup;
}
nb->us.Buffer = buf;
nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
if (!nb) {
ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto cleanup;
}
nb->us.Buffer = buf;
if (nb->us.Buffer[i] == ':') {
name_bit* nb2;
+ if (nb->us.Buffer[i+1] == 0) {
+ WARN("zero-length stream name\n");
+ Status = STATUS_OBJECT_NAME_INVALID;
+ goto cleanup;
+ }
+
nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
if (!nb2) {
ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto cleanup;
}
nb2->us.Buffer = &nb->us.Buffer[i+1];
*stream = has_stream;
return STATUS_SUCCESS;
+
+cleanup:
+ while (!IsListEmpty(parts)) {
+ nb = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
+
+ ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+ }
+
+ return Status;
}
NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
break;
}
- // If a directory has a lot of files, force it to stick around until the next flush
- // so we aren't constantly re-reading.
- if (num_children >= 100)
- mark_fcb_dirty(fcb);
-
return STATUS_SUCCESS;
}
BOOL atts_set = FALSE, sd_set = FALSE, no_data;
LIST_ENTRY* lastle = NULL;
EXTENT_DATA* ed = NULL;
+ UINT64 fcbs_version;
+ UINT32 hash;
+
+ hash = calc_crc32c(0xffffffff, (UINT8*)&inode, sizeof(UINT64));
- if (!IsListEmpty(&subvol->fcbs)) {
- LIST_ENTRY* le = subvol->fcbs.Flink;
+ acquire_fcb_lock_shared(Vcb);
+
+ if (subvol->fcbs_ptrs[hash >> 24]) {
+ LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
while (le != &subvol->fcbs) {
fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
#endif
*pfcb = fcb;
+ release_fcb_lock(Vcb);
return STATUS_SUCCESS;
}
}
- } else if (fcb->inode > inode) {
+ } else if (fcb->hash > hash) {
if (deleted_fcb) {
InterlockedIncrement(&deleted_fcb->refcount);
*pfcb = deleted_fcb;
+ release_fcb_lock(Vcb);
return STATUS_SUCCESS;
}
lastle = le->Blink;
+ fcbs_version = subvol->fcbs_version;
+
break;
}
}
}
+ release_fcb_lock(Vcb);
+
if (deleted_fcb) {
InterlockedIncrement(&deleted_fcb->refcount);
*pfcb = deleted_fcb;
fcb->subvol = subvol;
fcb->inode = inode;
+ fcb->hash = hash;
fcb->type = type;
searchkey.obj_id = inode;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INVALID_PARAMETER;
}
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
if (!hl) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
if (!hl->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(hl->name.Buffer);
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
}
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
if (!hl) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
if (!hl->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(hl->name.Buffer);
ExFreePool(hl);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
}
fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
if (!fcb->reparse_xattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
if (!fcb->ea_xattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
if (!fcb->sd) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
if (!dc) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!dc->utf8.Buffer) {
ERR("out of memory\n");
ExFreePool(dc);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("out of memory\n");
ExFreePool(dc->utf8.Buffer);
ExFreePool(dc);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ExFreePool(dc->utf8.Buffer);
ExFreePool(dc->name.Buffer);
ExFreePool(dc);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
ExFreePool(dc->utf8.Buffer);
ExFreePool(dc->name.Buffer);
ExFreePool(dc);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG);
if (!xa) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
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_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INTERNAL_ERROR;
}
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_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INTERNAL_ERROR;
}
ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
if (!ext) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = load_dir_children(Vcb, fcb, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_dir_children returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
}
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;
+ acquire_fcb_lock_exclusive(Vcb);
+
+ if (lastle && subvol->fcbs_version == fcbs_version)
+ InsertHeadList(lastle, &fcb->list_entry);
+ else {
+ if (subvol->fcbs_ptrs[hash >> 24]) {
+ LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
+
+ while (le != &subvol->fcbs) {
+ struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (fcb2->inode == inode) {
+ if (!fcb2->ads) {
+ if (fcb2->deleted)
+ deleted_fcb = fcb2;
+ else {
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc = InterlockedIncrement(&fcb2->refcount);
+
+ WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb2, rc, fcb2->subvol->id, fcb2->inode);
+#else
+ InterlockedIncrement(&fcb2->refcount);
+#endif
+
+ *pfcb = fcb2;
+ release_fcb_lock(Vcb);
+ reap_fcb(fcb);
+ return STATUS_SUCCESS;
+ }
+ }
+ } else if (fcb2->hash > hash) {
+ if (deleted_fcb) {
+ InterlockedIncrement(&deleted_fcb->refcount);
+ *pfcb = deleted_fcb;
+ release_fcb_lock(Vcb);
+ reap_fcb(fcb);
+ return STATUS_SUCCESS;
+ }
+
+ lastle = le->Blink;
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
+ fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+ if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
+ fcb->atts_changed = TRUE;
+ mark_fcb_dirty(fcb);
+ }
+ }
+
+ if (!lastle) {
+ UINT8 c = hash >> 24;
+
+ if (c != 0xff) {
+ UINT8 d = c + 1;
+
+ do {
+ if (subvol->fcbs_ptrs[d]) {
+ lastle = subvol->fcbs_ptrs[d]->Blink;
+ break;
+ }
+
+ d++;
+ } while (d != 0);
+ }
+ }
+
+ if (lastle) {
+ InsertHeadList(lastle, &fcb->list_entry);
+
+ if (lastle == &subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
+ subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
+ } else {
+ InsertTailList(&subvol->fcbs, &fcb->list_entry);
- if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
- fcb->atts_changed = TRUE;
- mark_fcb_dirty(fcb);
+ if (fcb->list_entry.Blink == &subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
+ subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
}
}
- if (lastle)
- InsertHeadList(lastle, &fcb->list_entry);
- else
- InsertTailList(&subvol->fcbs, &fcb->list_entry);
+ subvol->fcbs_version++;
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ release_fcb_lock(Vcb);
+
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
*pfcb = fcb;
+
return STATUS_SUCCESS;
}
if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
ERR("get_xattr failed\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
ExFreePool(xattr.Buffer);
return STATUS_INTERNAL_ERROR;
}
Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return Status;
}
if (keycmp(tp.item->key, searchkey)) {
ERR("error - could not find key for xattr\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INTERNAL_ERROR;
}
if (tp.item->size < xattrlen) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
return STATUS_INTERNAL_ERROR;
}
TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
- InsertHeadList(&parent->list_entry, &fcb->list_entry);
-
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
*pfcb = fcb;
return STATUS_SUCCESS;
UNICODE_STRING name_uc;
dir_child* dc = NULL;
fcb* fcb;
+ struct _fcb* duff_fcb = NULL;
+ file_ref* duff_fr = NULL;
if (!case_sensitive) {
Status = RtlUpcaseUnicodeString(&name_uc, name, TRUE);
le = le->Flink;
}
- if (!case_sensitive)
- ExFreePool(name_uc.Buffer);
+ if (!dc) {
+ if (locked)
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
- if (locked)
- ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+ if (!case_sensitive)
+ ExFreePool(name_uc.Buffer);
- if (!dc)
return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
if (dc->fileref) {
+ if (locked)
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+
+ if (!case_sensitive)
+ ExFreePool(name_uc.Buffer);
+
increase_fileref_refcount(dc->fileref);
*psf2 = dc->fileref;
return STATUS_SUCCESS;
}
+ if (locked)
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+
+ if (!case_sensitive)
+ ExFreePool(name_uc.Buffer);
+
Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb_stream returned %08x\n", Status);
return Status;
}
+ fcb->hash = sf->fcb->hash;
+
+ acquire_fcb_lock_exclusive(Vcb);
+
+ if (sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
+ LIST_ENTRY* le = sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
+
+ while (le != &sf->fcb->subvol->fcbs) {
+ struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (fcb2->inode == fcb->inode) {
+ if (fcb2->ads && fcb2->adshash == fcb->adshash) { // FIXME - handle hash collisions
+ duff_fcb = fcb;
+ fcb = fcb2;
+ break;
+ }
+ } else if (fcb2->hash > fcb->hash)
+ break;
+
+ le = le->Flink;
+ }
+ }
+
+ if (!duff_fcb) {
+ InsertHeadList(&sf->fcb->list_entry, &fcb->list_entry);
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ fcb->subvol->fcbs_version++;
+ }
+
+ release_fcb_lock(Vcb);
+
+ if (duff_fcb) {
+ reap_fcb(duff_fcb);
+ InterlockedIncrement(&fcb->refcount);
+ }
+
sf2 = create_fileref(Vcb);
if (!sf2) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
- sf2->fcb = fcb;
-
- sf2->parent = (struct _file_ref*)sf;
+ ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
- sf2->dc = dc;
- dc->fileref = sf2;
+ if (dc->fileref) {
+ duff_fr = sf2;
+ sf2 = dc->fileref;
+ increase_fileref_refcount(sf2);
+ } else {
+ sf2->fcb = fcb;
+ sf2->parent = (struct _file_ref*)sf;
+ sf2->dc = dc;
+ dc->fileref = sf2;
+ increase_fileref_refcount(sf);
+ InsertTailList(&sf->children, &sf2->list_entry);
+ }
- ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
- InsertTailList(&sf->children, &sf2->list_entry);
- ExReleaseResourceLite(&sf->nonpaged->children_lock);
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
- increase_fileref_refcount(sf);
+ if (duff_fr)
+ reap_fileref(Vcb, duff_fr);
} else {
root* subvol;
UINT64 inode;
return Status;
} else {
fcb* fcb;
+ file_ref* duff_fr = NULL;
#ifdef DEBUG_STATS
LARGE_INTEGER time1, time2;
#endif
if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
TRACE("passed path including file as subdirectory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
sf2 = create_fileref(Vcb);
if (!sf2) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (dc->type == BTRFS_TYPE_DIRECTORY)
fcb->fileref = sf2;
- sf2->dc = dc;
- dc->fileref = sf2;
+ ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
- sf2->parent = (struct _file_ref*)sf;
+ if (!dc->fileref) {
+ sf2->parent = (struct _file_ref*)sf;
+ sf2->dc = dc;
+ dc->fileref = sf2;
+ InsertTailList(&sf->children, &sf2->list_entry);
+ increase_fileref_refcount(sf);
+ } else {
+ duff_fr = sf2;
+ sf2 = dc->fileref;
+ increase_fileref_refcount(sf2);
+ }
- ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
- InsertTailList(&sf->children, &sf2->list_entry);
- ExReleaseResourceLite(&sf->nonpaged->children_lock);
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
- increase_fileref_refcount(sf);
+ if (duff_fr)
+ reap_fileref(Vcb, duff_fr);
}
}
TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
-#ifdef DEBUG
- if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
- ERR("fcb_lock not acquired exclusively\n");
- int3;
- }
-#endif
-
if (Vcb->removing || Vcb->locked)
return STATUS_ACCESS_DENIED;
*fn_offset = 0;
return STATUS_SUCCESS;
- }
+ } else if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\')
+ return STATUS_OBJECT_NAME_INVALID;
dir = Vcb->root_fileref;
break;
}
- free_fileref(Vcb, sf);
+ free_fileref(sf);
sf = sf2;
le = le->Flink;
*pfr = sf2;
end:
- free_fileref(Vcb, sf);
+ free_fileref(sf);
while (!IsListEmpty(&parts)) {
name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, PUNICODE_STRING name, UINT8 type, dir_child** pdc) {
NTSTATUS Status;
dir_child* dc;
+ BOOL locked;
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
if (!dc) {
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);
+ locked = ExIsResourceAcquiredExclusive(&fcb->nonpaged->dir_children_lock);
+
+ if (!locked)
+ ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
if (IsListEmpty(&fcb->dir_children_index))
dc->index = 2;
insert_dir_child_into_hash_lists(fcb, dc);
- ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+ if (!locked)
+ ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
*pdc = dc;
static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ PUNICODE_STRING fpus,
_In_ file_ref* parfileref, _In_ ULONG options, _In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen,
- _Out_ file_ref** pfr, _In_ LIST_ENTRY* rollback) {
+ _Out_ file_ref** pfr, BOOL case_sensitive, _In_ LIST_ENTRY* rollback) {
NTSTATUS Status;
fcb* fcb;
ULONG utf8len;
file_ref* fileref;
dir_child* dc;
ANSI_STRING utf8as;
+ LIST_ENTRY* lastle = NULL;
+ file_ref* existing_fileref = NULL;
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
#endif
fcb->created = TRUE;
fcb->deleted = TRUE;
+ fcb->hash = calc_crc32c(0xffffffff, (UINT8*)&inode, sizeof(UINT64));
+
+ acquire_fcb_lock_exclusive(Vcb);
+
+ if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
+ LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
+
+ while (le != &fcb->subvol->fcbs) {
+ struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
+
+ if (fcb2->hash > fcb->hash) {
+ lastle = le->Blink;
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ if (!lastle) {
+ UINT8 c = fcb->hash >> 24;
+
+ if (c != 0xff) {
+ UINT8 d = c + 1;
+
+ do {
+ if (fcb->subvol->fcbs_ptrs[d]) {
+ lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
+ break;
+ }
+
+ d++;
+ } while (d != 0);
+ }
+ }
+
+ if (lastle) {
+ InsertHeadList(lastle, &fcb->list_entry);
+
+ if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
+ fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
+ } else {
+ InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+
+ if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
+ fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
+ }
+
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+
+ fcb->subvol->fcbs_version++;
+
+ release_fcb_lock(Vcb);
+
mark_fcb_dirty(fcb);
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);
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return Status;
}
Status = file_create_parse_ea(fcb, ea);
if (!NT_SUCCESS(Status)) {
ERR("file_create_parse_ea returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return Status;
}
}
fileref = create_fileref(Vcb);
if (!fileref) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(Status)) {
ERR("extend_file returned %08x\n", Status);
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return Status;
}
}
fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs) {
ERR("out of memory\n");
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs_uc) {
ERR("out of memory\n");
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
parfileref->fcb->inode_item.st_size -= utf8len * 2;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+ ExFreePool(utf8);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->deleted = FALSE;
fileref->created = TRUE;
- mark_fileref_dirty(fileref);
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
- fileref->parent = parfileref;
-
utf8as.Buffer = utf8;
utf8as.Length = utf8as.MaximumLength = (UINT16)utf8len;
- Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8as, fpus, fcb->type, &dc);
- if (!NT_SUCCESS(Status))
- WARN("add_dir_child returned %08x\n", Status);
+ ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
- ExFreePool(utf8);
+ // check again doesn't already exist
+ if (case_sensitive) {
+ UINT32 dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpus->Buffer, fpus->Length);
+
+ if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) {
+ LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24];
+ while (le != &parfileref->fcb->dir_children_hash) {
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
+
+ if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) {
+ existing_fileref = dc->fileref;
+ break;
+ } else if (dc->hash > dc_hash)
+ break;
+
+ le = le->Flink;
+ }
+ }
+ } else {
+ UNICODE_STRING fpusuc;
+#ifdef __REACTOS__
+ UINT32 dc_hash;
+#endif
+
+ Status = RtlUpcaseUnicodeString(&fpusuc, fpus, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ reap_fileref(Vcb, fileref);
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+ ExFreePool(utf8);
+
+ return Status;
+ }
+
+#ifndef __REACTOS__
+ UINT32 dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+#else
+ dc_hash = calc_crc32c(0xffffffff, (UINT8*)fpusuc.Buffer, fpusuc.Length);
+#endif
+
+ if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
+ LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24];
+ while (le != &parfileref->fcb->dir_children_hash_uc) {
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
+
+ if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) {
+ existing_fileref = dc->fileref;
+ break;
+ } else if (dc->hash_uc > dc_hash)
+ break;
+
+ le = le->Flink;
+ }
+ }
+
+ ExFreePool(fpusuc.Buffer);
+ }
+
+ if (existing_fileref) {
+ ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+ reap_fileref(Vcb, fileref);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+ ExFreePool(utf8);
+
+ increase_fileref_refcount(existing_fileref);
+ *pfr = existing_fileref;
+
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ Status = add_dir_child(parfileref->fcb, fcb->inode, FALSE, &utf8as, fpus, fcb->type, &dc);
+ if (!NT_SUCCESS(Status)) {
+ ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
+ ERR("add_dir_child returned %08x\n", Status);
+ reap_fileref(Vcb, fileref);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
+ ExFreePool(utf8);
+
+ return Status;
+ }
+
+ fileref->parent = parfileref;
fileref->dc = dc;
dc->fileref = fileref;
- ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+ if (type == BTRFS_TYPE_DIRECTORY)
+ fileref->fcb->fileref = fileref;
+
InsertTailList(&parfileref->children, &fileref->list_entry);
- ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+ ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
- increase_fileref_refcount(parfileref);
+ ExFreePool(utf8);
- InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ mark_fileref_dirty(fileref);
+ increase_fileref_refcount(parfileref);
*pfr = fileref;
- if (type == BTRFS_TYPE_DIRECTORY)
- fileref->fcb->fileref = fileref;
-
TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
return STATUS_SUCCESS;
KEY searchkey;
traverse_ptr tp;
dir_child* dc;
+ dir_child* existing_dc = NULL;
ACCESS_MASK granted_access;
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
#endif
+#ifdef __REACTOS__
+ LIST_ENTRY* le;
+#endif
TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
- Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, rollback);
+ Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
if (!NT_SUCCESS(Status)) {
ERR("file_create2 returned %08x\n", Status);
ExFreePool(fpus2.Buffer);
return Status;
+ } else if (Status != STATUS_OBJECT_NAME_COLLISION) {
+ send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
+ send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
}
- send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
- send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+ ExFreePool(fpus2.Buffer);
} else if (!NT_SUCCESS(Status)) {
ERR("open_fileref returned %08x\n", Status);
return Status;
if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
WARN("parent not file, directory, or symlink\n");
+ free_fileref(parfileref);
return STATUS_INVALID_PARAMETER;
}
if (options & FILE_DIRECTORY_FILE) {
WARN("tried to create directory as stream\n");
+ free_fileref(parfileref);
return STATUS_INVALID_PARAMETER;
}
- if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY)
+ if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+ free_fileref(parfileref);
return STATUS_ACCESS_DENIED;
+ }
SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
TRUE, FILE_WRITE_DATA, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
&granted_access, &Status)) {
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ free_fileref(parfileref);
return Status;
}
if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
(stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
(stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
+ free_fileref(parfileref);
return STATUS_OBJECT_NAME_INVALID;
}
fcb = create_fcb(Vcb, pool_type);
if (!fcb) {
ERR("out of memory\n");
+ free_fileref(parfileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
+ free_fileref(parfileref);
return Status;
}
fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
if (!fcb->adsxattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
+ free_fileref(parfileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
+ free_fileref(parfileref);
return Status;
}
Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
+ free_fileref(parfileref);
return Status;
}
if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
- free_fcb(Vcb, fcb);
+ reap_fcb(fcb);
+ free_fileref(parfileref);
return STATUS_DISK_FULL;
} else
fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
- fileref = create_fileref(Vcb);
- if (!fileref) {
- ERR("out of memory\n");
- free_fcb(Vcb, fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
+ fcb->created = TRUE;
+ fcb->deleted = TRUE;
+
+ acquire_fcb_lock_exclusive(Vcb);
+ InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ parfileref->fcb->subvol->fcbs_version++;
+ release_fcb_lock(Vcb);
+
+ mark_fcb_dirty(fcb);
+
+ fileref = create_fileref(Vcb);
+ if (!fileref) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ free_fileref(parfileref);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
fileref->fcb = fcb;
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
if (!dc) {
ERR("out of memory\n");
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
+ free_fileref(parfileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!dc->utf8.Buffer) {
ERR("out of memory\n");
ExFreePool(dc);
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
+ free_fileref(parfileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("out of memory\n");
ExFreePool(dc->utf8.Buffer);
ExFreePool(dc);
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
+ free_fileref(parfileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
ExFreePool(dc->utf8.Buffer);
ExFreePool(dc->name.Buffer);
ExFreePool(dc);
- free_fileref(Vcb, fileref);
+ reap_fileref(Vcb, fileref);
+ free_fileref(parfileref);
return Status;
}
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
+
+#ifndef __REACTOS__
+ LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink;
+#else
+ le = parfileref->fcb->dir_children_index.Flink;
+#endif
+ while (le != &parfileref->fcb->dir_children_index) {
+ dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
+
+ if (dc2->index == 0) {
+ if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) ||
+ (!case_sensitive && dc2->name_uc.Length == dc->name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, dc->name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
+ ) {
+ existing_dc = dc2;
+ break;
+ }
+ } else
+ break;
+
+ le = le->Flink;
+ }
+
+ if (existing_dc) {
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ reap_fileref(Vcb, fileref);
+ free_fileref(parfileref);
+
+ increase_fileref_refcount(existing_dc->fileref);
+ *pfileref = existing_dc->fileref;
+
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
dc->fileref = fileref;
fileref->dc = dc;
+ fileref->parent = (struct _file_ref*)parfileref;
+ fcb->deleted = FALSE;
InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
- mark_fcb_dirty(fcb);
- mark_fileref_dirty(fileref);
+ InsertTailList(&parfileref->children, &fileref->list_entry);
- InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
- KeQuerySystemTime(&time);
- win_time_to_unix(time, &now);
+ mark_fileref_dirty(fileref);
parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
parfileref->fcb->inode_item.sequence++;
parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
parfileref->fcb->subvol->root_item.ctime = now;
- fileref->parent = (struct _file_ref*)parfileref;
-
- ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
- InsertTailList(&parfileref->children, &fileref->list_entry);
- ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
-
increase_fileref_refcount(parfileref);
*pfileref = fileref;
#endif
static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
- PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
+ PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options,
+ file_ref** existing_fileref, LIST_ENTRY* rollback) {
NTSTATUS Status;
file_ref *fileref, *parfileref = NULL;
ULONG i, j;
}
Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
- &fileref, rollback);
+ &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
- if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_OBJECT_NAME_COLLISION) {
+ *existing_fileref = fileref;
+ goto end;
+ } else if (!NT_SUCCESS(Status)) {
ERR("file_create2 returned %08x\n", Status);
goto end;
}
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
}
- free_fileref(Vcb, fileref);
+ free_fileref(fileref);
goto end;
}
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
}
- free_fileref(Vcb, fileref);
+ free_fileref(fileref);
return Status;
}
}
end2:
if (parfileref && !loaded_related)
- free_fileref(Vcb, parfileref);
+ free_fileref(parfileref);
return Status;
}
fcb->csum_loaded = TRUE;
}
-static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
- PFILE_OBJECT FileObject = NULL;
- ULONG RequestedDisposition;
- ULONG options;
+static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, POOL_TYPE pool_type, file_ref* fileref, ACCESS_MASK* granted_access,
+ PFILE_OBJECT FileObject, UNICODE_STRING* fn, ULONG options, PIRP Irp, LIST_ENTRY* rollback) {
NTSTATUS Status;
+ file_ref* sf;
+ BOOL readonly;
ccb* ccb;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- USHORT parsed;
- ULONG fn_offset = 0;
- file_ref *related, *fileref = NULL;
- POOL_TYPE pool_type = IrpSp->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);
+ 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 || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
+ free_fileref(fileref);
- Irp->IoStatus.Information = 0;
+ return STATUS_ACCESS_DENIED;
+ }
- RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
- options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+ if (Vcb->readonly) {
+ free_fileref(fileref);
- if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
- WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
- return STATUS_INVALID_PARAMETER;
- }
+ return STATUS_MEDIA_WRITE_PROTECTED;
+ }
- FileObject = IrpSp->FileObject;
+ zero.QuadPart = 0;
+ if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
+ free_fileref(fileref);
- if (!FileObject) {
- ERR("FileObject was NULL\n");
- return STATUS_INVALID_PARAMETER;
+ return STATUS_USER_MAPPED_FILE;
+ }
}
- if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
- struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
-
- related = relatedccb->fileref;
- } else
- related = NULL;
+ if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
+ SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
- debug_create_options(options);
+ if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
+ &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
+ TRUE, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
+ granted_access, &Status)) {
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ TRACE("SeAccessCheck failed, returning %08x\n", Status);
- switch (RequestedDisposition) {
- case FILE_SUPERSEDE:
- TRACE("requested disposition: FILE_SUPERSEDE\n");
- break;
+ free_fileref(fileref);
- case FILE_CREATE:
- TRACE("requested disposition: FILE_CREATE\n");
- break;
+ return Status;
+ }
- case FILE_OPEN:
- TRACE("requested disposition: FILE_OPEN\n");
- break;
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ } else
+ *granted_access = 0;
- case FILE_OPEN_IF:
- TRACE("requested disposition: FILE_OPEN_IF\n");
- break;
+ TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
- case FILE_OVERWRITE:
- TRACE("requested disposition: FILE_OVERWRITE\n");
- break;
+ sf = fileref;
+ while (sf) {
+ if (sf->delete_on_close) {
+ TRACE("could not open as deletion pending\n");
- case FILE_OVERWRITE_IF:
- TRACE("requested disposition: FILE_OVERWRITE_IF\n");
- break;
+ free_fileref(fileref);
- default:
- ERR("unknown disposition: %x\n", RequestedDisposition);
- Status = STATUS_NOT_IMPLEMENTED;
- goto exit;
+ return STATUS_DELETE_PENDING;
+ }
+ sf = sf->parent;
}
- fn = FileObject->FileName;
+ readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) ||
+ is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
- TRACE("(%.*S)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
- TRACE("FileObject = %p\n", FileObject);
+ if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
+ free_fileref(fileref);
- if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
- Status = STATUS_MEDIA_WRITE_PROTECTED;
- goto exit;
+ return STATUS_CANNOT_DELETE;
}
- acquire_fcb_lock_exclusive(Vcb);
-
- if (options & FILE_OPEN_BY_FILE_ID) {
- if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
- UINT64 inode;
-
- RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
-
- if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
- inode = Vcb->root_fileref->fcb->inode;
+ if (readonly) {
+ ACCESS_MASK allowed;
- if (inode == 0) { // we use 0 to mean the parent of a subvolume
- fileref = related->parent;
- increase_fileref_refcount(fileref);
- Status = STATUS_SUCCESS;
- } else {
- Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
- }
- } else {
- WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
- Status = STATUS_NOT_IMPLEMENTED;
- release_fcb_lock(Vcb);
- goto exit;
- }
- } else {
- if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
- Status = STATUS_INVALID_PARAMETER;
- release_fcb_lock(Vcb);
- goto exit;
- }
+ allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
+ FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
+ FILE_TRAVERSE;
- if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
- ULONG fnoff;
+ if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
+ allowed |= DELETE;
- Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
- pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+ if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
+ allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
- 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->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
-
- if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
- Status = STATUS_REPARSE;
- fileref = related;
- parsed = (USHORT)fnoff - sizeof(WCHAR);
- } else {
- fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
- fn.Length -= (USHORT)fnoff;
+ if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
+ allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
+ } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
+ // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
- Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
- pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+ allowed |= FILE_WRITE_ATTRIBUTES;
+ }
- loaded_related = TRUE;
- }
+ if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
+ *granted_access &= allowed;
+ IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
+ } else if (*granted_access & ~allowed) {
+ free_fileref(fileref);
- }
- } else {
- Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
- pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+ return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
}
}
- if (Status == STATUS_REPARSE) {
+ if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) {
REPARSE_DATA_BUFFER* data;
- ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
- Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
- ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ /* 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, (UINT8**)&data);
if (!NT_SUCCESS(Status)) {
ERR("get_reparse_block returned %08x\n", Status);
-
Status = STATUS_SUCCESS;
} else {
- Status = STATUS_REPARSE;
- RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+ Irp->IoStatus.Information = data->ReparseTag;
- data->Reserved = FileObject->FileName.Length - parsed;
+ if (fn->Buffer[(fn->Length / sizeof(WCHAR)) - 1] == '\\')
+ data->Reserved = sizeof(WCHAR);
Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
+ return STATUS_REPARSE;
}
}
- if (NT_SUCCESS(Status) && fileref->deleted)
- Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
+ if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ free_fileref(fileref);
- if (NT_SUCCESS(Status)) {
- if (RequestedDisposition == FILE_CREATE) {
- TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
- Status = STATUS_OBJECT_NAME_COLLISION;
+ return STATUS_FILE_IS_A_DIRECTORY;
+ }
+ } else if (options & FILE_DIRECTORY_FILE) {
+ TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
- }
- } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
- TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
- release_fcb_lock(Vcb);
- goto exit;
- }
- } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
- TRACE("open_fileref returned %08x\n", Status);
- release_fcb_lock(Vcb);
- goto exit;
- } else {
- ERR("open_fileref returned %08x\n", Status);
- release_fcb_lock(Vcb);
- goto exit;
+ return STATUS_NOT_A_DIRECTORY;
}
- if (NT_SUCCESS(Status)) { // file already exists
- file_ref* sf;
- BOOL readonly;
+ if (fileref->open_count > 0) {
+ Status = IoCheckShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, FALSE);
- release_fcb_lock(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_SHARING_VIOLATION)
+ TRACE("IoCheckShareAccess failed, returning %08x\n", Status);
+ else
+ WARN("IoCheckShareAccess failed, returning %08x\n", Status);
- if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
- LARGE_INTEGER zero;
+ free_fileref(fileref);
-#ifdef DEBUG_STATS
- open_type = 1;
-#endif
- if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
- Status = STATUS_ACCESS_DENIED;
+ return Status;
+ }
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
+ } else
+ IoSetShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
- goto exit;
- }
+ if (*granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
+ if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
- if (Vcb->readonly) {
- Status = STATUS_MEDIA_WRITE_PROTECTED;
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
- }
+ return (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
+ }
+ }
- zero.QuadPart = 0;
- if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
- Status = STATUS_USER_MAPPED_FILE;
+ if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
+ ULONG defda, oldatts, filter;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
+ WARN("cannot overwrite readonly file\n");
- goto exit;
- }
- }
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
- SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ free_fileref(fileref);
- if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
- &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
- TRUE, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
- IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
- &granted_access, &Status)) {
- SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
- TRACE("SeAccessCheck failed, returning %08x\n", Status);
+ return STATUS_ACCESS_DENIED;
+ }
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) {
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- goto exit;
- }
+ free_fileref(fileref);
- SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
- } else
- granted_access = 0;
+ return STATUS_ACCESS_DENIED;
+ }
- TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
+ if (fileref->fcb->ads) {
+ Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, FALSE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("stream_set_end_of_file_information returned %08x\n", Status);
- sf = fileref;
- while (sf) {
- if (sf->delete_on_close) {
- TRACE("could not open as deletion pending\n");
- Status = STATUS_DELETE_PENDING;
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
+ return Status;
}
- sf = sf->parent;
- }
-
- readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) ||
- is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
+ } else {
+ Status = truncate_file(fileref->fcb, 0, Irp, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("truncate_file returned %08x\n", Status);
- if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
- Status = STATUS_CANNOT_DELETE;
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
+ return Status;
+ }
}
- if (readonly) {
- ACCESS_MASK allowed;
+ if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+ Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
- allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
- FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
- FILE_TRAVERSE;
-
- if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
- allowed |= DELETE;
+ if (!NT_SUCCESS(Status)) {
+ ERR("extend_file returned %08x\n", Status);
- if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
- allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
- allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
- } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
- // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
+ free_fileref(fileref);
- allowed |= FILE_WRITE_ATTRIBUTES;
+ return Status;
}
+ }
- if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
- granted_access &= allowed;
- IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
- } else if (granted_access & ~allowed) {
- Status = Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
+ if (!fileref->fcb->ads) {
+ LIST_ENTRY* le;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
+ ULONG offset;
+ FILE_FULL_EA_INFORMATION* eainfo;
- goto exit;
- }
- }
+ Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
- if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) {
- REPARSE_DATA_BUFFER* data;
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- /* 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. */
+ free_fileref(fileref);
- Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
- if (!NT_SUCCESS(Status)) {
- ERR("get_reparse_block returned %08x\n", Status);
- Status = STATUS_SUCCESS;
- } else {
- Status = STATUS_REPARSE;
- Irp->IoStatus.Information = data->ReparseTag;
+ return Status;
+ }
- if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
- data->Reserved = sizeof(WCHAR);
+ fileref->fcb->ealen = 4;
- Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+ // capitalize EA name
+ eainfo = Irp->AssociatedIrp.SystemBuffer;
+ do {
+ STRING s;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ s.Length = s.MaximumLength = eainfo->EaNameLength;
+ s.Buffer = eainfo->EaName;
- goto exit;
- }
- }
+ RtlUpperString(&s, &s);
- if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
- if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
- Status = STATUS_FILE_IS_A_DIRECTORY;
+ fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (eainfo->NextEntryOffset == 0)
+ break;
- goto exit;
- }
- } else if (options & FILE_DIRECTORY_FILE) {
- TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
- Status = STATUS_NOT_A_DIRECTORY;
+ eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+ } while (TRUE);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (fileref->fcb->ea_xattr.Buffer)
+ ExFreePool(fileref->fcb->ea_xattr.Buffer);
- goto exit;
- }
+ fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
+ if (!fileref->fcb->ea_xattr.Buffer) {
+ ERR("out of memory\n");
- if (fileref->open_count > 0) {
- Status = IoCheckShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, FALSE);
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- if (!NT_SUCCESS(Status)) {
- if (Status == STATUS_SHARING_VIOLATION)
- TRACE("IoCheckShareAccess failed, returning %08x\n", Status);
- else
- WARN("IoCheckShareAccess failed, returning %08x\n", Status);
+ free_fileref(fileref);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- goto exit;
+ fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
+ RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
+ } else {
+ if (fileref->fcb->ea_xattr.Length > 0) {
+ ExFreePool(fileref->fcb->ea_xattr.Buffer);
+ fileref->fcb->ea_xattr.Buffer = NULL;
+ fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
+
+ fileref->fcb->ea_changed = TRUE;
+ fileref->fcb->ealen = 0;
+ }
}
- IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
- } else
- IoSetShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
+ // remove streams and send notifications
+ le = fileref->fcb->dir_children_index.Flink;
+ while (le != &fileref->fcb->dir_children_index) {
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+ LIST_ENTRY* le2 = le->Flink;
- if (granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
- if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
- Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
+ if (dc->index == 0) {
+ if (!dc->fileref) {
+ file_ref* fr2;
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ Status = open_fileref_child(Vcb, fileref, &dc->name, TRUE, TRUE, TRUE, PagedPool, &fr2, NULL);
+ if (!NT_SUCCESS(Status))
+ WARN("open_fileref_child returned %08x\n", Status);
+ }
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (dc->fileref) {
+ send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
- goto exit;
+ Status = delete_fileref(dc->fileref, NULL, NULL, rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_fileref returned %08x\n", Status);
+
+ free_fileref(fileref);
+
+ return Status;
+ }
+ }
+ } else
+ break;
+
+ le = le2;
}
}
- if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
- ULONG defda, oldatts, filter;
- LARGE_INTEGER time;
- BTRFS_TIME now;
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
- if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
- WARN("cannot overwrite readonly file\n");
- Status = STATUS_ACCESS_DENIED;
+ filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ if (fileref->fcb->ads) {
+ fileref->parent->fcb->inode_item.st_mtime = now;
+ fileref->parent->fcb->inode_item_changed = TRUE;
+ mark_fcb_dirty(fileref->parent->fcb);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
+ } else {
+ mark_fcb_dirty(fileref->fcb);
- goto exit;
- }
+ oldatts = fileref->fcb->atts;
- if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) {
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
+ fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (RequestedDisposition == FILE_SUPERSEDE)
+ fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ else
+ fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
- Status = STATUS_ACCESS_DENIED;
- goto exit;
+ if (fileref->fcb->atts != oldatts) {
+ fileref->fcb->atts_changed = TRUE;
+ fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
+ filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
- if (fileref->fcb->ads) {
- Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("stream_set_end_of_file_information returned %08x\n", Status);
+ fileref->fcb->inode_item.transid = Vcb->superblock.generation;
+ fileref->fcb->inode_item.sequence++;
+ fileref->fcb->inode_item.st_ctime = now;
+ fileref->fcb->inode_item.st_mtime = now;
+ fileref->fcb->inode_item_changed = TRUE;
+
+ send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+ }
+ } else {
+ if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
+ FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
+
+ do {
+ if (ffei->Flags & FILE_NEED_EA) {
+ WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ free_fileref(fileref);
- goto exit;
+ return STATUS_ACCESS_DENIED;
}
- } else {
- Status = truncate_file(fileref->fcb, 0, Irp, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("truncate_file returned %08x\n", Status);
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ if (ffei->NextEntryOffset == 0)
+ break;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ ffei = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ffei) + ffei->NextEntryOffset);
+ } while (TRUE);
+ }
+ }
- goto exit;
- }
- }
+ FileObject->FsContext = fileref->fcb;
- if (Irp->Overlay.AllocationSize.QuadPart > 0) {
- Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
+ ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+ if (!ccb) {
+ ERR("out of memory\n");
- if (!NT_SUCCESS(Status)) {
- ERR("extend_file returned %08x\n", Status);
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ free_fileref(fileref);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- goto exit;
- }
- }
+ RtlZeroMemory(ccb, sizeof(*ccb));
- if (!fileref->fcb->ads) {
- LIST_ENTRY* le;
+ ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ ccb->NodeSize = sizeof(*ccb);
+ ccb->disposition = RequestedDisposition;
+ ccb->options = options;
+ ccb->query_dir_offset = 0;
+ RtlInitUnicodeString(&ccb->query_string, NULL);
+ ccb->has_wildcard = FALSE;
+ ccb->specific_file = FALSE;
+ ccb->access = *granted_access;
+ ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
+ ccb->reserving = FALSE;
+ ccb->lxss = called_from_lxss();
- if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
- ULONG offset;
- FILE_FULL_EA_INFORMATION* eainfo;
+ ccb->fileref = fileref;
- Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
- if (!NT_SUCCESS(Status)) {
- ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+ FileObject->FsContext2 = ccb;
+ FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ if (NT_SUCCESS(Status)) {
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ Irp->IoStatus.Information = FILE_SUPERSEDED;
+ break;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ case FILE_OPEN:
+ case FILE_OPEN_IF:
+ Irp->IoStatus.Information = FILE_OPENED;
+ break;
- goto exit;
- }
+ case FILE_OVERWRITE:
+ case FILE_OVERWRITE_IF:
+ Irp->IoStatus.Information = FILE_OVERWRITTEN;
+ break;
+ }
+ }
- fileref->fcb->ealen = 4;
+ // Make sure paging files don't have any extents marked as being prealloc,
+ // as this would mean we'd have to lock exclusively when writing.
+ if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
+ LIST_ENTRY* le;
+ BOOL changed = FALSE;
- // capitalize EA name
- eainfo = Irp->AssociatedIrp.SystemBuffer;
- do {
- STRING s;
+ ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
- s.Length = s.MaximumLength = eainfo->EaNameLength;
- s.Buffer = eainfo->EaName;
+ le = fileref->fcb->extents.Flink;
- RtlUpperString(&s, &s);
+ while (le != &fileref->fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
- fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+ if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+ ext->extent_data.type = EXTENT_TYPE_REGULAR;
+ changed = TRUE;
+ }
- if (eainfo->NextEntryOffset == 0)
- break;
+ le = le->Flink;
+ }
- eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
- } while (TRUE);
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
- if (fileref->fcb->ea_xattr.Buffer)
- ExFreePool(fileref->fcb->ea_xattr.Buffer);
+ if (changed) {
+ fileref->fcb->extents_changed = TRUE;
+ mark_fcb_dirty(fileref->fcb);
+ }
- fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
- if (!fileref->fcb->ea_xattr.Buffer) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
+ fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
+ Vcb->disallow_dismount = TRUE;
+ }
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG oc = InterlockedIncrement(&fileref->open_count);
+ ERR("fileref %p: open_count now %i\n", fileref, oc);
+#else
+ InterlockedIncrement(&fileref->open_count);
+#endif
+ InterlockedIncrement(&Vcb->open_files);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ return STATUS_SUCCESS;
+}
- goto exit;
- }
+NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
+ root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
+ NTSTATUS Status;
+ fcb* fcb;
+ UINT64 parent = 0;
+ UNICODE_STRING name;
+ BOOL hl_alloc = FALSE;
+ file_ref *parfr, *fr;
+#ifdef __REACTOS__
+ hardlink* hl;
+#endif
- fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
- RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
- } else {
- if (fileref->fcb->ea_xattr.Length > 0) {
- ExFreePool(fileref->fcb->ea_xattr.Buffer);
- fileref->fcb->ea_xattr.Buffer = NULL;
- fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
+ Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb returned %08x\n", Status);
+ return Status;
+ }
- fileref->fcb->ea_changed = TRUE;
- fileref->fcb->ealen = 0;
- }
- }
+ if (fcb->fileref) {
+ *pfr = fcb->fileref;
+ increase_fileref_refcount(fcb->fileref);
+ return STATUS_SUCCESS;
+ }
- // remove streams and send notifications
- le = fileref->fcb->dir_children_index.Flink;
- while (le != &fileref->fcb->dir_children_index) {
- dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
- LIST_ENTRY* le2 = le->Flink;
+#ifndef __REACTOS__
+ hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+#else
+ hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+#endif
- if (dc->index == 0) {
- if (!dc->fileref) {
- file_ref* fr2;
+ name = hl->name;
+ parent = hl->parent;
- Status = open_fileref_child(Vcb, fileref, &dc->name, TRUE, TRUE, TRUE, PagedPool, &fr2, NULL);
- if (!NT_SUCCESS(Status))
- WARN("open_fileref_child returned %08x\n", Status);
- }
+ if (parent == inode) { // subvolume root
+ KEY searchkey;
+ traverse_ptr tp;
- if (dc->fileref) {
- send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
+ searchkey.obj_id = subvol->id;
+ searchkey.obj_type = TYPE_ROOT_BACKREF;
+ searchkey.offset = 0xffffffffffffffff;
- Status = delete_fileref(dc->fileref, NULL, NULL, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("delete_fileref returned %08x\n", Status);
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_item returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+ ROOT_REF* rr = (ROOT_REF*)tp.item->data;
+ LIST_ENTRY* le;
+ root* r = NULL;
+ ULONG stringlen;
- goto exit;
- }
- }
- } else
- break;
+ 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));
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
- le = le2;
- }
+ if (tp.item->size < offsetof(ROOT_REF, name[0]) + 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, offsetof(ROOT_REF, name[0]) + rr->n);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
}
- KeQuerySystemTime(&time);
- win_time_to_unix(time, &now);
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r2 = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r2->id == tp.item->key.offset) {
+ r = r2;
+ break;
+ }
- filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
+ le = le->Flink;
+ }
- if (fileref->fcb->ads) {
- fileref->parent->fcb->inode_item.st_mtime = now;
- fileref->parent->fcb->inode_item_changed = TRUE;
- mark_fcb_dirty(fileref->parent->fcb);
+ if (!r) {
+ ERR("couldn't find subvol %llx\n", tp.item->key.offset);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
- send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
- } else {
- mark_fcb_dirty(fileref->fcb);
+ Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fileref_by_inode returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
- oldatts = fileref->fcb->atts;
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
- defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
- fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
+ name.Length = name.MaximumLength = (UINT16)stringlen;
- if (RequestedDisposition == FILE_SUPERSEDE)
- fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
- else
- fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
+ if (stringlen == 0)
+ name.Buffer = NULL;
+ else {
+ name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
- if (fileref->fcb->atts != oldatts) {
- fileref->fcb->atts_changed = TRUE;
- fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
- filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ if (!name.Buffer) {
+ ERR("out of memory\n");
+ free_fcb(fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- fileref->fcb->inode_item.transid = Vcb->superblock.generation;
- fileref->fcb->inode_item.sequence++;
- fileref->fcb->inode_item.st_ctime = now;
- fileref->fcb->inode_item.st_mtime = now;
- fileref->fcb->inode_item_changed = TRUE;
+ Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(name.Buffer);
+ free_fcb(fcb);
+ return Status;
+ }
- send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
+ hl_alloc = TRUE;
}
} else {
- if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
- FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
+ ERR("couldn't find parent for subvol %llx\n", subvol->id);
+ free_fcb(fcb);
+ return STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fileref_by_inode returned %08x\n", Status);
+ free_fcb(fcb);
+ return Status;
+ }
+ }
- do {
- if (ffei->Flags & FILE_NEED_EA) {
- WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
- Status = STATUS_ACCESS_DENIED;
+ Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+ if (hl_alloc)
+ ExFreePool(name.Buffer);
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fileref_child returned %08x\n", Status);
- goto exit;
- }
+ free_fcb(fcb);
+ free_fileref(parfr);
- if (ffei->NextEntryOffset == 0)
- break;
+ return Status;
+ }
- ffei = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ffei) + ffei->NextEntryOffset);
- } while (TRUE);
- }
- }
+ *pfr = fr;
- FileObject->FsContext = fileref->fcb;
+ free_fcb(fcb);
+ free_fileref(parfr);
- ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
- if (!ccb) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_SUCCESS;
+}
- IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+ PFILE_OBJECT FileObject = NULL;
+ ULONG RequestedDisposition;
+ ULONG options;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ USHORT parsed;
+ ULONG fn_offset = 0;
+ file_ref *related, *fileref = NULL;
+ POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
+ ACCESS_MASK granted_access;
+ BOOL loaded_related = FALSE;
+ UNICODE_STRING fn;
+#ifdef DEBUG_STATS
+ LARGE_INTEGER time1, time2;
+ UINT8 open_type = 0;
+
+ time1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+ Irp->IoStatus.Information = 0;
+
+ RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
+ options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+
+ if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
+ WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ FileObject = IrpSp->FileObject;
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
+ struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+
+ related = relatedccb->fileref;
+ } else
+ related = NULL;
+
+ debug_create_options(options);
+
+ switch (RequestedDisposition) {
+ case FILE_SUPERSEDE:
+ TRACE("requested disposition: FILE_SUPERSEDE\n");
+ break;
+
+ case FILE_CREATE:
+ TRACE("requested disposition: FILE_CREATE\n");
+ break;
+
+ case FILE_OPEN:
+ TRACE("requested disposition: FILE_OPEN\n");
+ break;
+
+ case FILE_OPEN_IF:
+ TRACE("requested disposition: FILE_OPEN_IF\n");
+ break;
+
+ case FILE_OVERWRITE:
+ TRACE("requested disposition: FILE_OVERWRITE\n");
+ break;
+
+ case FILE_OVERWRITE_IF:
+ TRACE("requested disposition: FILE_OVERWRITE_IF\n");
+ break;
+
+ default:
+ ERR("unknown disposition: %x\n", RequestedDisposition);
+ Status = STATUS_NOT_IMPLEMENTED;
+ goto exit;
+ }
+
+ 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)) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto exit;
+ }
+
+ if (options & FILE_OPEN_BY_FILE_ID) {
+ if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+ UINT64 inode;
+
+ RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
+
+ if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
+ inode = Vcb->root_fileref->fcb->inode;
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, fileref);
- release_fcb_lock(Vcb);
+ if (inode == 0) { // we use 0 to mean the parent of a subvolume
+ fileref = related->parent;
+ increase_fileref_refcount(fileref);
+ Status = STATUS_SUCCESS;
+ } else
+ Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
+ goto loaded;
+ } else {
+ WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
+ Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
+ }
- RtlZeroMemory(ccb, sizeof(*ccb));
+ if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
- ccb->NodeType = BTRFS_NODE_TYPE_CCB;
- ccb->NodeSize = sizeof(*ccb);
- ccb->disposition = RequestedDisposition;
- ccb->options = options;
- ccb->query_dir_offset = 0;
- RtlInitUnicodeString(&ccb->query_string, NULL);
- ccb->has_wildcard = FALSE;
- ccb->specific_file = FALSE;
- ccb->access = granted_access;
- ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
- ccb->reserving = FALSE;
- ccb->lxss = called_from_lxss();
+ if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
+ ULONG fnoff;
- ccb->fileref = fileref;
+ Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
- FileObject->FsContext2 = ccb;
- FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
+ 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->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
- if (NT_SUCCESS(Status)) {
- switch (RequestedDisposition) {
- case FILE_SUPERSEDE:
- Irp->IoStatus.Information = FILE_SUPERSEDED;
- break;
+ if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+ Status = STATUS_REPARSE;
+ fileref = related;
+ parsed = (USHORT)fnoff - sizeof(WCHAR);
+ } else {
+ fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
+ fn.Length -= (USHORT)fnoff;
- case FILE_OPEN:
- case FILE_OPEN_IF:
- Irp->IoStatus.Information = FILE_OPENED;
- break;
+ Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
- case FILE_OVERWRITE:
- case FILE_OVERWRITE_IF:
- Irp->IoStatus.Information = FILE_OVERWRITTEN;
- break;
+ loaded_related = TRUE;
}
}
+ } else {
+ Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+ }
- // Make sure paging files don't have any extents marked as being prealloc,
- // as this would mean we'd have to lock exclusively when writing.
- if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
- LIST_ENTRY* le;
- BOOL changed = FALSE;
+loaded:
+ if (Status == STATUS_REPARSE) {
+ REPARSE_DATA_BUFFER* data;
- ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
+ ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
+ Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
- le = fileref->fcb->extents.Flink;
+ if (!NT_SUCCESS(Status)) {
+ ERR("get_reparse_block returned %08x\n", Status);
- while (le != &fileref->fcb->extents) {
- extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_REPARSE;
+ RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
- if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
- ext->extent_data.type = EXTENT_TYPE_REGULAR;
- changed = TRUE;
- }
+ data->Reserved = FileObject->FileName.Length - parsed;
- le = le->Flink;
- }
+ Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
- ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ free_fileref(fileref);
- if (changed) {
- fileref->fcb->extents_changed = TRUE;
- mark_fcb_dirty(fileref->fcb);
- }
+ goto exit;
+ }
+ }
+
+ if (NT_SUCCESS(Status) && fileref->deleted)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
- fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
- Vcb->disallow_dismount = TRUE;
+ if (NT_SUCCESS(Status)) {
+ if (RequestedDisposition == FILE_CREATE) {
+ TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
+ Status = STATUS_OBJECT_NAME_COLLISION;
+
+ free_fileref(fileref);
+
+ goto exit;
}
+ } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
+ TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
+ goto exit;
+ }
+ } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
+ TRACE("open_fileref returned %08x\n", Status);
+ goto exit;
+ } else {
+ ERR("open_fileref returned %08x\n", Status);
+ goto exit;
+ }
-#ifdef DEBUG_FCB_REFCOUNTS
- oc = InterlockedIncrement(&fileref->open_count);
- ERR("fileref %p: open_count now %i\n", fileref, oc);
-#else
- InterlockedIncrement(&fileref->open_count);
-#endif
- InterlockedIncrement(&Vcb->open_files);
+ if (NT_SUCCESS(Status)) { // file already exists
+ Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
+ if (!NT_SUCCESS(Status))
+ goto exit;
} else {
+ file_ref* existing_file;
+
#ifdef DEBUG_STATS
open_type = 2;
#endif
- Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
- release_fcb_lock(Vcb);
+ Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, &existing_file, rollback);
+
+ if (Status == STATUS_OBJECT_NAME_COLLISION) { // already exists
+ fileref = existing_file;
- Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
- granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
+ if (!NT_SUCCESS(Status))
+ goto exit;
+ } else {
+ Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
+ granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ }
}
if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
FileObject->Flags |= FO_CACHE_SUPPORTED;
exit:
- if (loaded_related) {
- acquire_fcb_lock_exclusive(Vcb);
- free_fileref(Vcb, related);
- release_fcb_lock(Vcb);
- }
+ if (loaded_related)
+ free_fileref(related);
if (Status == STATUS_SUCCESS) {
fcb* fcb2;
if (!skip_lock)
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+ ExAcquireResourceSharedLite(&Vcb->fileref_lock, TRUE);
+
Status = open_file(DeviceObject, Vcb, Irp, &rollback);
if (!NT_SUCCESS(Status))
else
clear_rollback(&rollback);
+ ExReleaseResourceLite(&Vcb->fileref_lock);
+
if (!skip_lock)
ExReleaseResourceLite(&Vcb->tree_lock);
}
#if (NTDDI_VERSION >= NTDDI_WIN10)
// not currently in mingw - introduced with Windows 10
-#ifndef FileIdInformation
+#ifndef _MSC_VER
#define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
#define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
if (!fileref)
return STATUS_INVALID_PARAMETER;
- acquire_fcb_lock_exclusive(Vcb);
-
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
end:
ExReleaseResourceLite(fcb->Header.Resource);
- release_fcb_lock(Vcb);
-
// send notification that directory is about to be deleted
if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
if (!fcb->adsxattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!fcb->adsdata.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
if (!fcb->sd) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!ext2) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
if (!ext2->csum) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!hl2) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!hl2->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl2);
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
ERR("out of memory\n");
ExFreePool(hl2->name.Buffer);
ExFreePool(hl2);
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
if (!fcb->reparse_xattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
if (!fcb->ea_xattr.Buffer) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
if (!xa2) {
ERR("out of memory\n");
- free_fcb(Vcb, fcb);
+ free_fcb(fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
+ reap_fcb(fcb);
ERR("SeAssignSecurity returned %08x\n", Status);
return Status;
}
if (!fcb->sd) {
+ reap_fcb(fcb);
ERR("SeAssignSecurity returned NULL security descriptor\n");
return STATUS_INTERNAL_ERROR;
}
fcb->inode_item_changed = TRUE;
- InsertTailList(&r->fcbs, &fcb->list_entry);
- InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.ValidDataLength.QuadPart = 0;
fcb->created = TRUE;
- mark_fcb_dirty(fcb);
if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+ acquire_fcb_lock_exclusive(Vcb);
+ InsertTailList(&r->fcbs, &fcb->list_entry);
+ InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+ r->fcbs_version++;
+ release_fcb_lock(Vcb);
+
+ mark_fcb_dirty(fcb);
+
*pfcb = fcb;
return STATUS_SUCCESS;
BTRFS_TIME now;
file_ref* origparent;
+ // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
+
InitializeListHead(&move_list);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
+ acquire_fcb_lock_exclusive(fileref->fcb->Vcb);
+
me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
if (!me) {
me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
increase_fileref_refcount(me->dummyfileref->parent);
- ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->nonpaged->children_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
- ExReleaseResourceLite(&me->dummyfileref->parent->nonpaged->children_lock);
+ ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
me->dummyfileref->debug_desc = me->fileref->debug_desc;
ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
}
- free_fileref(fileref->fcb->Vcb, me->fileref->parent);
+ free_fileref(me->fileref->parent);
me->fileref->parent = destdir;
- ExAcquireResourceExclusiveLite(&me->fileref->parent->nonpaged->children_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
- ExReleaseResourceLite(&me->fileref->parent->nonpaged->children_lock);
+ ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
me = CONTAINING_RECORD(le, move_entry, list_entry);
if (me->dummyfcb)
- free_fcb(fileref->fcb->Vcb, me->dummyfcb);
+ free_fcb(me->dummyfcb);
if (me->dummyfileref)
- free_fileref(fileref->fcb->Vcb, me->dummyfileref);
+ free_fileref(me->dummyfileref);
- free_fileref(fileref->fcb->Vcb, me->fileref);
+ free_fileref(me->fileref);
ExFreePool(me);
}
+ destdir->fcb->subvol->fcbs_version++;
+ fileref->fcb->subvol->fcbs_version++;
+
+ release_fcb_lock(fileref->fcb->Vcb);
+
return Status;
}
}
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
- acquire_fcb_lock_exclusive(Vcb);
+ ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
if (fcb->ads) {
}
if (fileref == oldfileref || oldfileref->deleted) {
- free_fileref(Vcb, oldfileref);
+ free_fileref(oldfileref);
oldfileref = NULL;
}
}
fileref->created = TRUE;
fileref->parent = related;
- ExAcquireResourceExclusiveLite(&fileref->parent->nonpaged->children_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
InsertHeadList(&fileref->list_entry, &fr2->list_entry);
RemoveEntryList(&fileref->list_entry);
- ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock);
+ ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
mark_fileref_dirty(fr2);
mark_fileref_dirty(fileref);
ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
}
- ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
InsertTailList(&related->children, &fileref->list_entry);
- ExReleaseResourceLite(&related->nonpaged->children_lock);
+ ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
if (fcb->inode_item.st_nlink > 1) {
// add new hardlink entry to fcb
fr2->parent->fcb->inode_item.st_ctime = now;
fr2->parent->fcb->inode_item.st_mtime = now;
- free_fileref(Vcb, fr2);
+ free_fileref(fr2);
fr2->parent->fcb->inode_item_changed = TRUE;
mark_fcb_dirty(fr2->parent->fcb);
end:
if (oldfileref)
- free_fileref(Vcb, oldfileref);
+ free_fileref(oldfileref);
if (!NT_SUCCESS(Status) && related)
- free_fileref(Vcb, related);
+ free_fileref(related);
if (!NT_SUCCESS(Status) && fr2)
- free_fileref(Vcb, fr2);
+ free_fileref(fr2);
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
do_rollback(Vcb, &rollback);
ExReleaseResourceLite(fcb->Header.Resource);
- release_fcb_lock(Vcb);
+ ExReleaseResourceLite(&Vcb->fileref_lock);
ExReleaseResourceLite(&Vcb->tree_lock);
return Status;
}
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
- acquire_fcb_lock_exclusive(Vcb);
+ ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
if (fcb->type == BTRFS_TYPE_DIRECTORY) {
goto end;
}
} else {
- free_fileref(Vcb, oldfileref);
+ free_fileref(oldfileref);
oldfileref = NULL;
}
}
fr2->dc = dc;
dc->fileref = fr2;
- ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+ ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
InsertTailList(&related->children, &fr2->list_entry);
- ExReleaseResourceLite(&related->nonpaged->children_lock);
+ ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
// add hardlink for existing fileref, if it's not there already
if (IsListEmpty(&fcb->hardlinks)) {
InsertTailList(&fcb->hardlinks, &hl->list_entry);
mark_fileref_dirty(fr2);
- free_fileref(Vcb, fr2);
+ free_fileref(fr2);
// update inode's INODE_ITEM
end:
if (oldfileref)
- free_fileref(Vcb, oldfileref);
+ free_fileref(oldfileref);
if (!NT_SUCCESS(Status) && related)
- free_fileref(Vcb, related);
+ free_fileref(related);
if (!NT_SUCCESS(Status) && fr2)
- free_fileref(Vcb, fr2);
+ free_fileref(fr2);
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
do_rollback(Vcb, &rollback);
ExReleaseResourceLite(fcb->Header.Resource);
- release_fcb_lock(Vcb);
+ ExReleaseResourceLite(&Vcb->fileref_lock);
ExReleaseResourceLite(&Vcb->tree_lock);
return Status;
return STATUS_SUCCESS;
}
-#endif /* __REACTOS__ */
-
-NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
- root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
- NTSTATUS Status;
- fcb* fcb;
- UINT64 parent = 0;
- UNICODE_STRING name;
- BOOL hl_alloc = FALSE;
- file_ref *parfr, *fr;
-
- Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fcb returned %08x\n", Status);
- return Status;
- }
-
- if (fcb->fileref) {
- *pfr = fcb->fileref;
- increase_fileref_refcount(fcb->fileref);
- return STATUS_SUCCESS;
- }
-
- // find hardlink if fcb doesn't have any loaded
- if (IsListEmpty(&fcb->hardlinks)) {
- KEY searchkey;
- traverse_ptr tp;
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_INODE_EXTREF;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("find_item returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- if (tp.item->key.obj_id == fcb->inode) {
- if (tp.item->key.obj_type == TYPE_INODE_REF) {
- INODE_REF* ir;
- ULONG stringlen;
-
- ir = (INODE_REF*)tp.item->data;
-
- parent = tp.item->key.offset;
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- name.Length = name.MaximumLength = (UINT16)stringlen;
-
- if (stringlen == 0)
- name.Buffer = NULL;
- else {
- name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
- if (!name.Buffer) {
- ERR("out of memory\n");
- free_fcb(Vcb, fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(name.Buffer);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- hl_alloc = TRUE;
- }
- } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
- INODE_EXTREF* ier;
- ULONG stringlen;
-
- ier = (INODE_EXTREF*)tp.item->data;
-
- parent = ier->dir;
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- name.Length = name.MaximumLength = (UINT16)stringlen;
-
- if (stringlen == 0)
- name.Buffer = NULL;
- else {
- name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
- if (!name.Buffer) {
- ERR("out of memory\n");
- free_fcb(Vcb, fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(name.Buffer);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- hl_alloc = TRUE;
- }
-
- }
- }
- } else {
- hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
-
- name = hl->name;
- parent = hl->parent;
- }
-
- if (parent == 0) {
- ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return STATUS_INVALID_PARAMETER;
- }
-
- if (parent == inode) { // subvolume root
- KEY searchkey;
- traverse_ptr tp;
-
- searchkey.obj_id = subvol->id;
- searchkey.obj_type = TYPE_ROOT_BACKREF;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("find_item returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return Status;
- }
-
- if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
- ROOT_REF* rr = (ROOT_REF*)tp.item->data;
- LIST_ENTRY* le;
- root* r = NULL;
- ULONG stringlen;
-
- 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));
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return STATUS_INTERNAL_ERROR;
- }
-
- if (tp.item->size < offsetof(ROOT_REF, name[0]) + 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, offsetof(ROOT_REF, name[0]) + rr->n);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return STATUS_INTERNAL_ERROR;
- }
-
- le = Vcb->roots.Flink;
- while (le != &Vcb->roots) {
- root* r2 = CONTAINING_RECORD(le, root, list_entry);
-
- if (r2->id == tp.item->key.offset) {
- r = r2;
- break;
- }
-
- le = le->Flink;
- }
-
- if (!r) {
- ERR("couldn't find subvol %llx\n", tp.item->key.offset);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return STATUS_INTERNAL_ERROR;
- }
-
- Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fileref_by_inode returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return Status;
- }
-
- if (hl_alloc) {
- ExFreePool(name.Buffer);
- hl_alloc = FALSE;
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- name.Length = name.MaximumLength = (UINT16)stringlen;
-
- if (stringlen == 0)
- name.Buffer = NULL;
- else {
- name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
-
- if (!name.Buffer) {
- ERR("out of memory\n");
- free_fcb(Vcb, fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- ExFreePool(name.Buffer);
- free_fcb(Vcb, fcb);
- return Status;
- }
-
- hl_alloc = TRUE;
- }
- } else {
- ERR("couldn't find parent for subvol %llx\n", subvol->id);
- free_fcb(Vcb, fcb);
- if (hl_alloc) ExFreePool(name.Buffer);
- return STATUS_INTERNAL_ERROR;
- }
- } else {
- Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fileref_by_inode returned %08x\n", Status);
- free_fcb(Vcb, fcb);
-
- if (hl_alloc)
- ExFreePool(name.Buffer);
-
- return Status;
- }
- }
-
- Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
-
- if (!NT_SUCCESS(Status)) {
- ERR("open_fileref_child returned %08x\n", Status);
-
- if (hl_alloc)
- ExFreePool(name.Buffer);
-
- free_fcb(Vcb, fcb);
- free_fileref(Vcb, parfr);
-
- return Status;
- }
-
- *pfr = fr;
-
- if (hl_alloc)
- ExFreePool(name.Buffer);
-
- free_fcb(Vcb, fcb);
- free_fileref(Vcb, parfr);
-
- return STATUS_SUCCESS;
-}
-#ifndef __REACTOS__
static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
NTSTATUS Status;
LIST_ENTRY* le;
len = bytes_needed;
}
} else {
- acquire_fcb_lock_exclusive(fcb->Vcb);
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
if (IsListEmpty(&fcb->hardlinks)) {
bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
}
}
- free_fileref(fcb->Vcb, parfr);
+ free_fileref(parfr);
}
le = le->Flink;
}
}
- release_fcb_lock(fcb->Vcb);
+ ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
}
fli->BytesNeeded = bytes_needed;