-/* Copyright (c) Mark Harmstone 2016
- *
+/* Copyright (c) Mark Harmstone 2016-17
+ *
* This file is part of WinBtrfs.
- *
+ *
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
- *
+ *
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include <sys/stat.h>
#endif /* __REACTOS__ */
#include "btrfs_drv.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#endif
+#include <ntddstor.h>
-extern PDEVICE_OBJECT devobj;
+extern PDEVICE_OBJECT master_devobj;
static WCHAR datastring[] = L"::$DATA";
-fcb* create_fcb(POOL_TYPE pool_type) {
+fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
fcb* fcb;
-
- fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
- if (!fcb) {
- ERR("out of memory\n");
- return NULL;
+
+ if (pool_type == NonPagedPool) {
+ fcb = ExAllocatePoolWithTag(pool_type, sizeof(struct _fcb), ALLOC_TAG);
+ if (!fcb) {
+ ERR("out of memory\n");
+ return NULL;
+ }
+ } else {
+ fcb = ExAllocateFromPagedLookasideList(&Vcb->fcb_lookaside);
+ if (!fcb) {
+ ERR("out of memory\n");
+ return NULL;
+ }
}
-
+
#ifdef DEBUG_FCB_REFCOUNTS
WARN("allocating fcb %p\n", fcb);
#endif
RtlZeroMemory(fcb, sizeof(struct _fcb));
-
+ fcb->pool_type = pool_type;
+
fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
fcb->Header.NodeByteSize = sizeof(struct _fcb);
-
- fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
+
+ fcb->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fcb_np_lookaside);
if (!fcb->nonpaged) {
ERR("out of memory\n");
- ExFreePool(fcb);
+
+ if (pool_type == NonPagedPool)
+ ExFreePool(fcb);
+ else
+ ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
+
return NULL;
}
RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
-
+
ExInitializeResourceLite(&fcb->nonpaged->paging_resource);
fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
-
+
ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
-
+
fcb->refcount = 1;
#ifdef DEBUG_FCB_REFCOUNTS
WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
#endif
-
+
ExInitializeResourceLite(&fcb->nonpaged->resource);
fcb->Header.Resource = &fcb->nonpaged->resource;
-
+
ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
-
+
FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
-
+
InitializeListHead(&fcb->extents);
InitializeListHead(&fcb->hardlinks);
-
+ InitializeListHead(&fcb->xattrs);
+
InitializeListHead(&fcb->dir_children_index);
InitializeListHead(&fcb->dir_children_hash);
InitializeListHead(&fcb->dir_children_hash_uc);
-
+
return fcb;
}
-file_ref* create_fileref() {
+file_ref* create_fileref(device_extension* Vcb) {
file_ref* fr;
-
- fr = ExAllocatePoolWithTag(PagedPool, sizeof(file_ref), ALLOC_TAG);
+
+ fr = ExAllocateFromPagedLookasideList(&Vcb->fileref_lookaside);
if (!fr) {
ERR("out of memory\n");
return NULL;
}
-
+
RtlZeroMemory(fr, sizeof(file_ref));
-
- fr->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(file_ref_nonpaged), ALLOC_TAG);
+
+ fr->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fileref_np_lookaside);
if (!fr->nonpaged) {
ERR("out of memory\n");
- ExFreePool(fr);
+ ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
return NULL;
}
-
+
fr->refcount = 1;
-
+
#ifdef DEBUG_FCB_REFCOUNTS
WARN("fileref %p: refcount now 1\n", fr);
#endif
-
+
InitializeListHead(&fr->children);
-
+
+ ExInitializeResourceLite(&fr->nonpaged->fileref_lock);
ExInitializeResourceLite(&fr->nonpaged->children_lock);
-
+
return fr;
}
-static NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, fcb* fcb,
- root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive, PIRP Irp) {
+NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive) {
NTSTATUS Status;
UNICODE_STRING fnus;
UINT32 hash;
LIST_ENTRY* le;
UINT8 c;
-
+ BOOL locked = FALSE;
+
if (!case_sensitive) {
Status = RtlUpcaseUnicodeString(&fnus, filename, TRUE);
-
+
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
return Status;
}
} else
fnus = *filename;
-
+
hash = calc_crc32c(0xffffffff, (UINT8*)fnus.Buffer, fnus.Length);
-
+
c = hash >> 24;
-
- ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
-
+
+ if (!ExIsResourceAcquiredSharedLite(&fcb->nonpaged->dir_children_lock)) {
+ ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
+ locked = TRUE;
+ }
+
if (case_sensitive) {
if (!fcb->hash_ptrs[c]) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
-
+
le = fcb->hash_ptrs[c];
while (le != &fcb->dir_children_hash) {
dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
-
+
if (dc->hash == hash) {
if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
if (dc->key.obj_type == TYPE_ROOT_ITEM) {
LIST_ENTRY* le2;
-
+
*subvol = NULL;
-
+
le2 = fcb->Vcb->roots.Flink;
while (le2 != &fcb->Vcb->roots) {
root* r2 = CONTAINING_RECORD(le2, root, list_entry);
-
+
if (r2->id == dc->key.obj_id) {
*subvol = r2;
break;
}
-
+
le2 = le2->Flink;
}
-
+
*inode = SUBVOL_ROOT_INODE;
} else {
*subvol = fcb->subvol;
*inode = dc->key.obj_id;
}
-
+
*pdc = dc;
Status = STATUS_SUCCESS;
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
-
+
le = le->Flink;
}
} else {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
-
+
le = fcb->hash_ptrs_uc[c];
while (le != &fcb->dir_children_hash_uc) {
dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
-
+
if (dc->hash_uc == hash) {
if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
if (dc->key.obj_type == TYPE_ROOT_ITEM) {
LIST_ENTRY* le2;
-
+
*subvol = NULL;
-
+
le2 = fcb->Vcb->roots.Flink;
while (le2 != &fcb->Vcb->roots) {
root* r2 = CONTAINING_RECORD(le2, root, list_entry);
-
+
if (r2->id == dc->key.obj_id) {
*subvol = r2;
break;
}
-
+
le2 = le2->Flink;
}
-
+
*inode = SUBVOL_ROOT_INODE;
} else {
*subvol = fcb->subvol;
*inode = dc->key.obj_id;
}
-
+
*pdc = dc;
-
+
Status = STATUS_SUCCESS;
goto end;
}
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
-
+
le = le->Flink;
}
}
-
+
Status = STATUS_OBJECT_NAME_NOT_FOUND;
end:
- ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
-
+ if (locked)
+ ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+
if (!case_sensitive)
ExFreePool(fnus.Buffer);
-
- return Status;
-}
-static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
- NTSTATUS Status;
- ULONG utf8len;
- char* utf8;
- UINT32 crc32;
- KEY searchkey;
- traverse_ptr tp, next_tp;
- BOOL success = FALSE, b;
-
- static char xapref[] = "user.";
- ULONG xapreflen = strlen(xapref);
-
- TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
-
- Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
- return FALSE;
- }
-
- TRACE("utf8len = %u\n", utf8len);
-
- utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
- if (!utf8) {
- ERR("out of memory\n");
- goto end;
- }
-
- RtlCopyMemory(utf8, xapref, xapreflen);
-
- Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
- goto end;
- }
-
- utf8len += xapreflen;
- utf8[utf8len] = 0;
-
- TRACE("utf8 = %s\n", utf8);
-
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
- TRACE("crc32 = %08x\n", crc32);
-
- if ((crc32 == EA_DOSATTRIB_HASH && utf8len == strlen(EA_DOSATTRIB) && RtlCompareMemory(utf8, EA_DOSATTRIB, utf8len) == utf8len) ||
- (crc32 == EA_EA_HASH && utf8len == strlen(EA_EA) && RtlCompareMemory(utf8, EA_EA, utf8len) == utf8len)) {
- return FALSE;
- }
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_XATTR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
- }
-
- if (!keycmp(tp.item->key, searchkey)) {
- if (tp.item->size < sizeof(DIR_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
- } else {
- ULONG len = tp.item->size, xasize;
- DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
-
- TRACE("found match on hash\n");
-
- while (len > 0) {
- if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
- TRACE("found exact match for %s\n", utf8);
-
- *hash = tp.item->key.offset;
-
- xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
- if (!xattr->Buffer) {
- ERR("out of memory\n");
- goto end;
- }
-
- xattr->Length = xattr->MaximumLength = di->n;
- RtlCopyMemory(xattr->Buffer, di->name, di->n);
- xattr->Buffer[di->n] = 0;
-
- success = TRUE;
- goto end;
- }
-
- xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
-
- if (len > xasize) {
- len -= xasize;
- di = (DIR_ITEM*)&di->name[di->m + di->n];
- } else
- break;
- }
- }
- }
-
- searchkey.offset = 0;
-
- Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
- }
-
- do {
- if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
- if (tp.item->size < sizeof(DIR_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
- } else {
- ULONG len = tp.item->size, xasize;
- DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
- ULONG utf16len;
-
- TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
-
- while (len > 0) {
- if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
- TRACE("found potential xattr %.*s\n", di->n, di->name);
- }
-
- Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
- } else {
- WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
- if (!utf16) {
- ERR("out of memory\n");
- goto end;
- }
-
- Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
-
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
- } else {
- UNICODE_STRING us;
-
- us.Buffer = utf16;
- us.Length = us.MaximumLength = (USHORT)utf16len;
-
- if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
- TRACE("found case-insensitive match for %s\n", utf8);
-
- *newstreamname = us;
- *hash = tp.item->key.offset;
-
- xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
- if (!xattr->Buffer) {
- ERR("out of memory\n");
- ExFreePool(utf16);
- goto end;
- }
-
- xattr->Length = xattr->MaximumLength = di->n;
- RtlCopyMemory(xattr->Buffer, di->name, di->n);
- xattr->Buffer[di->n] = 0;
-
- success = TRUE;
- goto end;
- }
- }
-
- ExFreePool(utf16);
- }
-
- xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
-
- if (len > xasize) {
- len -= xasize;
- di = (DIR_ITEM*)&di->name[di->m + di->n];
- } else
- break;
- }
- }
- }
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
- if (b) {
- tp = next_tp;
-
- if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
- break;
- }
- } while (b);
-
-end:
- ExFreePool(utf8);
-
- return success;
+ return Status;
}
-static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
- ULONG len, i, j, np;
+static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENTRY* parts, BOOL* stream) {
+ ULONG len, i;
BOOL has_stream;
- UNICODE_STRING* ps;
WCHAR* buf;
-
- np = 1;
-
+ name_bit* nb;
+
len = path->Length / sizeof(WCHAR);
if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
len--;
-
+
has_stream = FALSE;
for (i = 0; i < len; i++) {
if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
- np++;
has_stream = FALSE;
} else if (path->Buffer[i] == ':') {
has_stream = TRUE;
}
}
-
- if (has_stream)
- np++;
-
- ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
- if (!ps) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
-
+
buf = path->Buffer;
-
- j = 0;
+
for (i = 0; i < len; i++) {
if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
- ps[j].Buffer = buf;
- ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
- ps[j].MaximumLength = ps[j].Length;
-
+ nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+ if (!nb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nb->us.Buffer = buf;
+ nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
+ InsertTailList(parts, &nb->list_entry);
+
buf = &path->Buffer[i+1];
- j++;
}
}
-
- ps[j].Buffer = buf;
- ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
- ps[j].MaximumLength = ps[j].Length;
-
+
+ nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+ if (!nb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nb->us.Buffer = buf;
+ nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
+ InsertTailList(parts, &nb->list_entry);
+
if (has_stream) {
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
UNICODE_STRING dsus;
-
+
dsus.Buffer = datasuf;
- dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
-
- for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
- if (ps[j].Buffer[i] == ':') {
- ps[j+1].Buffer = &ps[j].Buffer[i+1];
- ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
-
- ps[j].Length = i * sizeof(WCHAR);
- ps[j].MaximumLength = ps[j].Length;
-
- j++;
-
+ dsus.Length = dsus.MaximumLength = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
+
+ for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) {
+ if (nb->us.Buffer[i] == ':') {
+ name_bit* nb2;
+
+ nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+ if (!nb2) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nb2->us.Buffer = &nb->us.Buffer[i+1];
+ nb2->us.Length = nb2->us.MaximumLength = (UINT16)(nb->us.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
+ InsertTailList(parts, &nb2->list_entry);
+
+ nb->us.Length = (UINT16)i * sizeof(WCHAR);
+ nb->us.MaximumLength = nb->us.Length;
+
+ nb = nb2;
+
break;
}
}
-
+
// FIXME - should comparison be case-insensitive?
// remove :$DATA suffix
- if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
- ps[j].Length -= dsus.Length;
-
- if (ps[j].Length == 0) {
- np--;
+ if (nb->us.Length >= dsus.Length && RtlCompareMemory(&nb->us.Buffer[(nb->us.Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
+ nb->us.Length -= dsus.Length;
+
+ if (nb->us.Length == 0) {
+ RemoveTailList(parts);
+ ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+
has_stream = FALSE;
}
}
-
+
// if path is just stream name, remove first empty item
if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
- ps[0] = ps[1];
- np--;
+ name_bit *nb1 = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
+
+ ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1);
}
-// for (i = 0; i < np; i++) {
-// ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
-// }
-
- *num_parts = np;
- *parts = ps;
*stream = has_stream;
-
- return STATUS_SUCCESS;
-}
-
-// #ifdef DEBUG_FCB_REFCOUNTS
-// static void print_fcbs(device_extension* Vcb) {
-// fcb* fcb = Vcb->fcbs;
-//
-// while (fcb) {
-// ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
-//
-// fcb = fcb->next;
-// }
-// }
-// #endif
-
-static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name, BOOL case_sensitive) {
- LIST_ENTRY* le;
- file_ref *c, *deleted = NULL;
- NTSTATUS Status;
- UNICODE_STRING ucus;
-#ifdef DEBUG_FCB_REFCOUNTS
- ULONG rc;
-#endif
-
- if (case_sensitive) {
- le = dir->children.Flink;
- while (le != &dir->children) {
- c = CONTAINING_RECORD(le, file_ref, list_entry);
-
- if (c->refcount > 0 && c->filepart.Length == name->Length &&
- RtlCompareMemory(c->filepart.Buffer, name->Buffer, name->Length) == name->Length) {
- if (c->deleted) {
- deleted = c;
- } else {
-#ifdef DEBUG_FCB_REFCOUNTS
- rc = InterlockedIncrement(&c->refcount);
- WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
-#else
- InterlockedIncrement(&c->refcount);
-#endif
- return c;
- }
- }
-
- le = le->Flink;
- }
-
- goto end;
- }
- Status = RtlUpcaseUnicodeString(&ucus, name, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- return NULL;
- }
-
- le = dir->children.Flink;
- while (le != &dir->children) {
- c = CONTAINING_RECORD(le, file_ref, list_entry);
-
- if (c->refcount > 0 && c->filepart_uc.Length == ucus.Length &&
- RtlCompareMemory(c->filepart_uc.Buffer, ucus.Buffer, ucus.Length) == ucus.Length) {
- if (c->deleted) {
- deleted = c;
- } else {
-#ifdef DEBUG_FCB_REFCOUNTS
- rc = InterlockedIncrement(&c->refcount);
- WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
-#else
- InterlockedIncrement(&c->refcount);
-#endif
- ExFreePool(ucus.Buffer);
-
- return c;
- }
- }
-
- le = le->Flink;
- }
-
- ExFreePool(ucus.Buffer);
-
-end:
- if (deleted)
- increase_fileref_refcount(deleted);
-
- return deleted;
+ return STATUS_SUCCESS;
}
-NTSTATUS load_csum(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
+NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp, next_tp;
UINT64 i, j;
BOOL b;
-
+
searchkey.obj_id = EXTENT_CSUM_ID;
searchkey.obj_type = TYPE_EXTENT_CSUM;
searchkey.offset = start;
-
+
Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
+
i = 0;
do {
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
ULONG readlen;
-
+
if (start < tp.item->key.offset)
j = 0;
else
j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i;
-
+
if (j * sizeof(UINT32) > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
ERR("checksum not found for %llx\n", start + (i * Vcb->superblock.sector_size));
return STATUS_INTERNAL_ERROR;
}
-
- readlen = min((tp.item->size / sizeof(UINT32)) - j, length - i);
+
+ readlen = (ULONG)min((tp.item->size / sizeof(UINT32)) - j, length - i);
RtlCopyMemory(&csum[i], tp.item->data + (j * sizeof(UINT32)), readlen * sizeof(UINT32));
i += readlen;
-
+
if (i == length)
break;
}
-
+
b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
-
+
if (b)
tp = next_tp;
} while (b);
-
+
if (i < length) {
ERR("could not read checksums: offset %llx, length %llx sectors\n", start, length);
return STATUS_INTERNAL_ERROR;
}
-
+
return STATUS_SUCCESS;
}
-NTSTATUS load_dir_children(fcb* fcb, BOOL ignore_size, PIRP Irp) {
+NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, BOOL ignore_size, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
NTSTATUS Status;
-
+ ULONG num_children = 0;
+
fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
-
+
fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs_uc) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
-
+
if (!ignore_size && fcb->inode_item.st_size == 0)
return STATUS_SUCCESS;
-
+
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = 2;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
+
+ Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
return Status;
}
-
+
if (keycmp(tp.item->key, searchkey) == -1) {
- if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
}
}
-
+
while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
dir_child* dc;
ULONG utf16len;
-
+
if (tp.item->size < sizeof(DIR_ITEM)) {
WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
goto cont;
}
-
+
if (di->n == 0) {
WARN("(%llx,%x,%llx): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
goto cont;
}
-
+
Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, di->name, di->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
dc->key = di->key;
dc->index = tp.item->key.offset;
dc->type = di->type;
dc->fileref = NULL;
-
+
dc->utf8.MaximumLength = dc->utf8.Length = di->n;
dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
if (!dc->utf8.Buffer) {
ExFreePool(dc);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
-
- dc->name.MaximumLength = dc->name.Length = utf16len;
+
+ dc->name.MaximumLength = dc->name.Length = (UINT16)utf16len;
dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
if (!dc->name.Buffer) {
ERR("out of memory\n");
ExFreePool(dc);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
Status = RtlUTF8ToUnicodeN(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(dc);
goto cont;
}
-
+
Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
ExFreePool(dc);
goto cont;
}
-
+
dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
-
+
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
-
+
insert_dir_child_into_hash_lists(fcb, dc);
-
+
+ num_children++;
+
cont:
- if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp))
+ if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp))
tp = next_tp;
else
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;
}
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
+NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
+ root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
NTSTATUS Status;
- fcb* fcb;
+ fcb *fcb, *deleted_fcb = NULL;
BOOL atts_set = FALSE, sd_set = FALSE, no_data;
LIST_ENTRY* lastle = NULL;
EXTENT_DATA* ed = NULL;
-
+
if (!IsListEmpty(&subvol->fcbs)) {
LIST_ENTRY* le = subvol->fcbs.Flink;
-
+
while (le != &subvol->fcbs) {
fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
-
+
if (fcb->inode == inode) {
if (!fcb->ads) {
+ if (fcb->deleted)
+ deleted_fcb = fcb;
+ else {
#ifdef DEBUG_FCB_REFCOUNTS
- LONG rc = InterlockedIncrement(&fcb->refcount);
+ LONG rc = InterlockedIncrement(&fcb->refcount);
- WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
+ WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
#else
- InterlockedIncrement(&fcb->refcount);
+ InterlockedIncrement(&fcb->refcount);
#endif
- *pfcb = fcb;
- return STATUS_SUCCESS;
+ *pfcb = fcb;
+ return STATUS_SUCCESS;
+ }
}
} else if (fcb->inode > inode) {
+ if (deleted_fcb) {
+ InterlockedIncrement(&deleted_fcb->refcount);
+ *pfcb = deleted_fcb;
+ return STATUS_SUCCESS;
+ }
+
lastle = le->Blink;
break;
}
-
+
le = le->Flink;
}
}
-
- fcb = create_fcb(pooltype);
+
+ if (deleted_fcb) {
+ InterlockedIncrement(&deleted_fcb->refcount);
+ *pfcb = deleted_fcb;
+ return STATUS_SUCCESS;
+ }
+
+ fcb = create_fcb(Vcb, pooltype);
if (!fcb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
fcb->Vcb = Vcb;
-
+
fcb->subvol = subvol;
fcb->inode = inode;
fcb->type = type;
-
+
searchkey.obj_id = inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
-
+
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, 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(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INVALID_PARAMETER;
}
-
+
if (tp.item->size > 0)
RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
-
+
if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
- if (fcb->inode_item.st_mode & __S_IFDIR)
+ if ((fcb->inode_item.st_mode & __S_IFDIR) == __S_IFDIR)
fcb->type = BTRFS_TYPE_DIRECTORY;
- else if (fcb->inode_item.st_mode & __S_IFCHR)
+ else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
fcb->type = BTRFS_TYPE_CHARDEV;
- else if (fcb->inode_item.st_mode & __S_IFBLK)
+ else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
fcb->type = BTRFS_TYPE_BLOCKDEV;
- else if (fcb->inode_item.st_mode & __S_IFIFO)
+ else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
fcb->type = BTRFS_TYPE_FIFO;
- else if (fcb->inode_item.st_mode & __S_IFLNK)
+ else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK)
fcb->type = BTRFS_TYPE_SYMLINK;
- else if (fcb->inode_item.st_mode & __S_IFSOCK)
+ else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
fcb->type = BTRFS_TYPE_SOCKET;
else
fcb->type = BTRFS_TYPE_FILE;
}
-
+
no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
-
+
while (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
tp = next_tp;
-
+
if (tp.item->key.obj_id > inode)
break;
-
+
if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
break;
-
+
if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_REF) {
ULONG len;
INODE_REF* ir;
-
+
len = tp.item->size;
ir = (INODE_REF*)tp.item->data;
-
+
while (len >= sizeof(INODE_REF) - 1) {
hardlink* hl;
ULONG stringlen;
-
+
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
if (!hl) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
hl->parent = tp.item->key.offset;
hl->index = ir->index;
-
+
hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
-
+
if (hl->utf8.Length > 0) {
hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
}
-
+
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
- hl->name.Length = hl->name.MaximumLength = stringlen;
-
+
+ hl->name.Length = hl->name.MaximumLength = (UINT16)stringlen;
+
if (stringlen == 0)
hl->name.Buffer = NULL;
else {
hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
-
+
if (!hl->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(hl->name.Buffer);
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
}
-
+
InsertTailList(&fcb->hardlinks, &hl->list_entry);
-
+
len -= sizeof(INODE_REF) - 1 + ir->n;
ir = (INODE_REF*)&ir->name[ir->n];
}
} else if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
ULONG len;
INODE_EXTREF* ier;
-
+
len = tp.item->size;
ier = (INODE_EXTREF*)tp.item->data;
-
+
while (len >= sizeof(INODE_EXTREF) - 1) {
hardlink* hl;
ULONG stringlen;
-
+
hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
if (!hl) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
hl->parent = ier->dir;
hl->index = ier->index;
-
+
hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
-
+
if (hl->utf8.Length > 0) {
hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
}
-
+
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
- hl->name.Length = hl->name.MaximumLength = stringlen;
-
+
+ hl->name.Length = hl->name.MaximumLength = (UINT16)stringlen;
+
if (stringlen == 0)
hl->name.Buffer = NULL;
else {
hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
-
+
if (!hl->name.Buffer) {
ERR("out of memory\n");
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(hl->name.Buffer);
ExFreePool(hl);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
}
-
+
InsertTailList(&fcb->hardlinks, &hl->list_entry);
-
+
len -= sizeof(INODE_EXTREF) - 1 + ier->n;
ier = (INODE_EXTREF*)&ier->name[ier->n];
}
} else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
- if (tp.item->size < sizeof(DIR_ITEM)) {
- ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+ ULONG len;
+ DIR_ITEM* di;
+
+ static char xapref[] = "user.";
+
+ if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0]));
continue;
}
-
- if (tp.item->key.offset == EA_REPARSE_HASH) {
- UINT8* xattrdata;
- UINT16 xattrlen;
-
- if (extract_xattr(tp.item->data, tp.item->size, EA_REPARSE, &xattrdata, &xattrlen)) {
- fcb->reparse_xattr.Buffer = (char*)xattrdata;
- fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
- }
- } else if (tp.item->key.offset == EA_EA_HASH) {
- UINT8* eadata;
- UINT16 ealen;
-
- if (extract_xattr(tp.item->data, tp.item->size, EA_EA, &eadata, &ealen)) {
- ULONG offset;
-
- Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
-
+
+ len = tp.item->size;
+ di = (DIR_ITEM*)tp.item->data;
+
+ do {
+ if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n)
+ break;
+
+ if (tp.item->key.offset == EA_REPARSE_HASH && di->n == strlen(EA_REPARSE) && RtlCompareMemory(EA_REPARSE, di->name, di->n) == di->n) {
+ if (di->m > 0) {
+ fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+ if (!fcb->reparse_xattr.Buffer) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m);
+ } else
+ fcb->reparse_xattr.Buffer = NULL;
+
+ fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = di->m;
+ } else if (tp.item->key.offset == EA_EA_HASH && di->n == strlen(EA_EA) && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
+ if (di->m > 0) {
+ ULONG offset;
+
+ Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
+
+ if (!NT_SUCCESS(Status))
+ WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+ else {
+ FILE_FULL_EA_INFORMATION* eainfo;
+
+ fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+ if (!fcb->ea_xattr.Buffer) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
+
+ fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = di->m;
+
+ fcb->ealen = 4;
+
+ // calculate ealen
+ eainfo = (FILE_FULL_EA_INFORMATION*)&di->name[di->n];
+ do {
+ fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+
+ if (eainfo->NextEntryOffset == 0)
+ break;
+
+ eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+ } while (TRUE);
+ }
+ }
+ } else if (tp.item->key.offset == EA_DOSATTRIB_HASH && di->n == strlen(EA_DOSATTRIB) && RtlCompareMemory(EA_DOSATTRIB, di->name, di->n) == di->n) {
+ if (di->m > 0) {
+ if (get_file_attributes_from_xattr(&di->name[di->n], di->m, &fcb->atts)) {
+ atts_set = TRUE;
+
+ if (fcb->type == BTRFS_TYPE_DIRECTORY)
+ fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (fcb->type == BTRFS_TYPE_SYMLINK)
+ fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ if (fcb->type != BTRFS_TYPE_DIRECTORY)
+ fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+ if (inode == SUBVOL_ROOT_INODE) {
+ if (subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
+ fcb->atts |= FILE_ATTRIBUTE_READONLY;
+ else
+ fcb->atts &= ~FILE_ATTRIBUTE_READONLY;
+ }
+ }
+ }
+ } else if (tp.item->key.offset == EA_NTACL_HASH && di->n == strlen(EA_NTACL) && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n) {
+ if (di->m > 0) {
+ fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+ if (!fcb->sd) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(fcb->sd, &di->name[di->n], di->m);
+
+ // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
+ // will fail if the ACLs aren't 32-bit aligned.
+ if (!RtlValidRelativeSecurityDescriptor(fcb->sd, di->m, 0))
+ ExFreePool(fcb->sd);
+ else
+ sd_set = TRUE;
+ }
+ } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH && di->n == strlen(EA_PROP_COMPRESSION) && RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) {
+ if (di->m > 0) {
+ const char lzo[] = "lzo";
+ const char zlib[] = "zlib";
+
+ if (di->m == strlen(lzo) && RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
+ fcb->prop_compression = PropCompression_LZO;
+ else if (di->m == strlen(zlib) && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
+ fcb->prop_compression = PropCompression_Zlib;
+ else
+ fcb->prop_compression = PropCompression_None;
+ }
+ } else if (di->n > strlen(xapref) && RtlCompareMemory(xapref, di->name, strlen(xapref)) == strlen(xapref)) {
+ dir_child* dc;
+ ULONG utf16len;
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[strlen(xapref)], di->n - (ULONG)strlen(xapref));
if (!NT_SUCCESS(Status)) {
- WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
- ExFreePool(eadata);
- } else {
- FILE_FULL_EA_INFORMATION* eainfo;
- fcb->ea_xattr.Buffer = (char*)eadata;
- fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
-
- fcb->ealen = 4;
-
- // calculate ealen
- eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
- do {
- fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-
- if (eainfo->NextEntryOffset == 0)
- break;
-
- eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
- } while (TRUE);
+ ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+ free_fcb(Vcb, fcb);
+ return Status;
}
- }
- } else if (tp.item->key.offset == EA_DOSATTRIB_HASH) {
- UINT8* xattrdata;
- UINT16 xattrlen;
-
- if (extract_xattr(tp.item->data, tp.item->size, EA_DOSATTRIB, &xattrdata, &xattrlen)) {
- if (get_file_attributes_from_xattr((char*)xattrdata, xattrlen, &fcb->atts)) {
- atts_set = TRUE;
-
- if (fcb->type == BTRFS_TYPE_DIRECTORY)
- fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
- else if (fcb->type == BTRFS_TYPE_SYMLINK)
- fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+ if (!dc) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
- ExFreePool(xattrdata);
- }
- } else if (tp.item->key.offset == EA_NTACL_HASH) {
- UINT16 buflen;
-
- if (extract_xattr(tp.item->data, tp.item->size, EA_NTACL, (UINT8**)&fcb->sd, &buflen)) {
- if (get_sd_from_xattr(fcb, buflen)) {
- sd_set = TRUE;
- } else
- ExFreePool(fcb->sd);
+
+ RtlZeroMemory(dc, sizeof(dir_child));
+
+ dc->utf8.MaximumLength = dc->utf8.Length = di->n - (UINT16)strlen(xapref);
+ dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
+ if (!dc->utf8.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc);
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(dc->utf8.Buffer, &di->name[strlen(xapref)], dc->utf8.Length);
+
+ dc->name.MaximumLength = dc->name.Length = (UINT16)utf16len;
+ dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
+ if (!dc->name.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc);
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlUTF8ToUnicodeN(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ free_fcb(Vcb, fcb);
+ return Status;
+ }
+
+ Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ free_fcb(Vcb, fcb);
+ return Status;
+ }
+
+ dc->size = di->m;
+
+ InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+ } else {
+ xattr* xa;
+
+ xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG);
+ if (!xa) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ xa->namelen = di->n;
+ xa->valuelen = di->m;
+ xa->dirty = FALSE;
+ RtlCopyMemory(xa->data, di->name, di->m + di->n);
+
+ InsertTailList(&fcb->xattrs, &xa->list_entry);
}
- }
+
+ len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
+
+ if (len < offsetof(DIR_ITEM, name[0]))
+ break;
+
+ di = (DIR_ITEM*)&di->name[di->m + di->n];
+ } while (TRUE);
} else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
extent* ext;
BOOL unique = FALSE;
-
+
ed = (EXTENT_DATA*)tp.item->data;
-
+
if (tp.item->size < sizeof(EXTENT_DATA)) {
- ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+ 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(fcb);
+
+ free_fcb(Vcb, fcb);
return STATUS_INTERNAL_ERROR;
}
-
+
if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
-
+
if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
- ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+ 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(fcb);
+
+ free_fcb(Vcb, fcb);
return STATUS_INTERNAL_ERROR;
}
-
- if (ed2->address == 0 && ed2->size == 0) // sparse
+
+ if (ed2->address == 0 || ed2->size == 0) // sparse
continue;
-
+
if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
}
-
- ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
+
+ ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
if (!ext) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
- if (!ext->data) {
- ERR("out of memory\n");
- ExFreePool(ext);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
+
ext->offset = tp.item->key.offset;
- RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
+ RtlCopyMemory(&ext->extent_data, tp.item->data, tp.item->size);
ext->datalen = tp.item->size;
ext->unique = unique;
ext->ignore = FALSE;
ext->inserted = FALSE;
-
- if (ed->type == EXTENT_TYPE_REGULAR && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
- EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
- UINT64 len;
-
- len = (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
-
- ext->csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
- if (!ext->csum) {
- ERR("out of memory\n");
- ExFreePool(ext);
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- Status = load_csum(Vcb, ext->csum, ed2->address + (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
-
- if (!NT_SUCCESS(Status)) {
- ERR("load_csum returned %08x\n", Status);
- ExFreePool(ext);
- free_fcb(fcb);
- return Status;
- }
- } else
- ext->csum = NULL;
-
+ ext->csum = NULL;
+
InsertTailList(&fcb->extents, &ext->list_entry);
}
}
-
+
if (fcb->type == BTRFS_TYPE_DIRECTORY) {
- Status = load_dir_children(fcb, FALSE, Irp);
+ Status = load_dir_children(Vcb, fcb, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_dir_children returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
}
-
+
if (no_data) {
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
else
fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
-
+
fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
}
-
+
if (!atts_set)
- fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
-
+ fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
+
if (!sd_set)
fcb_get_sd(fcb, parent, FALSE, Irp);
-
+
if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-
- if (!Vcb->readonly && !(subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
+
+ if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
fcb->atts_changed = TRUE;
mark_fcb_dirty(fcb);
}
}
-
+
if (lastle)
InsertHeadList(lastle, &fcb->list_entry);
else
InsertTailList(&subvol->fcbs, &fcb->list_entry);
-
+
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
+
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
*pfcb = fcb;
return STATUS_SUCCESS;
}
-NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
- UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) {
+static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
+ dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) {
fcb* fcb;
UINT8* xattrdata;
UINT16 xattrlen, overhead;
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
- LIST_ENTRY* lastle = NULL;
-
- if (!IsListEmpty(&subvol->fcbs)) {
- LIST_ENTRY* le = subvol->fcbs.Flink;
-
- while (le != &subvol->fcbs) {
- fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
-
- if (fcb->inode == inode) {
- if (fcb->ads && fcb->adsxattr.Length == xattr->Length &&
- RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
-#ifdef DEBUG_FCB_REFCOUNTS
- LONG rc = InterlockedIncrement(&fcb->refcount);
-
- WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
-#else
- InterlockedIncrement(&fcb->refcount);
-#endif
+ static char xapref[] = "user.";
+ ANSI_STRING xattr;
+ UINT32 crc32;
- *pfcb = fcb;
- return STATUS_SUCCESS;
- }
- } else if (fcb->inode > inode) {
- lastle = le->Blink;
- break;
- }
-
- le = le->Flink;
- }
+ xattr.Length = (UINT16)strlen(xapref) + dc->utf8.Length;
+ xattr.MaximumLength = xattr.Length + 1;
+ xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
+ if (!xattr.Buffer) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
- fcb = create_fcb(PagedPool);
+
+ RtlCopyMemory(xattr.Buffer, xapref, strlen(xapref));
+ RtlCopyMemory(&xattr.Buffer[strlen(xapref)], dc->utf8.Buffer, dc->utf8.Length);
+ xattr.Buffer[xattr.Length] = 0;
+
+ fcb = create_fcb(Vcb, PagedPool);
if (!fcb) {
ERR("out of memory\n");
+ ExFreePool(xattr.Buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) {
+
+ fcb->Vcb = Vcb;
+
+ crc32 = calc_crc32c(0xfffffffe, (UINT8*)xattr.Buffer, xattr.Length);
+
+ if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
ERR("get_xattr failed\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
+ ExFreePool(xattr.Buffer);
return STATUS_INTERNAL_ERROR;
}
- fcb->Vcb = Vcb;
-
fcb->subvol = parent->subvol;
fcb->inode = parent->inode;
fcb->type = parent->type;
fcb->ads = TRUE;
- fcb->adshash = streamhash;
- fcb->adsxattr = *xattr;
-
+ fcb->adshash = crc32;
+ fcb->adsxattr = xattr;
+
// find XATTR_ITEM overhead and hence calculate maximum length
-
+
searchkey.obj_id = parent->inode;
searchkey.obj_type = TYPE_XATTR_ITEM;
- searchkey.offset = streamhash;
+ searchkey.offset = crc32;
Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
+
if (keycmp(tp.item->key, searchkey)) {
ERR("error - could not find key for xattr\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INTERNAL_ERROR;
}
-
+
if (tp.item->size < xattrlen) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INTERNAL_ERROR;
}
-
+
overhead = tp.item->size - xattrlen;
-
+
fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
-
+
fcb->adsdata.Buffer = (char*)xattrdata;
fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
-
+
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = xattrlen;
fcb->Header.FileSize.QuadPart = xattrlen;
fcb->Header.ValidDataLength.QuadPart = xattrlen;
-
+
TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
-
- if (lastle)
- InsertHeadList(lastle, &fcb->list_entry);
- else
- InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
-
+
+ InsertHeadList(&parent->list_entry, &fcb->list_entry);
+
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
+
*pfcb = fcb;
-
- return STATUS_SUCCESS;
-}
-void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
- if (do_lock)
- ExAcquireResourceExclusiveLite(&parent->nonpaged->children_lock, TRUE);
-
- if (IsListEmpty(&parent->children))
- InsertTailList(&parent->children, &child->list_entry);
- else {
- LIST_ENTRY* le = parent->children.Flink;
- file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
-
- if (child->index < fr1->index)
- InsertHeadList(&parent->children, &child->list_entry);
- else {
- while (le != &parent->children) {
- file_ref* fr2 = (le->Flink == &parent->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
-
- if (child->index >= fr1->index && (!fr2 || fr2->index > child->index)) {
- InsertHeadList(&fr1->list_entry, &child->list_entry);
- break;
- }
-
- fr1 = fr2;
- le = le->Flink;
- }
- }
- }
-
- if (do_lock)
- ExReleaseResourceLite(&parent->nonpaged->children_lock);
+ return STATUS_SUCCESS;
}
-static NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
- POOL_TYPE pooltype, file_ref** psf2, PIRP Irp) {
+NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb,
+ _In_ file_ref* sf, _In_ PUNICODE_STRING name, _In_ BOOL case_sensitive, _In_ BOOL lastpart, _In_ BOOL streampart,
+ _In_ POOL_TYPE pooltype, _Out_ file_ref** psf2, _In_opt_ PIRP Irp) {
NTSTATUS Status;
file_ref* sf2;
-
- if (streampart) {
- UNICODE_STRING streamname;
- ANSI_STRING xattr;
- UINT32 streamhash;
-
- sf2 = search_fileref_children(sf, name, case_sensitive);
-
- if (sf2) {
- if (sf2->deleted) {
- TRACE("element in path has been deleted\n");
- free_fileref(sf2);
- return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
- }
-
- *psf2 = sf2;
-
- return STATUS_SUCCESS;
- }
-
- streamname.Buffer = NULL;
- streamname.Length = streamname.MaximumLength = 0;
- xattr.Buffer = NULL;
- xattr.Length = xattr.MaximumLength = 0;
-
- // FIXME - check if already opened
-
- if (!find_stream(Vcb, sf->fcb, name, &streamname, &streamhash, &xattr, Irp)) {
- TRACE("could not find stream %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
-
- return STATUS_OBJECT_NAME_NOT_FOUND;
- } else {
- fcb* fcb;
- if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
- RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
- WARN("not allowing user.DOSATTRIB to be opened as stream\n");
-
- return STATUS_OBJECT_NAME_NOT_FOUND;
- }
-
- Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fcb_stream returned %08x\n", Status);
- return Status;
- }
-
- sf2 = create_fileref();
- if (!sf2) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sf2->fcb = fcb;
+ if (sf->fcb == Vcb->dummy_fcb)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
- if (streamname.Buffer) // case has changed
- sf2->filepart = streamname;
- else {
- sf2->filepart.MaximumLength = sf2->filepart.Length = name->Length;
- sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
- if (!sf2->filepart.Buffer) {
- ERR("out of memory\n");
- free_fileref(sf2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(sf2->filepart.Buffer, name->Buffer, name->Length);
- }
-
- Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
+ if (streampart) {
+ BOOL locked = FALSE;
+ LIST_ENTRY* le;
+ UNICODE_STRING name_uc;
+ dir_child* dc = NULL;
+ fcb* fcb;
+
+ if (!case_sensitive) {
+ Status = RtlUpcaseUnicodeString(&name_uc, name, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(sf2);
return Status;
}
-
- // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+ }
- sf2->parent = (struct _file_ref*)sf;
- insert_fileref_child(sf, sf2, TRUE);
-
- increase_fileref_refcount(sf);
+ if (!ExIsResourceAcquiredSharedLite(&sf->fcb->nonpaged->dir_children_lock)) {
+ ExAcquireResourceSharedLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
+ locked = TRUE;
}
+
+ le = sf->fcb->dir_children_index.Flink;
+ while (le != &sf->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 == name->Length && RtlCompareMemory(dc2->name.Buffer, name->Buffer, dc2->name.Length) == dc2->name.Length) ||
+ (!case_sensitive && dc2->name_uc.Length == name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
+ ) {
+ dc = dc2;
+ break;
+ }
+ } else
+ break;
+
+ le = le->Flink;
+ }
+
+ if (!case_sensitive)
+ ExFreePool(name_uc.Buffer);
+
+ if (locked)
+ ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
+
+ if (!dc)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (dc->fileref) {
+ increase_fileref_refcount(dc->fileref);
+ *psf2 = dc->fileref;
+ return STATUS_SUCCESS;
+ }
+
+ Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb_stream returned %08x\n", Status);
+ return Status;
+ }
+
+ sf2 = create_fileref(Vcb);
+ if (!sf2) {
+ ERR("out of memory\n");
+ free_fcb(Vcb, fcb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sf2->fcb = fcb;
+
+ sf2->parent = (struct _file_ref*)sf;
+
+ sf2->dc = dc;
+ dc->fileref = sf2;
+
+ ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
+ InsertTailList(&sf->children, &sf2->list_entry);
+ ExReleaseResourceLite(&sf->nonpaged->children_lock);
+
+ increase_fileref_refcount(sf);
} else {
root* subvol;
UINT64 inode;
dir_child* dc;
-
- Status = find_file_in_dir(Vcb, name, sf->fcb, &subvol, &inode, &dc, case_sensitive, Irp);
+
+ Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
TRACE("could not find %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
return Status;
} else {
fcb* fcb;
-
+#ifdef DEBUG_STATS
+ LARGE_INTEGER time1, time2;
+#endif
+
if (dc->fileref) {
if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
- WARN("passed path including file as subdirectory\n");
+ TRACE("passed path including file as subdirectory\n");
return STATUS_OBJECT_PATH_NOT_FOUND;
}
-
+
InterlockedIncrement(&dc->fileref->refcount);
*psf2 = dc->fileref;
return STATUS_SUCCESS;
}
-
- Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("open_fcb returned %08x\n", Status);
- return Status;
+
+ if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id)) {
+ fcb = Vcb->dummy_fcb;
+ InterlockedIncrement(&fcb->refcount);
+ } else {
+#ifdef DEBUG_STATS
+ time1 = KeQueryPerformanceCounter(NULL);
+#endif
+ Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
+#ifdef DEBUG_STATS
+ time2 = KeQueryPerformanceCounter(NULL);
+ Vcb->stats.open_fcb_calls++;
+ Vcb->stats.open_fcb_time += time2.QuadPart - time1.QuadPart;
+#endif
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("open_fcb returned %08x\n", Status);
+ return Status;
+ }
}
-
+
if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
- WARN("passed path including file as subdirectory\n");
- free_fcb(fcb);
+ TRACE("passed path including file as subdirectory\n");
+ free_fcb(Vcb, fcb);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
- sf2 = create_fileref();
+ sf2 = create_fileref(Vcb);
if (!sf2) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
sf2->fcb = fcb;
-
+
if (dc->type == BTRFS_TYPE_DIRECTORY)
fcb->fileref = sf2;
-
- sf2->index = dc->index;
+
sf2->dc = dc;
dc->fileref = sf2;
-
- sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.Length, ALLOC_TAG);
- if (!sf2->utf8.Buffer) {
- ERR("out of memory\n");
- free_fileref(sf2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.Length, ALLOC_TAG);
- if (!sf2->filepart.Buffer) {
- ERR("out of memory\n");
- free_fileref(sf2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sf2->filepart_uc.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name_uc.Length, ALLOC_TAG);
- if (!sf2->filepart_uc.Buffer) {
- ERR("out of memory\n");
- free_fileref(sf2);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sf2->utf8.Length = sf2->utf8.MaximumLength = dc->utf8.Length;
- RtlCopyMemory(sf2->utf8.Buffer, dc->utf8.Buffer, dc->utf8.Length);
-
- sf2->filepart.Length = sf2->filepart.MaximumLength = dc->name.Length;
- RtlCopyMemory(sf2->filepart.Buffer, dc->name.Buffer, dc->name.Length);
-
- sf2->filepart_uc.Length = sf2->filepart_uc.MaximumLength = dc->name_uc.Length;
- RtlCopyMemory(sf2->filepart_uc.Buffer, dc->name_uc.Buffer, dc->name_uc.Length);
-
+
sf2->parent = (struct _file_ref*)sf;
-
- insert_fileref_child(sf, sf2, TRUE);
-
+
+ ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
+ InsertTailList(&sf->children, &sf2->list_entry);
+ ExReleaseResourceLite(&sf->nonpaged->children_lock);
+
increase_fileref_refcount(sf);
}
}
-
+
*psf2 = sf2;
-
+
return STATUS_SUCCESS;
}
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* parsed, ULONG* fn_offset,
- POOL_TYPE pooltype, BOOL case_sensitive, PIRP Irp) {
+NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Out_ file_ref** pfr,
+ _In_ PUNICODE_STRING fnus, _In_opt_ file_ref* related, _In_ BOOL parent, _Out_opt_ USHORT* parsed, _Out_opt_ ULONG* fn_offset, _In_ POOL_TYPE pooltype,
+ _In_ BOOL case_sensitive, _In_opt_ PIRP Irp) {
UNICODE_STRING fnus2;
file_ref *dir, *sf, *sf2;
- ULONG i, num_parts;
- UNICODE_STRING* parts = NULL;
+ LIST_ENTRY parts;
BOOL has_stream;
NTSTATUS Status;
-
+ LIST_ENTRY* le;
+
TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
-#ifdef DEBUG
+#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;
-
+
fnus2 = *fnus;
-
+
if (fnus2.Length < sizeof(WCHAR) && !related) {
ERR("error - fnus was too short\n");
return STATUS_INTERNAL_ERROR;
}
-
+
if (related && fnus->Length == 0) {
increase_fileref_refcount(related);
-
+
*pfr = related;
return STATUS_SUCCESS;
}
-
+
if (related) {
dir = related;
} else {
ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
-
+
if (fnus2.Length == sizeof(WCHAR)) {
- if (Vcb->root_fileref->open_count == 0) { // don't allow root to be opened on unmounted FS
- ULONG cc;
- IO_STATUS_BLOCK iosb;
-
- Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
-
- if (!NT_SUCCESS(Status))
- return Status;
- }
-
+ if (Vcb->root_fileref->open_count == 0 && !(Vcb->Vpb->Flags & VPB_MOUNTED)) // don't allow root to be opened on unmounted FS
+ return STATUS_DEVICE_NOT_READY;
+
increase_fileref_refcount(Vcb->root_fileref);
*pfr = Vcb->root_fileref;
-
+
if (fn_offset)
*fn_offset = 0;
-
+
return STATUS_SUCCESS;
}
-
+
dir = Vcb->root_fileref;
-
+
fnus2.Buffer++;
fnus2.Length -= sizeof(WCHAR);
fnus2.MaximumLength -= sizeof(WCHAR);
}
-
+
if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
file_desc_fileref(related), fnus->Length / sizeof(WCHAR), fnus->Buffer);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
-
- if (fnus->Length == 0) {
- num_parts = 0;
- } else if (fnus->Length == wcslen(datastring) * sizeof(WCHAR) &&
- RtlCompareMemory(fnus->Buffer, datastring, wcslen(datastring) * sizeof(WCHAR)) == wcslen(datastring) * sizeof(WCHAR)) {
- num_parts = 0;
- } else {
- Status = split_path(&fnus2, &parts, &num_parts, &has_stream);
+
+ InitializeListHead(&parts);
+
+ if (fnus->Length != 0 &&
+ (fnus->Length != wcslen(datastring) * sizeof(WCHAR) || RtlCompareMemory(fnus->Buffer, datastring, wcslen(datastring) * sizeof(WCHAR)) != wcslen(datastring) * sizeof(WCHAR))) {
+ Status = split_path(Vcb, &fnus2, &parts, &has_stream);
if (!NT_SUCCESS(Status)) {
ERR("split_path returned %08x\n", Status);
return Status;
}
}
-
+
sf = dir;
increase_fileref_refcount(dir);
-
- if (parent) {
- num_parts--;
-
- if (has_stream && num_parts > 0) {
- num_parts--;
+
+ if (parent && !IsListEmpty(&parts)) {
+ name_bit* nb;
+
+ nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
+ ExFreePool(nb);
+
+ if (has_stream && !IsListEmpty(&parts)) {
+ nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
+ ExFreePool(nb);
+
has_stream = FALSE;
}
}
-
- if (num_parts == 0) {
+
+ if (IsListEmpty(&parts)) {
Status = STATUS_SUCCESS;
*pfr = dir;
-
+
if (fn_offset)
*fn_offset = 0;
-
+
goto end2;
}
-
- for (i = 0; i < num_parts; i++) {
- BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
-
- Status = open_fileref_child(Vcb, sf, &parts[i], case_sensitive, lastpart, has_stream && i == num_parts - 1, pooltype, &sf2, Irp);
+
+ le = parts.Flink;
+ do {
+ name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
+ BOOL lastpart = le->Flink == &parts || (has_stream && le->Flink->Flink == &parts);
+ BOOL streampart = has_stream && le->Flink == &parts;
+
+ Status = open_fileref_child(Vcb, sf, &nb->us, case_sensitive, lastpart, streampart, pooltype, &sf2, Irp);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND)
TRACE("open_fileref_child returned %08x\n", Status);
else
ERR("open_fileref_child returned %08x\n", Status);
-
+
goto end;
}
-
- if (i == num_parts - 1) {
- if (fn_offset)
- *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
-
+
+ if (le->Flink == &parts) { // last entry
+ if (fn_offset) {
+ if (has_stream)
+ nb = CONTAINING_RECORD(le->Blink, name_bit, list_entry);
+
+ *fn_offset = (ULONG)(nb->us.Buffer - fnus->Buffer);
+ }
+
break;
}
-
+
if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
Status = STATUS_REPARSE;
-
- if (parsed)
- *parsed = (parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
-
+
+ if (parsed) {
+ name_bit* nb2 = CONTAINING_RECORD(le->Flink, name_bit, list_entry);
+
+ *parsed = (USHORT)(nb2->us.Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
+ }
+
break;
}
-
- free_fileref(sf);
+
+ free_fileref(Vcb, sf);
sf = sf2;
- }
-
+
+ le = le->Flink;
+ } while (le != &parts);
+
if (Status != STATUS_REPARSE)
Status = STATUS_SUCCESS;
*pfr = sf2;
-
+
end:
- free_fileref(sf);
-
+ free_fileref(Vcb, sf);
+
+ while (!IsListEmpty(&parts)) {
+ name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
+ ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+ }
+
end2:
- if (parts)
- ExFreePool(parts);
-
TRACE("returning %08x\n", Status);
-
- return Status;
-}
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) {
- KEY searchkey;
- traverse_ptr tp, prev_tp;
- NTSTATUS Status;
-
- ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
-
- if (fcb->last_dir_index != 0) {
- *index = fcb->last_dir_index;
- fcb->last_dir_index++;
- Status = STATUS_SUCCESS;
- goto end;
- }
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_DIR_INDEX + 1;
- searchkey.offset = 0;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto end;
- }
-
- if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) {
- if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE, Irp))
- tp = prev_tp;
- }
-
- if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
- fcb->last_dir_index = tp.item->key.offset + 1;
- } else
- fcb->last_dir_index = 2;
-
- *index = fcb->last_dir_index;
- fcb->last_dir_index++;
-
- Status = STATUS_SUCCESS;
-
-end:
- ExReleaseResourceLite(fcb->Header.Resource);
-
return Status;
}
-NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, UINT64 index, PANSI_STRING utf8, PUNICODE_STRING name, PUNICODE_STRING name_uc, UINT8 type, dir_child** pdc) {
+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;
-
+
dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
if (!dc) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
if (!dc->utf8.Buffer) {
ERR("out of memory\n");
ExFreePool(dc);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
if (!dc->name.Buffer) {
ERR("out of memory\n");
ExFreePool(dc);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, name_uc->Length, ALLOC_TAG);
- if (!dc->name_uc.Buffer) {
- ERR("out of memory\n");
- ExFreePool(dc->utf8.Buffer);
- ExFreePool(dc->name.Buffer);
- ExFreePool(dc);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
+
dc->key.obj_id = inode;
dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
- dc->key.offset = 0;
- dc->index = index;
+ dc->key.offset = subvol ? 0xffffffffffffffff : 0;
dc->type = type;
dc->fileref = NULL;
-
+
dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length;
RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
-
+
dc->name.Length = dc->name.MaximumLength = name->Length;
RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
-
- dc->name_uc.Length = dc->name_uc.MaximumLength = name_uc->Length;
- RtlCopyMemory(dc->name_uc.Buffer, name_uc->Buffer, name_uc->Length);
-
+
+ Status = RtlUpcaseUnicodeString(&dc->name_uc, name, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ return Status;
+ }
+
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);
-
+
+ if (IsListEmpty(&fcb->dir_children_index))
+ dc->index = 2;
+ else {
+ dir_child* dc2 = CONTAINING_RECORD(fcb->dir_children_index.Blink, dir_child, list_entry_index);
+
+ dc->index = max(2, dc2->index + 1);
+ }
+
InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
-
+
insert_dir_child_into_hash_lists(fcb, dc);
-
+
ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
-
+
*pdc = dc;
-
+
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options,
- FILE_FULL_EA_INFORMATION* ea, ULONG ealen, file_ref** pfr, LIST_ENTRY* rollback) {
+UINT32 inherit_mode(fcb* parfcb, BOOL is_dir) {
+ UINT32 mode;
+
+ if (!parfcb)
+ return 0755;
+
+ mode = parfcb->inode_item.st_mode & ~S_IFDIR;
+ mode &= ~S_ISVTX; // clear sticky bit
+ mode &= ~S_ISUID; // clear setuid bit
+
+ if (!is_dir)
+ mode &= ~S_ISGID; // if not directory, clear setgid bit
+
+ return mode;
+}
+
+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) {
NTSTATUS Status;
fcb* fcb;
ULONG utf8len;
char* utf8 = NULL;
- UINT64 dirpos, inode;
+ UINT64 inode;
UINT8 type;
LARGE_INTEGER time;
BTRFS_TIME now;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
- ULONG defda;
+ USHORT defda;
file_ref* fileref;
dir_child* dc;
+ ANSI_STRING utf8as;
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
#endif
-
+
+ if (parfileref->fcb == Vcb->dummy_fcb)
+ return STATUS_ACCESS_DENIED;
+
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
- if (!NT_SUCCESS(Status))
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N returned %08x\n", Status);
return Status;
-
+ }
+
utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
if (!utf8) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
Status = RtlUnicodeToUTF8N(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
if (!NT_SUCCESS(Status)) {
+ ERR("RtlUnicodeToUTF8N returned %08x\n", Status);
ExFreePool(utf8);
return Status;
}
-
+
utf8[utf8len] = 0;
-
- Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos, Irp);
- if (!NT_SUCCESS(Status)) {
- ERR("fcb_get_last_dir_index returned %08x\n", Status);
- ExFreePool(utf8);
- return Status;
- }
-
+
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
-
+
TRACE("create file %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
parfileref->fcb->inode_item.st_ctime = now;
parfileref->fcb->inode_item.st_mtime = now;
ExReleaseResourceLite(parfileref->fcb->Header.Resource);
-
+
parfileref->fcb->inode_item_changed = TRUE;
mark_fcb_dirty(parfileref->fcb);
-
+
inode = InterlockedIncrement64(&parfileref->fcb->subvol->lastinode);
-
+
type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
-
+
// FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
-
+
TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
-
+
IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
-
- defda = 0;
-
+
+ defda = FILE_ATTRIBUTE_ARCHIVE;
+
if (utf8[0] == '.')
defda |= FILE_ATTRIBUTE_HIDDEN;
-
+
if (options & FILE_DIRECTORY_FILE) {
defda |= FILE_ATTRIBUTE_DIRECTORY;
IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
- }
-
+ } else
+ IrpSp->Parameters.Create.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
+
TRACE("defda = %x\n", defda);
-
+
if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
IrpSp->Parameters.Create.FileAttributes = defda;
-
- fcb = create_fcb(pool_type);
+
+ fcb = create_fcb(Vcb, pool_type);
if (!fcb) {
ERR("out of memory\n");
ExFreePool(utf8);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
fcb->inode_item.st_blocks = 0;
fcb->inode_item.block_group = 0;
fcb->inode_item.st_nlink = 1;
-// fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
- fcb->inode_item.st_mode = parfileref->fcb ? (parfileref->fcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
+ fcb->inode_item.st_mode = inherit_mode(parfileref->fcb, type == BTRFS_TYPE_DIRECTORY); // use parent's permissions by default
fcb->inode_item.st_rdev = 0;
fcb->inode_item.flags = 0;
fcb->inode_item.sequence = 1;
fcb->inode_item.st_ctime = now;
fcb->inode_item.st_mtime = now;
fcb->inode_item.otime = now;
-
+
if (type == BTRFS_TYPE_DIRECTORY)
fcb->inode_item.st_mode |= S_IFDIR;
else {
fcb->inode_item.st_mode |= S_IFREG;
fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
}
-
+
if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
} else {
// inherit nodatacow flag from parent directory
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
-
+
if (type != BTRFS_TYPE_DIRECTORY)
fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
}
-
+
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
}
-
+
+ fcb->prop_compression = parfileref->fcb->prop_compression;
+ fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
+
fcb->inode_item_changed = TRUE;
-
+
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.ValidDataLength.QuadPart = 0;
-
- fcb->atts = IrpSp->Parameters.Create.FileAttributes;
+
+ fcb->atts = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
fcb->atts_changed = fcb->atts != defda;
-
+
#ifdef DEBUG_FCB_REFCOUNTS
rc = InterlockedIncrement(&parfileref->fcb->refcount);
WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
fcb->subvol = parfileref->fcb->subvol;
fcb->inode = inode;
fcb->type = type;
-
+ fcb->created = TRUE;
+ fcb->deleted = TRUE;
+
+ 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(fcb);
+ free_fcb(Vcb, fcb);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return Status;
}
-
+
fcb->sd_dirty = TRUE;
-
+
if (ea && ealen > 0) {
FILE_FULL_EA_INFORMATION* eainfo;
-
+
fcb->ealen = 4;
-
+
// capitalize EA names
eainfo = ea;
do {
STRING s;
-
+
s.Length = s.MaximumLength = eainfo->EaNameLength;
s.Buffer = eainfo->EaName;
-
+
RtlUpperString(&s, &s);
-
+
fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-
+
if (eainfo->NextEntryOffset == 0)
break;
-
+
eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
} while (TRUE);
-
+
fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, ealen, ALLOC_TAG);
if (!fcb->ea_xattr.Buffer) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
- RtlCopyMemory(fcb->ea_xattr.Buffer, ea, ealen);
-
+
+ fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = (UINT16)ealen;
+ RtlCopyMemory(fcb->ea_xattr.Buffer, ea, fcb->ea_xattr.Length);
+
fcb->ea_changed = TRUE;
}
-
- fileref = create_fileref();
+
+ fileref = create_fileref(Vcb);
if (!fileref) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
fileref->fcb = fcb;
- fileref->index = dirpos;
-
- fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
- fileref->utf8.Buffer = utf8;
-
- fileref->filepart.Length = fileref->filepart.MaximumLength = fpus->Length;
-
- if (fileref->filepart.Length == 0)
- fileref->filepart.Buffer = NULL;
- else {
- fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
-
- if (!fileref->filepart.Buffer) {
- ERR("out of memory\n");
- free_fcb(fcb);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(fileref->filepart.Buffer, fpus->Buffer, fpus->Length);
- }
-
- Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(fileref);
- return Status;
- }
if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
-
+
if (!NT_SUCCESS(Status)) {
ERR("extend_file returned %08x\n", Status);
- free_fileref(fileref);
+ free_fileref(Vcb, fileref);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return Status;
}
}
-
- fcb->created = TRUE;
- mark_fcb_dirty(fcb);
-
- fileref->created = TRUE;
- mark_fileref_dirty(fileref);
-
- fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
- fcb->subvol->root_item.ctime = now;
-
- fileref->parent = parfileref;
- insert_fileref_child(parfileref, fileref, TRUE);
-
- Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, fileref->index, &fileref->utf8, &fileref->filepart, &fileref->filepart_uc, fcb->type, &dc);
- if (!NT_SUCCESS(Status))
- WARN("add_dir_child returned %08x\n", Status);
-
- fileref->dc = dc;
- dc->fileref = fileref;
-
- increase_fileref_refcount(parfileref);
-
if (fcb->type == BTRFS_TYPE_DIRECTORY) {
fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs) {
ERR("out of memory\n");
- free_fileref(fileref);
+ free_fileref(Vcb, fileref);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
-
+
fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
if (!fcb->hash_ptrs_uc) {
ERR("out of memory\n");
- free_fileref(fileref);
+ free_fileref(Vcb, fileref);
+
+ ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+ parfileref->fcb->inode_item.st_size -= utf8len * 2;
+ ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
}
-
+
+ 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);
+
+ ExFreePool(utf8);
+
+ fileref->dc = dc;
+ dc->fileref = fileref;
+
+ ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+ InsertTailList(&parfileref->children, &fileref->list_entry);
+ ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+
+ increase_fileref_refcount(parfileref);
+
InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
+
*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;
}
-static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream,
- PIRP Irp, ULONG options, POOL_TYPE pool_type, BOOL case_sensitive, LIST_ENTRY* rollback) {
+static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
+ file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP Irp,
+ ULONG options, POOL_TYPE pool_type, BOOL case_sensitive, LIST_ENTRY* rollback) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
file_ref *fileref, *newpar, *parfileref;
fcb* fcb;
static char xapref[] = "user.";
static WCHAR DOSATTRIB[] = L"DOSATTRIB";
static WCHAR EA[] = L"EA";
- ULONG xapreflen = strlen(xapref), overhead;
+ static WCHAR reparse[] = L"reparse";
+ UINT16 xapreflen = (UINT16)strlen(xapref);
LARGE_INTEGER time;
BTRFS_TIME now;
- ULONG utf8len;
+ ULONG utf8len, overhead;
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
+ dir_child* dc;
+ ACCESS_MASK granted_access;
#ifdef DEBUG_FCB_REFCOUNTS
LONG rc;
#endif
-
+
TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
-
+
parfileref = *pparfileref;
-
+
+ if (parfileref->fcb == Vcb->dummy_fcb)
+ return STATUS_ACCESS_DENIED;
+
Status = open_fileref(Vcb, &newpar, fpus, parfileref, FALSE, NULL, NULL, PagedPool, case_sensitive, Irp);
-
+
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
UNICODE_STRING fpus2;
-
- if (!is_file_name_valid(fpus))
+
+ if (!is_file_name_valid(fpus, FALSE))
return STATUS_OBJECT_NAME_INVALID;
-
+
fpus2.Length = fpus2.MaximumLength = fpus->Length;
fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
-
+
if (!fpus2.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
-
+
+ SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
+ TRUE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
+ &granted_access, &Status)) {
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ return Status;
+ }
+
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, rollback);
-
+
if (!NT_SUCCESS(Status)) {
ERR("file_create2 returned %08x\n", Status);
ExFreePool(fpus2.Buffer);
return Status;
}
-
- send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
- send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+
+ 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);
} else if (!NT_SUCCESS(Status)) {
ERR("open_fileref returned %08x\n", Status);
return Status;
}
-
- free_fileref(parfileref);
-
+
parfileref = newpar;
*pparfileref = parfileref;
-
+
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");
return STATUS_INVALID_PARAMETER;
}
-
+
if (options & FILE_DIRECTORY_FILE) {
WARN("tried to create directory as stream\n");
return STATUS_INVALID_PARAMETER;
}
-
- if ((stream->Length == wcslen(DOSATTRIB) * sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
- (stream->Length == wcslen(EA) * sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length)) {
+
+ if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY)
+ return STATUS_ACCESS_DENIED;
+
+ SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ if (!SeAccessCheck(parfileref->fcb->sd, &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);
+ return Status;
+ }
+
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ if ((stream->Length == wcslen(DOSATTRIB) * sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
+ (stream->Length == wcslen(EA) * sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
+ (stream->Length == wcslen(reparse) * sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
return STATUS_OBJECT_NAME_INVALID;
}
-
- fcb = create_fcb(pool_type);
+
+ fcb = create_fcb(Vcb, pool_type);
if (!fcb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
fcb->Vcb = Vcb;
-
+
fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
fcb->Header.AllocationSize.QuadPart = 0;
fcb->Header.FileSize.QuadPart = 0;
fcb->Header.ValidDataLength.QuadPart = 0;
-
+
#ifdef DEBUG_FCB_REFCOUNTS
rc = InterlockedIncrement(&parfileref->fcb->refcount);
WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
fcb->subvol = parfileref->fcb->subvol;
fcb->inode = parfileref->fcb->inode;
fcb->type = parfileref->fcb->type;
-
+
fcb->ads = TRUE;
-
+
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
- fcb->adsxattr.Length = utf8len + xapreflen;
+
+ fcb->adsxattr.Length = (UINT16)utf8len + xapreflen;
fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
if (!fcb->adsxattr.Buffer) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
-
+
Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
+
fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
-
+
TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
-
+
fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
TRACE("adshash = %08x\n", fcb->adshash);
-
+
searchkey.obj_id = parfileref->fcb->inode;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = fcb->adshash;
-
+
Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("find_item returned %08x\n", Status);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return Status;
}
-
+
if (!keycmp(tp.item->key, searchkey))
overhead = tp.item->size;
else
overhead = 0;
-
+
fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
-
+
if (utf8len + xapreflen + overhead > fcb->adsmaxlen) {
WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + xapreflen, overhead, fcb->adsmaxlen);
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_DISK_FULL;
} else
fcb->adsmaxlen -= overhead + utf8len + xapreflen;
-
- fileref = create_fileref();
+
+ fileref = create_fileref(Vcb);
if (!fileref) {
ERR("out of memory\n");
- free_fcb(fcb);
+ free_fcb(Vcb, fcb);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
fileref->fcb = fcb;
- fileref->filepart.MaximumLength = fileref->filepart.Length = stream->Length;
- fileref->filepart.Buffer = ExAllocatePoolWithTag(pool_type, fileref->filepart.MaximumLength, ALLOC_TAG);
- if (!fileref->filepart.Buffer) {
+ dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+ if (!dc) {
+ ERR("out of memory\n");
+ free_fileref(Vcb, fileref);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(dc, sizeof(dir_child));
+
+ dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length - xapreflen;
+ dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
+ if (!dc->utf8.Buffer) {
+ ERR("out of memory\n");
+ ExFreePool(dc);
+ free_fileref(Vcb, fileref);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[xapreflen], fcb->adsxattr.Length - xapreflen);
+
+ dc->name.MaximumLength = dc->name.Length = stream->Length;
+ dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG);
+ if (!dc->name.Buffer) {
ERR("out of memory\n");
- free_fileref(fileref);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc);
+ free_fileref(Vcb, fileref);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length);
-
- Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+
+ RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
+
+ Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
- free_fileref(fileref);
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc);
+ free_fileref(Vcb, fileref);
return Status;
}
-
+
+ dc->fileref = fileref;
+ fileref->dc = dc;
+
+ InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
+
mark_fcb_dirty(fcb);
mark_fileref_dirty(fileref);
-
+
InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-
+
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
-
+
parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
parfileref->fcb->inode_item.sequence++;
parfileref->fcb->inode_item.st_ctime = now;
parfileref->fcb->inode_item_changed = TRUE;
-
+
mark_fcb_dirty(parfileref->fcb);
-
+
parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
parfileref->fcb->subvol->root_item.ctime = now;
-
+
fileref->parent = (struct _file_ref*)parfileref;
-
- insert_fileref_child(parfileref, fileref, TRUE);
-
+
+ ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+ InsertTailList(&parfileref->children, &fileref->list_entry);
+ ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+
increase_fileref_refcount(parfileref);
-
+
*pfileref = fileref;
-
+
+ send_notification_fileref(parfileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, &fileref->dc->name);
+
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related,
- PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
+// LXSS programs can be distinguished by the fact they have a NULL PEB.
+#ifdef _AMD64_
+static __inline BOOL called_from_lxss() {
+ NTSTATUS Status;
+ PROCESS_BASIC_INFORMATION pbi;
+ ULONG retlen;
+
+ Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwQueryInformationProcess returned %08x\n", Status);
+ return FALSE;
+ }
+
+ return !pbi.PebBaseAddress;
+}
+#else
+#define called_from_lxss() FALSE
+#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) {
NTSTATUS Status;
-// fcb *fcb, *parfcb = NULL;
file_ref *fileref, *parfileref = NULL;
- ULONG i, j, fn_offset;
-// ULONG utf8len;
+ ULONG i, j;
ccb* ccb;
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
UNICODE_STRING dsus, fpus, stream;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
- PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
#ifdef DEBUG_FCB_REFCOUNTS
LONG oc;
#endif
TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
-
+
if (Vcb->readonly)
return STATUS_MEDIA_WRITE_PROTECTED;
-
+
dsus.Buffer = datasuf;
- dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
+ dsus.Length = dsus.MaximumLength = (USHORT)wcslen(datasuf) * sizeof(WCHAR);
fpus.Buffer = NULL;
-
+
if (!loaded_related) {
Status = open_fileref(Vcb, &parfileref, fnus, related, TRUE, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
-
+
if (!NT_SUCCESS(Status))
goto end;
} else
parfileref = related;
-
+
if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
Status = STATUS_OBJECT_PATH_NOT_FOUND;
goto end;
}
-
- if (parfileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+
+ if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
-
+
i = (fnus->Length / sizeof(WCHAR))-1;
while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
-
+
j = i;
-
+
while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
-
- fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
+
+ fpus.MaximumLength = (USHORT)((j - i + 2) * sizeof(WCHAR));
fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
if (!fpus.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
-
- fpus.Length = (j - i + 1) * sizeof(WCHAR);
-
+
+ fpus.Length = (USHORT)((j - i + 1) * sizeof(WCHAR));
+
RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
fpus.Buffer[j - i + 1] = 0;
-
- fn_offset = i;
-
+
if (fpus.Length > dsus.Length) { // check for :$DATA suffix
UNICODE_STRING lb;
-
+
lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
lb.Length = lb.MaximumLength = dsus.Length;
-
+
TRACE("lb = %.*S\n", lb.Length/sizeof(WCHAR), lb.Buffer);
-
+
if (FsRtlAreNamesEqual(&dsus, &lb, TRUE, NULL)) {
TRACE("ignoring :$DATA suffix\n");
-
+
fpus.Length -= lb.Length;
-
+
if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
fpus.Length -= sizeof(WCHAR);
-
+
TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
}
}
-
+
stream.Length = 0;
-
- for (i = 0; i < fpus.Length/sizeof(WCHAR); i++) {
+
+ for (i = 0; i < fpus.Length / sizeof(WCHAR); i++) {
if (fpus.Buffer[i] == ':') {
- stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
+ stream.Length = (USHORT)(fpus.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
stream.Buffer = &fpus.Buffer[i+1];
fpus.Buffer[i] = 0;
- fpus.Length = i * sizeof(WCHAR);
+ fpus.Length = (USHORT)(i * sizeof(WCHAR));
break;
}
}
-
+
if (stream.Length > 0) {
Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("create_stream returned %08x\n", Status);
goto end;
}
+
+ IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess,
+ FileObject, &fileref->fcb->share_access);
} else {
- if (!is_file_name_valid(&fpus)) {
+ ACCESS_MASK granted_access;
+
+ if (!is_file_name_valid(&fpus, FALSE)) {
Status = STATUS_OBJECT_NAME_INVALID;
goto end;
}
-
+
+ SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
+ TRUE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
+ IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
+ &granted_access, &Status)) {
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ goto end;
+ }
+
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
ULONG offset;
-
+
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);
goto end;
}
}
-
+
Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
&fileref, rollback);
-
+
if (!NT_SUCCESS(Status)) {
ERR("file_create2 returned %08x\n", Status);
goto end;
}
-
- send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
- send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+
+ IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
+
+ send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
+ send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
}
-
+
FileObject->FsContext = fileref->fcb;
-
+
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
if (!ccb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
- free_fileref(fileref);
+ free_fileref(Vcb, fileref);
goto end;
}
-
+
RtlZeroMemory(ccb, sizeof(*ccb));
-
+
ccb->fileref = fileref;
-
+
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
- ccb->NodeSize = sizeof(ccb);
+ ccb->NodeSize = sizeof(*ccb);
ccb->disposition = disposition;
ccb->options = options;
ccb->query_dir_offset = 0;
RtlInitUnicodeString(&ccb->query_string, NULL);
ccb->has_wildcard = FALSE;
ccb->specific_file = FALSE;
- ccb->access = access_state->OriginalDesiredAccess;
+ ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
-
+ ccb->reserving = FALSE;
+ ccb->lxss = called_from_lxss();
+
#ifdef DEBUG_FCB_REFCOUNTS
oc = InterlockedIncrement(&fileref->open_count);
ERR("fileref %p: open_count now %i\n", fileref, oc);
InterlockedIncrement(&fileref->open_count);
#endif
InterlockedIncrement(&Vcb->open_files);
-
+
FileObject->FsContext2 = ccb;
-
- if (fn_offset > 0) {
- FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
- RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
- }
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
-
-// TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
-
-// ULONG fnlen;
-//
-// fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
-//
-// if (fcb->par != Vcb->root_fcb)
-// fcb->name_offset++;
-//
-// fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
-//
-// fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
-// if (!fcb->full_filename.Buffer) {
-// ERR("out of memory\n");
-// Status = STATUS_INSUFFICIENT_RESOURCES;
-// goto end;
-// }
-//
-// fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
-// RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
-//
-// if (fcb->par != Vcb->root_fcb)
-// fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
-//
-// RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
-
+
goto end2;
-
-end:
+
+end:
if (fpus.Buffer)
ExFreePool(fpus.Buffer);
-
+
end2:
if (parfileref && !loaded_related)
- free_fileref(parfileref);
-
+ free_fileref(Vcb, parfileref);
+
return Status;
}
static __inline void debug_create_options(ULONG RequestedOptions) {
if (RequestedOptions != 0) {
ULONG options = RequestedOptions;
-
+
TRACE("requested options:\n");
-
+
if (options & FILE_DIRECTORY_FILE) {
TRACE(" FILE_DIRECTORY_FILE\n");
options &= ~FILE_DIRECTORY_FILE;
TRACE(" FILE_NO_EA_KNOWLEDGE\n");
options &= ~FILE_NO_EA_KNOWLEDGE;
}
-
+
if (options & FILE_OPEN_REMOTE_INSTANCE) {
TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
options &= ~FILE_OPEN_REMOTE_INSTANCE;
TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
}
-
+
if (options & FILE_NO_COMPRESSION) {
TRACE(" FILE_NO_COMPRESSION\n");
options &= ~FILE_NO_COMPRESSION;
TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
options &= ~FILE_OPEN_REQUIRING_OPLOCK;
}
-
+
if (options & FILE_DISALLOW_EXCLUSIVE) {
TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
options &= ~FILE_DISALLOW_EXCLUSIVE;
TRACE(" FILE_OPEN_REPARSE_POINT\n");
options &= ~FILE_OPEN_REPARSE_POINT;
}
-
+
if (options & FILE_OPEN_NO_RECALL) {
TRACE(" FILE_OPEN_NO_RECALL\n");
options &= ~FILE_OPEN_NO_RECALL;
}
-
+
if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
}
-
+
if (options)
TRACE(" unknown options: %x\n", options);
} else {
static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
NTSTATUS Status;
-
+
if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
ULONG size, bytes_read, i;
-
+
if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
WARN("file was too short to be a reparse point\n");
return STATUS_INVALID_PARAMETER;
}
-
+
// 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
- size = min(0x10007, fcb->inode_item.st_size);
-
+ size = (ULONG)min(0x10007, fcb->inode_item.st_size);
+
+ if (size == 0)
+ return STATUS_INVALID_PARAMETER;
+
*data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
if (!*data) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- Status = read_file(fcb, *data, 0, size, &bytes_read, NULL, TRUE);
+
+ Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_file_fcb returned %08x\n", Status);
ExFreePool(*data);
return Status;
}
-
+
if (fcb->type == BTRFS_TYPE_SYMLINK) {
- ULONG stringlen, subnamelen, printnamelen, reqlen;
+ ULONG stringlen, reqlen;
+ UINT16 subnamelen, printnamelen;
REPARSE_DATA_BUFFER* rdb;
-
+
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)*data, bytes_read);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(*data);
return Status;
}
-
- subnamelen = stringlen;
- printnamelen = stringlen;
-
+
+ subnamelen = printnamelen = (USHORT)stringlen;
+
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
-
+
rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
-
+
if (!rdb) {
ERR("out of memory\n");
ExFreePool(*data);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
- rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+ rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
rdb->Reserved = 0;
-
+
rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
-
+
Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
stringlen, &stringlen, (char*)*data, size);
ExFreePool(*data);
return Status;
}
-
+
for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
}
-
+
RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
-
+
ExFreePool(*data);
-
+
*data = (UINT8*)rdb;
} else {
Status = FsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data);
} else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0)
return STATUS_INTERNAL_ERROR;
-
+
if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
WARN("xattr was too short to be a reparse point\n");
return STATUS_INTERNAL_ERROR;
}
-
+
Status = FsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer);
if (!NT_SUCCESS(Status)) {
ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
return Status;
}
-
+
*data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG);
if (!*data) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
}
-
+
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
- PFILE_OBJECT FileObject;
+static void fcb_load_csums(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, PIRP Irp) {
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ if (fcb->csum_loaded)
+ return;
+
+ if (IsListEmpty(&fcb->extents) || fcb->inode_item.flags & BTRFS_INODE_NODATASUM)
+ goto end;
+
+ le = fcb->extents.Flink;
+ while (le != &fcb->extents) {
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+ if (ext->extent_data.type == EXTENT_TYPE_REGULAR) {
+ EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
+ UINT64 len;
+
+ len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
+
+ ext->csum = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(len * sizeof(UINT32)), ALLOC_TAG);
+ if (!ext->csum) {
+ ERR("out of memory\n");
+ goto end;
+ }
+
+ Status = load_csum(Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_csum returned %08x\n", Status);
+ goto end;
+ }
+ }
+
+ le = le->Flink;
+ }
+
+end:
+ 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;
NTSTATUS Status;
ccb* ccb;
- device_extension* Vcb = DeviceObject->DeviceExtension;
- PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
USHORT parsed;
ULONG fn_offset = 0;
- file_ref *related, *fileref;
- POOL_TYPE pool_type = Stack->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
+ 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 = ((Stack->Parameters.Create.Options >> 24) & 0xff);
- options = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
-
+
+ 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");
- Status = STATUS_INVALID_PARAMETER;
- goto exit2;
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ FileObject = IrpSp->FileObject;
+
+ if (!FileObject) {
+ ERR("FileObject was NULL\n");
+ return STATUS_INVALID_PARAMETER;
}
- FileObject = Stack->FileObject;
-
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 exit2;
- }
-
- if (Vcb->readonly && Stack->Parameters.Create.SecurityContext->DesiredAccess &
- (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
- Status = STATUS_MEDIA_WRITE_PROTECTED;
- goto exit2;
+ goto exit;
}
-
+
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
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 (inode == 0) { // we use 0 to mean the parent of a subvolume
fileref = related->parent;
increase_fileref_refcount(fileref);
} else {
WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
Status = STATUS_NOT_IMPLEMENTED;
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
} else {
if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
Status = STATUS_OBJECT_NAME_INVALID;
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
-
- if (!related && RequestedDisposition != FILE_OPEN && !(Stack->Flags & SL_OPEN_TARGET_DIRECTORY)) {
+
+ if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
ULONG fnoff;
-
+
Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
- pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
-
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
+
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_OBJECT_PATH_NOT_FOUND;
else if (Status == STATUS_REPARSE)
fileref = related;
else if (NT_SUCCESS(Status)) {
fnoff *= sizeof(WCHAR);
- fnoff += related->filepart.Length + sizeof(WCHAR);
-
+ fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
+
if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
Status = STATUS_REPARSE;
fileref = related;
- parsed = fnoff - sizeof(WCHAR);
+ parsed = (USHORT)fnoff - sizeof(WCHAR);
} else {
fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
- fn.Length -= fnoff;
+ fn.Length -= (USHORT)fnoff;
+
+ Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
- Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
- pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
-
loaded_related = TRUE;
}
-
+
}
} else {
- Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
- pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+ Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+ pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
}
}
-
+
if (Status == STATUS_REPARSE) {
REPARSE_DATA_BUFFER* data;
-
+
ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
ExReleaseResourceLite(fileref->fcb->Header.Resource);
-
+
if (!NT_SUCCESS(Status)) {
ERR("get_reparse_block returned %08x\n", Status);
-
- free_fileref(fileref);
+
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_REPARSE;
+ RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+
+ data->Reserved = FileObject->FileName.Length - parsed;
+
+ Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
- Status = STATUS_REPARSE;
- RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
-
- data->Reserved = FileObject->FileName.Length - parsed;
-
- Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
-
- free_fileref(fileref);
- goto exit;
}
-
- if (NT_SUCCESS(Status) && fileref->deleted) {
- free_fileref(fileref);
-
+
+ if (NT_SUCCESS(Status) && fileref->deleted)
Status = STATUS_OBJECT_NAME_NOT_FOUND;
- }
-
+
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);
+
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
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");
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
} else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
TRACE("open_fileref returned %08x\n", Status);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
} else {
ERR("open_fileref returned %08x\n", Status);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
goto exit;
}
-
+
if (NT_SUCCESS(Status)) { // file already exists
file_ref* sf;
-
+ BOOL readonly;
+
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
LARGE_INTEGER zero;
-
+
#ifdef DEBUG_STATS
open_type = 1;
#endif
- if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+ if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
Status = STATUS_ACCESS_DENIED;
- free_fileref(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
if (Vcb->readonly) {
Status = STATUS_MEDIA_WRITE_PROTECTED;
- free_fileref(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
zero.QuadPart = 0;
if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
Status = STATUS_USER_MAPPED_FILE;
- free_fileref(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
}
-
- SeLockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-
- if (!SeAccessCheck(fileref->fcb->ads ? fileref->parent->fcb->sd : fileref->fcb->sd,
- &Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
- FALSE, Stack->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
- IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
- &granted_access, &Status)) {
- SeUnlockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
- WARN("SeAccessCheck failed, returning %08x\n", Status);
- goto exit;
- }
-
- SeUnlockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-
- // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
- if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && granted_access &
- (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC) &&
- fileref->fcb->inode != SUBVOL_ROOT_INODE) {
- Status = STATUS_ACCESS_DENIED;
- free_fileref(fileref);
- goto exit;
- }
-
+
+ if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
+ SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ 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);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ goto exit;
+ }
+
+ SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+ } else
+ granted_access = 0;
+
TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
-
+
sf = fileref;
while (sf) {
if (sf->delete_on_close) {
- WARN("could not open as deletion pending\n");
+ TRACE("could not open as deletion pending\n");
Status = STATUS_DELETE_PENDING;
-
- free_fileref(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
sf = sf->parent;
}
- if (fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
- ACCESS_MASK allowed = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC |
- SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
- FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES |
- FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
- FILE_TRAVERSE;
+ 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;
+
+ if (readonly) {
+ ACCESS_MASK allowed;
+
+ 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 (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
+ allowed |= WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
+
+ 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
+
+ allowed |= FILE_WRITE_ATTRIBUTES;
+ }
+
+ 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;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
- if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
- allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
-
- if (granted_access & ~allowed) {
- Status = STATUS_ACCESS_DENIED;
- free_fileref(fileref);
goto exit;
}
}
-
+
if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly ||
- fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) {
+ is_subvol_readonly(fileref->fcb->subvol, Irp) || readonly)) {
Status = STATUS_CANNOT_DELETE;
- free_fileref(fileref);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT)) {
REPARSE_DATA_BUFFER* data;
-
+
/* How reparse points work from the point of view of the filesystem appears to
* undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
* 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);
- free_fileref(fileref);
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_REPARSE;
+ Irp->IoStatus.Information = data->ReparseTag;
+
+ if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
+ data->Reserved = sizeof(WCHAR);
+
+ Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
- Status = STATUS_REPARSE;
- Irp->IoStatus.Information = data->ReparseTag;
-
- if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
- data->Reserved = sizeof(WCHAR);
-
- Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
-
- free_fileref(fileref);
- goto exit;
}
-
+
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);
Status = STATUS_FILE_IS_A_DIRECTORY;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
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));
- free_fileref(fileref);
Status = STATUS_NOT_A_DIRECTORY;
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
if (fileref->open_count > 0) {
- Status = IoCheckShareAccess(granted_access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
-
+ Status = IoCheckShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, FALSE);
+
if (!NT_SUCCESS(Status)) {
- WARN("IoCheckShareAccess failed, returning %08x\n", Status);
-
- free_fileref(fileref);
+ if (Status == STATUS_SHARING_VIOLATION)
+ TRACE("IoCheckShareAccess failed, returning %08x\n", Status);
+ else
+ WARN("IoCheckShareAccess failed, returning %08x\n", Status);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
- } else {
- IoSetShareAccess(granted_access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
- }
+
+ IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
+ } else
+ IoSetShareAccess(granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
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;
-
- free_fileref(fileref);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
}
-
+
if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
ULONG defda, oldatts, filter;
LARGE_INTEGER time;
BTRFS_TIME now;
-
- if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+
+ if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
WARN("cannot overwrite readonly file\n");
Status = STATUS_ACCESS_DENIED;
- free_fileref(fileref);
- goto exit;
- }
-
- // FIXME - where did we get this from?
-// if (fcb->refcount > 1) {
-// WARN("cannot overwrite open file (fcb = %p, refcount = %u)\n", fcb, fcb->refcount);
-// Status = STATUS_ACCESS_DENIED;
-// free_fcb(fcb);
-// goto exit;
-// }
-
- // FIXME - make sure not ADS!
- Status = truncate_file(fileref->fcb, 0, Irp, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("truncate_file returned %08x\n", Status);
- free_fileref(fileref);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
- if (Irp->Overlay.AllocationSize.QuadPart > 0) {
- Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
-
+
+ if (fileref->fcb->ads) {
+ Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, FALSE);
if (!NT_SUCCESS(Status)) {
- ERR("extend_file returned %08x\n", Status);
- free_fileref(fileref);
+ ERR("stream_set_end_of_file_information returned %08x\n", Status);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
- }
-
- if (Irp->AssociatedIrp.SystemBuffer && Stack->Parameters.Create.EaLength > 0) {
- ULONG offset;
- FILE_FULL_EA_INFORMATION* eainfo;
-
- Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, Stack->Parameters.Create.EaLength, &offset);
+ } else {
+ Status = truncate_file(fileref->fcb, 0, Irp, rollback);
if (!NT_SUCCESS(Status)) {
- ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
- free_fileref(fileref);
+ ERR("truncate_file returned %08x\n", Status);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
- fileref->fcb->ealen = 4;
-
- // capitalize EA name
- eainfo = Irp->AssociatedIrp.SystemBuffer;
- do {
- STRING s;
-
- s.Length = s.MaximumLength = eainfo->EaNameLength;
- s.Buffer = eainfo->EaName;
-
- RtlUpperString(&s, &s);
-
- fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-
- if (eainfo->NextEntryOffset == 0)
- break;
-
- eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
- } while (TRUE);
-
- if (fileref->fcb->ea_xattr.Buffer)
- ExFreePool(fileref->fcb->ea_xattr.Buffer);
-
- fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, Stack->Parameters.Create.EaLength, ALLOC_TAG);
- if (!fileref->fcb->ea_xattr.Buffer) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
-
- free_fileref(fileref);
+ }
+
+ if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+ Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("extend_file returned %08x\n", Status);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
- fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = Stack->Parameters.Create.EaLength;
- RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, Stack->Parameters.Create.EaLength);
- } 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;
- }
}
-
- filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
-
- mark_fcb_dirty(fileref->fcb);
-
- oldatts = fileref->fcb->atts;
-
- defda = get_file_attributes(Vcb, &fileref->fcb->inode_item, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
- fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
-
- if (RequestedDisposition == FILE_SUPERSEDE)
- fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
- else
- fileref->fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
-
- if (fileref->fcb->atts != oldatts) {
- fileref->fcb->atts_changed = TRUE;
- fileref->fcb->atts_deleted = Stack->Parameters.Create.FileAttributes == defda;
- filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+
+ if (!fileref->fcb->ads) {
+ if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
+ ULONG offset;
+ FILE_FULL_EA_INFORMATION* eainfo;
+
+ 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);
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ goto exit;
+ }
+
+ fileref->fcb->ealen = 4;
+
+ // capitalize EA name
+ eainfo = Irp->AssociatedIrp.SystemBuffer;
+ do {
+ STRING s;
+
+ s.Length = s.MaximumLength = eainfo->EaNameLength;
+ s.Buffer = eainfo->EaName;
+
+ RtlUpperString(&s, &s);
+
+ fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+
+ if (eainfo->NextEntryOffset == 0)
+ break;
+
+ eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+ } while (TRUE);
+
+ if (fileref->fcb->ea_xattr.Buffer)
+ ExFreePool(fileref->fcb->ea_xattr.Buffer);
+
+ 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;
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
+ 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;
+ }
+ }
}
-
+
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
-
- 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;
-
- // FIXME - truncate streams
- // FIXME - do we need to alter parent directory's times?
-
- send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED);
+
+ filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
+
+ 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);
+
+ send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
+ } else {
+ mark_fcb_dirty(fileref->fcb);
+
+ oldatts = fileref->fcb->atts;
+
+ 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);
+
+ 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 (fileref->fcb->atts != oldatts) {
+ fileref->fcb->atts_changed = TRUE;
+ fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
+ filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ 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");
- free_fileref(fileref);
Status = STATUS_ACCESS_DENIED;
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
if (ffei->NextEntryOffset == 0)
break;
-
+
ffei = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ffei) + ffei->NextEntryOffset);
} while (TRUE);
}
}
-
+
FileObject->FsContext = fileref->fcb;
-
+
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
if (!ccb) {
ERR("out of memory\n");
- free_fileref(fileref);
Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
+
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, fileref);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
goto exit;
}
-
+
RtlZeroMemory(ccb, sizeof(*ccb));
-
+
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
- ccb->NodeSize = sizeof(ccb);
+ ccb->NodeSize = sizeof(*ccb);
ccb->disposition = RequestedDisposition;
ccb->options = options;
ccb->query_dir_offset = 0;
ccb->has_wildcard = FALSE;
ccb->specific_file = FALSE;
ccb->access = granted_access;
- ccb->case_sensitive = Stack->Flags & SL_CASE_SENSITIVE;
-
+ ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
+ ccb->reserving = FALSE;
+ ccb->lxss = called_from_lxss();
+
ccb->fileref = fileref;
-
+
FileObject->FsContext2 = ccb;
-
- if (fn_offset > 0) {
- fn.Length -= fn_offset * sizeof(WCHAR);
- RtlMoveMemory(&fn.Buffer[0], &fn.Buffer[fn_offset], fn.Length);
- }
-
FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
-
+
if (NT_SUCCESS(Status)) {
switch (RequestedDisposition) {
case FILE_SUPERSEDE:
Irp->IoStatus.Information = FILE_SUPERSEDED;
break;
-
+
case FILE_OPEN:
case FILE_OPEN_IF:
Irp->IoStatus.Information = FILE_OPENED;
break;
-
+
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
Irp->IoStatus.Information = FILE_OVERWRITTEN;
break;
}
}
-
+
// 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 (Stack->Flags & SL_OPEN_PAGING_FILE) {
+ if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
LIST_ENTRY* le;
BOOL changed = FALSE;
-
+
ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
-
+
le = fileref->fcb->extents.Flink;
-
+
while (le != &fileref->fcb->extents) {
extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-
- if (ext->data->type == EXTENT_TYPE_PREALLOC) {
- ext->data->type = EXTENT_TYPE_REGULAR;
+
+ if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+ ext->extent_data.type = EXTENT_TYPE_REGULAR;
changed = TRUE;
}
-
+
le = le->Flink;
}
-
+
ExReleaseResourceLite(fileref->fcb->Header.Resource);
-
+
if (changed) {
fileref->fcb->extents_changed = TRUE;
mark_fcb_dirty(fileref->fcb);
}
-
+
fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
Vcb->disallow_dismount = TRUE;
}
-
+
#ifdef DEBUG_FCB_REFCOUNTS
oc = InterlockedIncrement(&fileref->open_count);
ERR("fileref %p: open_count now %i\n", fileref, oc);
open_type = 2;
#endif
Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+
Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
}
-
+
if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
FileObject->Flags |= FO_CACHE_SUPPORTED;
-
+
exit:
- ExReleaseResourceLite(&Vcb->fcb_lock);
-
-exit2:
- if (loaded_related)
- free_fileref(related);
-
- if (NT_SUCCESS(Status)) {
+ if (loaded_related) {
+ ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+ free_fileref(Vcb, related);
+ ExReleaseResourceLite(&Vcb->fcb_lock);
+ }
+
+ if (Status == STATUS_SUCCESS) {
+ fcb* fcb2;
+
+ IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= granted_access;
+ IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(granted_access | MAXIMUM_ALLOWED);
+
if (!FileObject->Vpb)
FileObject->Vpb = DeviceObject->Vpb;
- } else {
- if (Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
- TRACE("returning %08x\n", Status);
- }
-
+
+ fcb2 = FileObject->FsContext;
+
+ if (fcb2->ads) {
+ struct _ccb* ccb2 = FileObject->FsContext2;
+
+ fcb2 = ccb2->fileref->parent->fcb;
+ }
+
+ ExAcquireResourceExclusiveLite(fcb2->Header.Resource, TRUE);
+ fcb_load_csums(Vcb, fcb2, Irp);
+ ExReleaseResourceLite(fcb2->Header.Resource);
+ } else if (Status != STATUS_REPARSE && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
+ TRACE("returning %08x\n", Status);
+
#ifdef DEBUG_STATS
time2 = KeQueryPerformanceCounter(NULL);
-
+
if (open_type == 0) {
Vcb->stats.open_total_time += time2.QuadPart - time1.QuadPart;
Vcb->stats.num_opens++;
Vcb->stats.num_creates++;
}
#endif
-
+
return Status;
}
-NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
+static NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
NTSTATUS Status;
LIST_ENTRY* le;
-
+ BOOL need_verify = FALSE;
+
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-
+
le = Vcb->devices.Flink;
while (le != &Vcb->devices) {
device* dev = CONTAINING_RECORD(le, device, list_entry);
-
- if (dev->removable) {
+
+ if (dev->devobj && dev->removable) {
ULONG cc;
IO_STATUS_BLOCK iosb;
-
+
Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
-
- if (!NT_SUCCESS(Status)) {
- ERR("dev_ioctl returned %08x\n", Status);
+
+ if (IoIsErrorUserInduced(Status)) {
+ ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
+ need_verify = TRUE;
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
goto end;
- }
-
- if (iosb.Information < sizeof(ULONG)) {
+ } else if (iosb.Information < sizeof(ULONG)) {
ERR("iosb.Information was too short\n");
Status = STATUS_INTERNAL_ERROR;
- goto end;
- }
-
- if (cc != dev->change_count) {
- PDEVICE_OBJECT devobj;
-
+ } else if (cc != dev->change_count) {
dev->devobj->Flags |= DO_VERIFY_VOLUME;
-
- devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
- IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
-
- if (!devobj) {
- devobj = IoGetDeviceToVerify(PsGetCurrentThread());
- IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
- }
-
- devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
-
- if (devobj)
- IoVerifyVolume(devobj, FALSE);
-
- Status = STATUS_VERIFY_REQUIRED;
- goto end;
+ need_verify = TRUE;
}
}
-
+
le = le->Flink;
}
-
+
Status = STATUS_SUCCESS;
-
+
end:
ExReleaseResourceLite(&Vcb->tree_lock);
-
+
+ if (need_verify) {
+ PDEVICE_OBJECT devobj;
+
+ devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+ IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+
+ if (!devobj) {
+ devobj = IoGetDeviceToVerify(PsGetCurrentThread());
+ IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+ }
+
+ devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+
+ if (devobj)
+ Status = IoVerifyVolume(devobj, FALSE);
+ else
+ Status = STATUS_VERIFY_REQUIRED;
+ }
+
return Status;
}
return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? TRUE : FALSE;
}
-NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_CREATE)
+_Function_class_(DRIVER_DISPATCH)
+NTSTATUS drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level, locked = FALSE;
- LIST_ENTRY rollback;
-
- TRACE("create (flags = %x)\n", Irp->Flags);
-
- InitializeListHead(&rollback);
-
+
FsRtlEnterFileSystem();
-
+
+ TRACE("create (flags = %x)\n", Irp->Flags);
+
top_level = is_top_level(Irp);
-
+
/* return success if just called for FS device object */
- if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
+ if (DeviceObject == master_devobj) {
TRACE("create called for FS device object\n");
-
+
Irp->IoStatus.Information = FILE_OPENED;
Status = STATUS_SUCCESS;
+ goto exit;
+ } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_create(DeviceObject, Irp);
+ goto exit;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
+ Status = STATUS_DEVICE_NOT_READY;
+ goto exit;
+ }
+
+ if (Vcb->removing) {
+ Status = STATUS_ACCESS_DENIED;
goto exit;
}
-
+
Status = verify_vcb(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("verify_vcb returned %08x\n", Status);
goto exit;
}
-
+
ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
locked = TRUE;
-
+
IrpSp = IoGetCurrentIrpStackLocation(Irp);
-
+
if (IrpSp->Flags != 0) {
UINT32 flags = IrpSp->Flags;
-
+
TRACE("flags:\n");
-
+
if (flags & SL_CASE_SENSITIVE) {
TRACE("SL_CASE_SENSITIVE\n");
flags &= ~SL_CASE_SENSITIVE;
}
-
+
if (flags & SL_FORCE_ACCESS_CHECK) {
TRACE("SL_FORCE_ACCESS_CHECK\n");
flags &= ~SL_FORCE_ACCESS_CHECK;
}
-
+
if (flags & SL_OPEN_PAGING_FILE) {
TRACE("SL_OPEN_PAGING_FILE\n");
flags &= ~SL_OPEN_PAGING_FILE;
}
-
+
if (flags & SL_OPEN_TARGET_DIRECTORY) {
TRACE("SL_OPEN_TARGET_DIRECTORY\n");
flags &= ~SL_OPEN_TARGET_DIRECTORY;
}
-
+
if (flags & SL_STOP_ON_SYMLINK) {
TRACE("SL_STOP_ON_SYMLINK\n");
flags &= ~SL_STOP_ON_SYMLINK;
}
-
+
if (flags)
WARN("unknown flags: %x\n", flags);
} else {
TRACE("flags: (none)\n");
}
-
-// Vpb = DeviceObject->DeviceExtension;
-
-// TRACE("create called for something other than FS device object\n");
-
+
+ if (!IrpSp->FileObject) {
+ ERR("FileObject was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ if (IrpSp->FileObject->RelatedFileObject) {
+ fcb* relatedfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
+
+ if (relatedfcb && relatedfcb->Vcb != Vcb) {
+ WARN("RelatedFileObject was for different device\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+ }
+
// opening volume
- // FIXME - also check if RelatedFileObject is Vcb
if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
#ifdef DEBUG_FCB_REFCOUNTS
- LONG rc, oc;
+ LONG rc;
#endif
ccb* ccb;
-
+
TRACE("open operation for volume\n");
if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) {
Status = STATUS_NOT_A_DIRECTORY;
goto exit;
}
-
- if (Vcb->removing) {
- Status = STATUS_ACCESS_DENIED;
- goto exit;
- }
-
+
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
if (!ccb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
-
+
RtlZeroMemory(ccb, sizeof(*ccb));
-
+
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
- ccb->NodeSize = sizeof(ccb);
+ ccb->NodeSize = sizeof(*ccb);
ccb->disposition = RequestedDisposition;
ccb->options = RequestedOptions;
ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState,
IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode);
+ ccb->reserving = FALSE;
+ ccb->lxss = called_from_lxss();
#ifdef DEBUG_FCB_REFCOUNTS
rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
#endif
IrpSp->FileObject->FsContext = Vcb->volume_fcb;
IrpSp->FileObject->FsContext2 = ccb;
-
+
IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
if (!IrpSp->FileObject->Vpb)
IrpSp->FileObject->Vpb = DeviceObject->Vpb;
-
+
InterlockedIncrement(&Vcb->open_files);
Irp->IoStatus.Information = FILE_OPENED;
Status = STATUS_SUCCESS;
} else {
+ LIST_ENTRY rollback;
BOOL skip_lock;
-
+
+ InitializeListHead(&rollback);
+
TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
-
+
if (IrpSp->FileObject->RelatedFileObject)
TRACE("related file = %S\n", file_desc(IrpSp->FileObject->RelatedFileObject));
-
+
// Don't lock again if we're being called from within CcCopyRead etc.
skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
if (!skip_lock)
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-
- Status = open_file(DeviceObject, Irp, &rollback);
-
+
+ Status = open_file(DeviceObject, Vcb, Irp, &rollback);
+
if (!NT_SUCCESS(Status))
do_rollback(Vcb, &rollback);
else
- clear_rollback(Vcb, &rollback);
-
+ clear_rollback(&rollback);
+
if (!skip_lock)
ExReleaseResourceLite(&Vcb->tree_lock);
-
-// Status = STATUS_ACCESS_DENIED;
}
-
+
exit:
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT );
-// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-
+
TRACE("create returning %08x\n", Status);
-
+
if (locked)
ExReleaseResourceLite(&Vcb->load_lock);
-
- if (top_level)
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
return Status;