mirror of
https://github.com/reactos/reactos.git
synced 2025-06-02 16:01:58 +00:00
2422 lines
87 KiB
C
2422 lines
87 KiB
C
/* Copyright (c) Mark Harmstone 2016
|
|
*
|
|
* This file is part of WinBtrfs.
|
|
*
|
|
* WinBtrfs is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public Licence as published by
|
|
* the Free Software Foundation, either version 3 of the Licence, or
|
|
* (at your option) any later version.
|
|
*
|
|
* WinBtrfs is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public Licence for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public Licence
|
|
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include <sys/stat.h>
|
|
#include "btrfs_drv.h"
|
|
|
|
extern PDEVICE_OBJECT devobj;
|
|
|
|
BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r,
|
|
UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
|
|
DIR_ITEM* di;
|
|
KEY searchkey;
|
|
traverse_ptr tp, tp2, next_tp;
|
|
BOOL b;
|
|
NTSTATUS Status;
|
|
ULONG stringlen;
|
|
|
|
TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type);
|
|
|
|
searchkey.obj_id = parinode;
|
|
searchkey.obj_type = TYPE_DIR_ITEM;
|
|
searchkey.offset = crc32;
|
|
|
|
Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
|
|
if (!keycmp(&searchkey, &tp.item->key)) {
|
|
UINT32 size = tp.item->size;
|
|
|
|
// found by hash
|
|
|
|
if (tp.item->size < sizeof(DIR_ITEM)) {
|
|
WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", r->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
|
|
} else {
|
|
di = (DIR_ITEM*)tp.item->data;
|
|
|
|
while (size > 0) {
|
|
if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
|
|
WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
break;
|
|
}
|
|
|
|
size -= sizeof(DIR_ITEM) - sizeof(char);
|
|
size -= di->n;
|
|
size -= di->m;
|
|
|
|
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
|
|
} else {
|
|
WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
|
|
UNICODE_STRING us;
|
|
|
|
if (!utf16) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
return FALSE;
|
|
}
|
|
|
|
Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
|
|
} else {
|
|
us.Buffer = utf16;
|
|
us.Length = us.MaximumLength = (USHORT)stringlen;
|
|
|
|
if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
|
|
if (di->key.obj_type == TYPE_ROOT_ITEM) {
|
|
root* fcbroot = Vcb->roots;
|
|
while (fcbroot && fcbroot->id != di->key.obj_id)
|
|
fcbroot = fcbroot->next;
|
|
|
|
*subvol = fcbroot;
|
|
*inode = SUBVOL_ROOT_INODE;
|
|
*type = BTRFS_TYPE_DIRECTORY;
|
|
} else {
|
|
*subvol = r;
|
|
*inode = di->key.obj_id;
|
|
*type = di->type;
|
|
}
|
|
|
|
if (utf8) {
|
|
utf8->MaximumLength = di->n;
|
|
utf8->Length = utf8->MaximumLength;
|
|
utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
|
|
if (!utf8->Buffer) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf16);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(utf8->Buffer, di->name, di->n);
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf16);
|
|
|
|
TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ExFreePool(utf16);
|
|
}
|
|
|
|
di = (DIR_ITEM*)&di->name[di->n + di->m];
|
|
}
|
|
}
|
|
}
|
|
|
|
searchkey.obj_id = parinode;
|
|
searchkey.obj_type = TYPE_DIR_INDEX;
|
|
searchkey.offset = 2;
|
|
|
|
Status = find_item(Vcb, r, &tp2, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
free_traverse_ptr(&tp);
|
|
return FALSE;
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
tp = tp2;
|
|
|
|
TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
|
|
if (keycmp(&tp.item->key, &searchkey) == -1) {
|
|
if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
|
|
free_traverse_ptr(&tp);
|
|
tp = next_tp;
|
|
|
|
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
}
|
|
}
|
|
|
|
if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
|
|
free_traverse_ptr(&tp);
|
|
return FALSE;
|
|
}
|
|
|
|
b = TRUE;
|
|
do {
|
|
TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
di = (DIR_ITEM*)tp.item->data;
|
|
|
|
if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
|
|
WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
|
} else {
|
|
TRACE("%.*s\n", di->n, di->name);
|
|
|
|
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
|
|
} else {
|
|
WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
|
|
UNICODE_STRING us;
|
|
|
|
if (!utf16) {
|
|
ERR("out of memory\n");
|
|
|
|
free_traverse_ptr(&tp);
|
|
return FALSE;
|
|
}
|
|
|
|
Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
|
|
} else {
|
|
us.Buffer = utf16;
|
|
us.Length = us.MaximumLength = (USHORT)stringlen;
|
|
|
|
if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
|
|
if (di->key.obj_type == TYPE_ROOT_ITEM) {
|
|
root* fcbroot = Vcb->roots;
|
|
while (fcbroot && fcbroot->id != di->key.obj_id)
|
|
fcbroot = fcbroot->next;
|
|
|
|
*subvol = fcbroot;
|
|
*inode = SUBVOL_ROOT_INODE;
|
|
*type = BTRFS_TYPE_DIRECTORY;
|
|
} else {
|
|
*subvol = r;
|
|
*inode = di->key.obj_id;
|
|
*type = di->type;
|
|
}
|
|
TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
|
|
|
|
if (utf8) {
|
|
utf8->MaximumLength = di->n;
|
|
utf8->Length = utf8->MaximumLength;
|
|
utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
|
|
if (!utf8->Buffer) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf16);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(utf8->Buffer, di->name, di->n);
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf16);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ExFreePool(utf16);
|
|
}
|
|
}
|
|
|
|
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
|
|
|
|
if (b) {
|
|
free_traverse_ptr(&tp);
|
|
tp = next_tp;
|
|
|
|
b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
|
|
}
|
|
} while (b);
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
fcb* create_fcb() {
|
|
fcb* fcb;
|
|
|
|
fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
|
|
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->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
|
|
fcb->Header.NodeByteSize = sizeof(struct _fcb);
|
|
|
|
fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
|
|
if (!fcb->nonpaged) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(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;
|
|
|
|
FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
|
|
|
|
InitializeListHead(&fcb->children);
|
|
|
|
return fcb;
|
|
}
|
|
|
|
static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
|
|
UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
|
|
char* fn;
|
|
UINT32 crc32;
|
|
BOOL ret;
|
|
ULONG utf8len;
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
|
|
if (!fn) {
|
|
ERR("out of memory\n");
|
|
return FALSE;
|
|
}
|
|
|
|
Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(fn);
|
|
ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("%.*s\n", utf8len, fn);
|
|
|
|
crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
|
|
TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
|
|
|
|
ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, UINT32* hash, PANSI_STRING xattr) {
|
|
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);
|
|
|
|
searchkey.obj_id = fcb->inode;
|
|
searchkey.obj_type = TYPE_XATTR_ITEM;
|
|
searchkey.offset = crc32;
|
|
|
|
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
|
|
if (!keycmp(&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);
|
|
|
|
*size = di->m;
|
|
*hash = tp.item->key.offset;
|
|
|
|
xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
|
|
if (!xattr->Buffer) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
goto end;
|
|
}
|
|
|
|
xattr->Length = xattr->MaximumLength = di->n;
|
|
RtlCopyMemory(xattr->Buffer, di->name, di->n);
|
|
xattr->Buffer[di->n] = 0;
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
searchkey.offset = 0;
|
|
|
|
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
|
|
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");
|
|
free_traverse_ptr(&tp);
|
|
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;
|
|
*size = di->m;
|
|
*hash = tp.item->key.offset;
|
|
|
|
xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
|
|
if (!xattr->Buffer) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf16);
|
|
goto end;
|
|
}
|
|
|
|
xattr->Length = xattr->MaximumLength = di->n;
|
|
RtlCopyMemory(xattr->Buffer, di->name, di->n);
|
|
xattr->Buffer[di->n] = 0;
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
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);
|
|
if (b) {
|
|
free_traverse_ptr(&tp);
|
|
tp = next_tp;
|
|
|
|
if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
|
|
break;
|
|
}
|
|
} while (b);
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
end:
|
|
ExFreePool(utf8);
|
|
|
|
return success;
|
|
}
|
|
|
|
static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
|
|
ULONG len, i, j, np;
|
|
BOOL has_stream;
|
|
UNICODE_STRING* ps;
|
|
WCHAR* buf;
|
|
|
|
np = 1;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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++;
|
|
|
|
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--;
|
|
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--;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
|
|
LIST_ENTRY* le;
|
|
fcb *c, *deleted = NULL;
|
|
ULONG rc;
|
|
|
|
le = dir->children.Flink;
|
|
while (le != &dir->children) {
|
|
c = CONTAINING_RECORD(le, fcb, list_entry);
|
|
|
|
if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
|
|
if (c->deleted) {
|
|
deleted = c;
|
|
} else {
|
|
rc = InterlockedIncrement(&c->refcount);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", c, rc, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
|
|
#endif
|
|
return c;
|
|
}
|
|
}
|
|
|
|
le = le->Flink;
|
|
}
|
|
|
|
if (deleted) {
|
|
rc = InterlockedIncrement(&deleted->refcount);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", deleted, rc, deleted->full_filename.Length / sizeof(WCHAR), deleted->full_filename.Buffer);
|
|
#endif
|
|
}
|
|
|
|
return deleted;
|
|
}
|
|
|
|
// #ifdef DEBUG_FCB_REFCOUNTS
|
|
// static void print_fcbs(device_extension* Vcb) {
|
|
// fcb* fcb = Vcb->fcbs;
|
|
//
|
|
// while (fcb) {
|
|
// ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
|
|
//
|
|
// fcb = fcb->next;
|
|
// }
|
|
// }
|
|
// #endif
|
|
|
|
NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
|
|
fcb *dir, *sf, *sf2;
|
|
ULONG i, num_parts;
|
|
UNICODE_STRING fnus2;
|
|
UNICODE_STRING* parts = NULL;
|
|
BOOL has_stream;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
|
|
|
|
// #ifdef DEBUG_FCB_REFCOUNTS
|
|
// print_fcbs(Vcb);
|
|
// #endif
|
|
|
|
fnus2 = *fnus;
|
|
|
|
if (fnus2.Length < sizeof(WCHAR) && !relatedfcb) {
|
|
ERR("error - fnus was too short\n");
|
|
return STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (relatedfcb) {
|
|
dir = relatedfcb;
|
|
} else {
|
|
if (fnus2.Buffer[0] != '\\') {
|
|
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)) {
|
|
LONG rc;
|
|
|
|
*pfcb = Vcb->root_fcb;
|
|
rc = InterlockedIncrement(&Vcb->root_fcb->refcount);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, rc);
|
|
#endif
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
dir = Vcb->root_fcb;
|
|
|
|
fnus2.Buffer++;
|
|
fnus2.Length -= sizeof(WCHAR);
|
|
fnus2.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
if (dir->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
|
|
WARN("passed relatedfcb which isn't a directory (%.*S) (fnus = %.*S)\n",
|
|
relatedfcb->full_filename.Length / sizeof(WCHAR), relatedfcb->full_filename.Buffer, fnus->Length / sizeof(WCHAR), fnus->Buffer);
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
if (fnus->Length == 0) {
|
|
num_parts = 0;
|
|
} else {
|
|
Status = split_path(&fnus2, &parts, &num_parts, &has_stream);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("split_path returned %08x\n", Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// FIXME - handle refcounts(?)
|
|
sf = dir;
|
|
dir->refcount++;
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", dir, dir->refcount, dir->full_filename.Length / sizeof(WCHAR), dir->full_filename.Buffer);
|
|
#endif
|
|
|
|
if (parent) {
|
|
num_parts--;
|
|
|
|
if (has_stream) {
|
|
num_parts--;
|
|
has_stream = FALSE;
|
|
}
|
|
}
|
|
|
|
if (num_parts == 0) {
|
|
Status = STATUS_SUCCESS;
|
|
*pfcb = dir;
|
|
goto end2;
|
|
}
|
|
|
|
for (i = 0; i < num_parts; i++) {
|
|
BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
|
|
|
|
sf2 = search_fcb_children(sf, &parts[i]);
|
|
|
|
if (sf2 && sf2->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
|
|
WARN("passed path including file as subdirectory\n");
|
|
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto end;
|
|
}
|
|
|
|
if (!sf2) {
|
|
if (has_stream && i == num_parts - 1) {
|
|
UNICODE_STRING streamname;
|
|
ANSI_STRING xattr;
|
|
UINT32 streamsize, streamhash;
|
|
|
|
streamname.Buffer = NULL;
|
|
streamname.Length = streamname.MaximumLength = 0;
|
|
xattr.Buffer = NULL;
|
|
xattr.Length = xattr.MaximumLength = 0;
|
|
|
|
if (!find_stream(Vcb, sf, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
|
|
TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto end;
|
|
} else {
|
|
ULONG fnlen;
|
|
|
|
if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
|
|
RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
|
|
WARN("not allowing user.DOSATTRIB to be opened as stream\n");
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto end;
|
|
}
|
|
|
|
sf2 = create_fcb();
|
|
if (!sf2) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
sf2->Vcb = Vcb;
|
|
|
|
if (streamname.Buffer) // case has changed
|
|
sf2->filepart = streamname;
|
|
else {
|
|
sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
|
|
sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
|
|
if (!sf2->filepart.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(sf2);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
|
|
}
|
|
|
|
sf2->par = sf;
|
|
|
|
sf->refcount++;
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
|
|
#endif
|
|
|
|
sf2->subvol = sf->subvol;
|
|
sf2->inode = sf->inode;
|
|
sf2->type = sf->type;
|
|
sf2->ads = TRUE;
|
|
sf2->adssize = streamsize;
|
|
sf2->adshash = streamhash;
|
|
sf2->adsxattr = xattr;
|
|
|
|
TRACE("stream found: size = %x, hash = %08x\n", sf2->adssize, sf2->adshash);
|
|
|
|
if (Vcb->fcbs)
|
|
Vcb->fcbs->prev = sf2;
|
|
|
|
sf2->next = Vcb->fcbs;
|
|
Vcb->fcbs = sf2;
|
|
|
|
sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
|
|
|
|
if (sf != Vcb->root_fcb)
|
|
sf2->name_offset++;
|
|
|
|
fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
|
|
|
|
sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
|
|
if (!sf2->full_filename.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(sf2);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
|
|
RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
|
|
|
|
sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':';
|
|
|
|
RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
|
|
|
|
// FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
|
|
|
|
TRACE("found stream %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, sf->subvol);
|
|
|
|
InsertTailList(&sf->children, &sf2->list_entry);
|
|
}
|
|
} else {
|
|
root* subvol;
|
|
UINT64 inode;
|
|
UINT8 type;
|
|
ANSI_STRING utf8;
|
|
KEY searchkey;
|
|
traverse_ptr tp;
|
|
|
|
if (!find_file_in_dir(Vcb, &parts[i], sf->subvol, sf->inode, &subvol, &inode, &type, &utf8)) {
|
|
TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
|
|
|
|
Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto end;
|
|
} else if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
|
|
WARN("passed path including file as subdirectory\n");
|
|
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto end;
|
|
} else {
|
|
ULONG fnlen, strlen;
|
|
|
|
sf2 = create_fcb();
|
|
if (!sf2) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
sf2->Vcb = Vcb;
|
|
|
|
Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
|
|
free_fcb(sf2);
|
|
goto end;
|
|
} else {
|
|
sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
|
|
sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
|
|
if (!sf2->filepart.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(sf2);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
|
|
free_fcb(sf2);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
sf2->par = sf;
|
|
|
|
sf->refcount++;
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
|
|
#endif
|
|
|
|
sf2->subvol = subvol;
|
|
sf2->inode = inode;
|
|
sf2->type = type;
|
|
|
|
if (Vcb->fcbs)
|
|
Vcb->fcbs->prev = sf2;
|
|
|
|
sf2->next = Vcb->fcbs;
|
|
Vcb->fcbs = sf2;
|
|
|
|
sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
|
|
|
|
if (sf != Vcb->root_fcb)
|
|
sf2->name_offset++;
|
|
|
|
fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
|
|
|
|
sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
|
|
if (!sf2->full_filename.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(sf2);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
|
|
RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
|
|
|
|
if (sf != Vcb->root_fcb)
|
|
sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
|
|
|
|
RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
|
|
|
|
sf2->utf8 = utf8;
|
|
|
|
searchkey.obj_id = sf2->inode;
|
|
searchkey.obj_type = TYPE_INODE_ITEM;
|
|
searchkey.offset = 0xffffffffffffffff;
|
|
|
|
Status = find_item(sf2->Vcb, sf2->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
free_fcb(sf2);
|
|
goto end;
|
|
}
|
|
|
|
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
|
|
ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id);
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
free_fcb(sf2);
|
|
free_traverse_ptr(&tp);
|
|
goto end;
|
|
}
|
|
|
|
if (tp.item->size > 0)
|
|
RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
sf2->atts = get_file_attributes(Vcb, &sf2->inode_item, sf2->subvol, sf2->inode, sf2->type, sf2->filepart.Buffer[0] == '.', FALSE);
|
|
|
|
fcb_get_sd(sf2);
|
|
|
|
TRACE("found %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, subvol);
|
|
|
|
InsertTailList(&sf->children, &sf2->list_entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i == num_parts - 1)
|
|
break;
|
|
|
|
free_fcb(sf);
|
|
sf = sf2;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
*pfcb = sf2;
|
|
|
|
end:
|
|
free_fcb(sf);
|
|
|
|
end2:
|
|
if (parts)
|
|
ExFreePool(parts);
|
|
|
|
// #ifdef DEBUG_FCB_REFCOUNTS
|
|
// print_fcbs(Vcb);
|
|
// #endif
|
|
|
|
TRACE("returning %08x\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static NTSTATUS STDCALL attach_fcb_to_fileobject(device_extension* Vcb, fcb* fcb, PFILE_OBJECT FileObject) {
|
|
FileObject->FsContext = fcb;
|
|
// FileObject->FsContext2 = 0x0badc0de;//NULL;
|
|
|
|
// FIXME - cache stuff
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, fcb* parfcb, ULONG options, fcb** pfcb, LIST_ENTRY* rollback) {
|
|
NTSTATUS Status;
|
|
fcb* fcb;
|
|
ULONG utf8len;
|
|
char* utf8 = NULL;
|
|
UINT32 crc32;
|
|
UINT64 dirpos, inode;
|
|
KEY searchkey;
|
|
traverse_ptr tp;
|
|
INODE_ITEM *dirii, *ii;
|
|
UINT8 type;
|
|
ULONG disize;
|
|
DIR_ITEM *di, *di2;
|
|
LARGE_INTEGER time;
|
|
BTRFS_TIME now;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
ANSI_STRING utf8as;
|
|
ULONG defda;
|
|
LONG rc;
|
|
|
|
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
utf8 = ExAllocatePoolWithTag(PagedPool, 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)) {
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
utf8[utf8len] = 0;
|
|
|
|
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
|
|
|
|
dirpos = find_next_dir_index(Vcb, parfcb->subvol, parfcb->inode);
|
|
if (dirpos == 0) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos);
|
|
|
|
KeQuerySystemTime(&time);
|
|
win_time_to_unix(time, &now);
|
|
|
|
TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
|
|
parfcb->inode_item.st_size += utf8len * 2;
|
|
TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
|
|
parfcb->inode_item.transid = Vcb->superblock.generation;
|
|
parfcb->inode_item.sequence++;
|
|
parfcb->inode_item.st_ctime = now;
|
|
parfcb->inode_item.st_mtime = now;
|
|
|
|
searchkey.obj_id = parfcb->inode;
|
|
searchkey.obj_type = TYPE_INODE_ITEM;
|
|
searchkey.offset = 0;
|
|
|
|
Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
if (keycmp(&searchkey, &tp.item->key)) {
|
|
ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfcb->inode, parfcb->subvol->id);
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf8);
|
|
return STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
|
|
if (!dirii) {
|
|
ERR("out of memory\n");
|
|
free_traverse_ptr(&tp);
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(dirii, &parfcb->inode_item, sizeof(INODE_ITEM));
|
|
delete_tree_item(Vcb, &tp, rollback);
|
|
|
|
insert_tree_item(Vcb, parfcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
if (parfcb->subvol->lastinode == 0)
|
|
get_last_inode(Vcb, parfcb->subvol);
|
|
|
|
inode = parfcb->subvol->lastinode + 1;
|
|
|
|
type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
|
|
|
|
disize = sizeof(DIR_ITEM) - 1 + utf8len;
|
|
di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
|
|
if (!di) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
di->key.obj_id = inode;
|
|
di->key.obj_type = TYPE_INODE_ITEM;
|
|
di->key.offset = 0;
|
|
di->transid = Vcb->superblock.generation;
|
|
di->m = 0;
|
|
di->n = (UINT16)utf8len;
|
|
di->type = type;
|
|
RtlCopyMemory(di->name, utf8, utf8len);
|
|
|
|
insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback);
|
|
|
|
di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
|
|
if (!di2) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(di2, di, disize);
|
|
|
|
Status = add_dir_item(Vcb, parfcb->subvol, parfcb->inode, crc32, di2, disize, rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("add_dir_item returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
// FIXME - handle Irp->Overlay.AllocationSize
|
|
|
|
utf8as.Buffer = utf8;
|
|
utf8as.Length = utf8as.MaximumLength = utf8len;
|
|
|
|
Status = add_inode_ref(Vcb, parfcb->subvol, inode, parfcb->inode, dirpos, &utf8as, rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("add_inode_ref returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
// 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;
|
|
|
|
if (utf8[0] == '.')
|
|
defda |= FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
if (options & FILE_DIRECTORY_FILE) {
|
|
defda |= FILE_ATTRIBUTE_DIRECTORY;
|
|
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;
|
|
|
|
if (IrpSp->Parameters.Create.FileAttributes != defda) {
|
|
char val[64];
|
|
|
|
sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
|
|
|
|
Status = set_xattr(Vcb, parfcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("set_xattr returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
parfcb->subvol->lastinode++;
|
|
|
|
fcb = create_fcb();
|
|
if (!fcb) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
fcb->Vcb = Vcb;
|
|
|
|
RtlZeroMemory(&fcb->inode_item, sizeof(INODE_ITEM));
|
|
fcb->inode_item.generation = Vcb->superblock.generation;
|
|
fcb->inode_item.transid = Vcb->superblock.generation;
|
|
fcb->inode_item.st_size = 0;
|
|
fcb->inode_item.st_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 = parfcb ? (parfcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
|
|
fcb->inode_item.st_rdev = 0;
|
|
fcb->inode_item.flags = 0;
|
|
fcb->inode_item.sequence = 1;
|
|
fcb->inode_item.st_atime = now;
|
|
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
|
|
}
|
|
|
|
// inherit nodatacow flag from parent directory
|
|
if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
|
|
fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
|
|
|
|
if (type != BTRFS_TYPE_DIRECTORY)
|
|
fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
|
|
}
|
|
|
|
// fcb->Header.IsFastIoPossible = TRUE;
|
|
fcb->Header.AllocationSize.QuadPart = 0;
|
|
fcb->Header.FileSize.QuadPart = 0;
|
|
fcb->Header.ValidDataLength.QuadPart = 0;
|
|
|
|
fcb->atts = IrpSp->Parameters.Create.FileAttributes;
|
|
|
|
if (options & FILE_DELETE_ON_CLOSE)
|
|
fcb->delete_on_close = TRUE;
|
|
|
|
fcb->par = parfcb;
|
|
rc = InterlockedIncrement(&parfcb->refcount);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
|
|
#endif
|
|
fcb->subvol = parfcb->subvol;
|
|
fcb->inode = inode;
|
|
fcb->type = type;
|
|
|
|
fcb->utf8.MaximumLength = fcb->utf8.Length = utf8len;
|
|
fcb->utf8.Buffer = utf8;
|
|
|
|
Status = fcb_get_new_sd(fcb, IrpSp->Parameters.Create.SecurityContext->AccessState);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("fcb_get_new_sd returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
fcb->filepart = *fpus;
|
|
|
|
Status = set_xattr(Vcb, parfcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("set_xattr returned %08x\n", Status);
|
|
ExFreePool(utf8);
|
|
return Status;
|
|
}
|
|
|
|
fcb->full_filename.Length = parfcb->full_filename.Length + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fcb->filepart.Length;
|
|
fcb->full_filename.MaximumLength = fcb->full_filename.Length;
|
|
fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.Length, ALLOC_TAG);
|
|
if (!fcb->full_filename.Buffer) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
|
|
|
|
if (parfcb->full_filename.Length > sizeof(WCHAR))
|
|
fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = '\\';
|
|
|
|
RtlCopyMemory(&fcb->full_filename.Buffer[(parfcb->full_filename.Length / sizeof(WCHAR)) + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], fcb->filepart.Buffer, fcb->filepart.Length);
|
|
|
|
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
|
|
if (!ii) {
|
|
ERR("out of memory\n");
|
|
ExFreePool(utf8);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
|
|
insert_tree_item(Vcb, parfcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
|
|
|
|
*pfcb = fcb;
|
|
|
|
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
|
|
fcb->subvol->root_item.ctime = now;
|
|
|
|
InsertTailList(&fcb->par->children, &fcb->list_entry);
|
|
|
|
TRACE("created new file %.*S in subvol %llx, inode %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->subvol->id, fcb->inode);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
|
|
NTSTATUS Status;
|
|
fcb *fcb, *parfcb = NULL;
|
|
ULONG i, j;
|
|
ULONG utf8len;
|
|
ccb* ccb;
|
|
static WCHAR datasuf[] = {':','$','D','A','T','A',0};
|
|
UNICODE_STRING dsus, fpus, stream;
|
|
LONG oc;
|
|
|
|
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);
|
|
fpus.Buffer = NULL;
|
|
|
|
// FIXME - apparently you can open streams using RelatedFileObject. How can we test this?
|
|
|
|
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
|
|
Status = get_fcb(Vcb, &parfcb, fnus, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, TRUE);
|
|
ExReleaseResourceLite(&Vcb->fcb_lock);
|
|
if (!NT_SUCCESS(Status))
|
|
goto end;
|
|
|
|
if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto end;
|
|
}
|
|
|
|
if (parfcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
|
|
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.Buffer = ExAllocatePoolWithTag(PagedPool, 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);
|
|
|
|
RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
|
|
fpus.Buffer[j - i + 1] = 0;
|
|
|
|
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++) {
|
|
if (fpus.Buffer[i] == ':') {
|
|
stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
|
|
stream.Buffer = &fpus.Buffer[i+1];
|
|
fpus.Buffer[i] = 0;
|
|
fpus.Length = i * sizeof(WCHAR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stream.Length > 0) {
|
|
struct _fcb* newpar;
|
|
static char xapref[] = "user.";
|
|
ULONG xapreflen = strlen(xapref), fnlen;
|
|
LARGE_INTEGER time;
|
|
BTRFS_TIME now;
|
|
KEY searchkey;
|
|
traverse_ptr tp;
|
|
INODE_ITEM* ii;
|
|
LONG rc;
|
|
|
|
TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
|
|
TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
|
|
|
|
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
|
|
Status = get_fcb(Vcb, &newpar, &fpus, parfcb, FALSE);
|
|
ExReleaseResourceLite(&Vcb->fcb_lock);
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &newpar, rollback);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("file_create2 returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
} else if (!NT_SUCCESS(Status)) {
|
|
ERR("get_fcb returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
|
|
free_fcb(parfcb);
|
|
parfcb = newpar;
|
|
|
|
if (newpar->type != BTRFS_TYPE_FILE && newpar->type != BTRFS_TYPE_SYMLINK) {
|
|
WARN("parent not file or symlink\n");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto end;
|
|
}
|
|
|
|
if (options & FILE_DIRECTORY_FILE) {
|
|
WARN("tried to create directory as stream\n");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto end;
|
|
}
|
|
|
|
fcb = create_fcb();
|
|
if (!fcb) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
fcb->Vcb = Vcb;
|
|
|
|
// fcb->Header.IsFastIoPossible = TRUE;
|
|
fcb->Header.AllocationSize.QuadPart = 0;
|
|
fcb->Header.FileSize.QuadPart = 0;
|
|
fcb->Header.ValidDataLength.QuadPart = 0;
|
|
|
|
if (options & FILE_DELETE_ON_CLOSE)
|
|
fcb->delete_on_close = TRUE;
|
|
|
|
fcb->par = parfcb;
|
|
rc = InterlockedIncrement(&parfcb->refcount);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
|
|
#endif
|
|
fcb->subvol = parfcb->subvol;
|
|
fcb->inode = parfcb->inode;
|
|
fcb->type = parfcb->type;
|
|
|
|
fcb->ads = TRUE;
|
|
fcb->adssize = 0;
|
|
|
|
Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto end;
|
|
|
|
fcb->adsxattr.Length = utf8len + xapreflen;
|
|
fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
|
|
fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
|
|
if (!fcb->adsxattr.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(fcb);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
|
|
|
|
Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
|
|
if (!NT_SUCCESS(Status)) {
|
|
free_fcb(fcb);
|
|
goto end;
|
|
}
|
|
|
|
fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
|
|
|
|
TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
|
|
|
|
fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
|
|
TRACE("adshash = %08x\n", fcb->adshash);
|
|
|
|
fcb->name_offset = parfcb->full_filename.Length / sizeof(WCHAR);
|
|
if (parfcb != Vcb->root_fcb)
|
|
fcb->name_offset++;
|
|
|
|
fcb->filepart.MaximumLength = fcb->filepart.Length = stream.Length;
|
|
fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.MaximumLength, ALLOC_TAG);
|
|
if (!fcb->filepart.Buffer) {
|
|
ERR("out of memory\n");
|
|
free_fcb(fcb);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
RtlCopyMemory(fcb->filepart.Buffer, stream.Buffer, stream.Length);
|
|
|
|
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");
|
|
free_fcb(fcb);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
|
|
RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
|
|
|
|
fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = ':';
|
|
|
|
RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
|
|
TRACE("full_filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
|
|
|
|
InsertTailList(&fcb->par->children, &fcb->list_entry);
|
|
|
|
Status = set_xattr(Vcb, parfcb->subvol, parfcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("set_xattr returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto end;
|
|
}
|
|
|
|
KeQuerySystemTime(&time);
|
|
win_time_to_unix(time, &now);
|
|
|
|
parfcb->inode_item.transid = Vcb->superblock.generation;
|
|
parfcb->inode_item.sequence++;
|
|
parfcb->inode_item.st_ctime = now;
|
|
|
|
searchkey.obj_id = parfcb->inode;
|
|
searchkey.obj_type = TYPE_INODE_ITEM;
|
|
searchkey.offset = 0xffffffffffffffff;
|
|
|
|
Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
|
|
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
|
|
delete_tree_item(Vcb, &tp, rollback);
|
|
} else {
|
|
WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfcb->subvol->id);
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
|
|
if (!ii) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
RtlCopyMemory(ii, &parfcb->inode_item, sizeof(INODE_ITEM));
|
|
|
|
insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
|
|
|
|
parfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
|
|
parfcb->subvol->root_item.ctime = now;
|
|
|
|
ExFreePool(fpus.Buffer);
|
|
fpus.Buffer = NULL;
|
|
} else {
|
|
Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &fcb, rollback);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("file_create2 returned %08x\n", Status);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
|
|
|
|
if (Vcb->fcbs)
|
|
Vcb->fcbs->prev = fcb;
|
|
|
|
fcb->next = Vcb->fcbs;
|
|
Vcb->fcbs = fcb;
|
|
|
|
ExReleaseResourceLite(&Vcb->fcb_lock);
|
|
|
|
Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
|
|
|
|
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
|
|
if (!ccb) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
RtlZeroMemory(ccb, sizeof(*ccb));
|
|
ccb->NodeType = BTRFS_NODE_TYPE_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;
|
|
|
|
oc = InterlockedIncrement(&fcb->open_count);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
ERR("fcb %p: open_count now %i\n", fcb, oc);
|
|
#endif
|
|
|
|
FileObject->FsContext2 = ccb;
|
|
|
|
FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
|
|
|
|
TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
|
|
|
|
Status = consider_write(Vcb);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ULONG fnlen;
|
|
|
|
fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
|
|
|
|
if (fcb->par != Vcb->root_fcb)
|
|
fcb->name_offset++;
|
|
|
|
fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
|
|
|
|
fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
|
|
if (!fcb->full_filename.Buffer) {
|
|
ERR("out of memory\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
|
|
RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
|
|
|
|
if (fcb->par != Vcb->root_fcb)
|
|
fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
|
|
|
|
RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
|
|
|
|
FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
|
|
options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
|
|
FILE_ACTION_ADDED, NULL);
|
|
|
|
goto end2;
|
|
}
|
|
|
|
end:
|
|
if (fpus.Buffer)
|
|
ExFreePool(fpus.Buffer);
|
|
|
|
end2:
|
|
if (parfcb)
|
|
free_fcb(parfcb);
|
|
|
|
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;
|
|
}
|
|
|
|
if (options & FILE_WRITE_THROUGH) {
|
|
TRACE(" FILE_WRITE_THROUGH\n");
|
|
options &= ~FILE_WRITE_THROUGH;
|
|
}
|
|
|
|
if (options & FILE_SEQUENTIAL_ONLY) {
|
|
TRACE(" FILE_SEQUENTIAL_ONLY\n");
|
|
options &= ~FILE_SEQUENTIAL_ONLY;
|
|
}
|
|
|
|
if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
|
|
TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
|
|
options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
|
|
}
|
|
|
|
if (options & FILE_SYNCHRONOUS_IO_ALERT) {
|
|
TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
|
|
options &= ~FILE_SYNCHRONOUS_IO_ALERT;
|
|
}
|
|
|
|
if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
|
|
TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
|
|
options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
|
|
}
|
|
|
|
if (options & FILE_NON_DIRECTORY_FILE) {
|
|
TRACE(" FILE_NON_DIRECTORY_FILE\n");
|
|
options &= ~FILE_NON_DIRECTORY_FILE;
|
|
}
|
|
|
|
if (options & FILE_CREATE_TREE_CONNECTION) {
|
|
TRACE(" FILE_CREATE_TREE_CONNECTION\n");
|
|
options &= ~FILE_CREATE_TREE_CONNECTION;
|
|
}
|
|
|
|
if (options & FILE_COMPLETE_IF_OPLOCKED) {
|
|
TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
|
|
options &= ~FILE_COMPLETE_IF_OPLOCKED;
|
|
}
|
|
|
|
if (options & FILE_NO_EA_KNOWLEDGE) {
|
|
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;
|
|
}
|
|
|
|
if (options & FILE_RANDOM_ACCESS) {
|
|
TRACE(" FILE_RANDOM_ACCESS\n");
|
|
options &= ~FILE_RANDOM_ACCESS;
|
|
}
|
|
|
|
if (options & FILE_DELETE_ON_CLOSE) {
|
|
TRACE(" FILE_DELETE_ON_CLOSE\n");
|
|
options &= ~FILE_DELETE_ON_CLOSE;
|
|
}
|
|
|
|
if (options & FILE_OPEN_BY_FILE_ID) {
|
|
TRACE(" FILE_OPEN_BY_FILE_ID\n");
|
|
options &= ~FILE_OPEN_BY_FILE_ID;
|
|
}
|
|
|
|
if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
|
|
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;
|
|
}
|
|
|
|
#if NTDDI_VERSION >= NTDDI_WIN7
|
|
if (options & FILE_OPEN_REQUIRING_OPLOCK) {
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
if (options & FILE_RESERVE_OPFILTER) {
|
|
TRACE(" FILE_RESERVE_OPFILTER\n");
|
|
options &= ~FILE_RESERVE_OPFILTER;
|
|
}
|
|
|
|
if (options & FILE_OPEN_REPARSE_POINT) {
|
|
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 {
|
|
TRACE("requested options: (none)\n");
|
|
}
|
|
}
|
|
|
|
static NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) {
|
|
KEY searchkey;
|
|
traverse_ptr tp;
|
|
INODE_ITEM* newii;
|
|
NTSTATUS Status;
|
|
|
|
searchkey.obj_id = inode;
|
|
searchkey.obj_type = TYPE_INODE_ITEM;
|
|
searchkey.offset = 0xffffffffffffffff;
|
|
|
|
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
|
|
delete_tree_item(Vcb, &tp, rollback);
|
|
} else {
|
|
WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
|
|
}
|
|
|
|
free_traverse_ptr(&tp);
|
|
|
|
newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
|
|
if (!newii) {
|
|
ERR("out of memory\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
|
|
|
|
insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, 0, newii, sizeof(INODE_ITEM), NULL, rollback);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
|
|
PFILE_OBJECT FileObject;
|
|
ULONG RequestedDisposition;
|
|
ULONG options;
|
|
NTSTATUS Status;
|
|
fcb* fcb;
|
|
ccb* ccb;
|
|
device_extension* Vcb = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
ULONG access;
|
|
PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState;
|
|
LONG oc;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
|
|
options = Stack->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 exit;
|
|
}
|
|
|
|
FileObject = Stack->FileObject;
|
|
|
|
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;
|
|
}
|
|
|
|
TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
|
|
TRACE("FileObject = %p\n", FileObject);
|
|
|
|
if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
|
|
Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
goto exit;
|
|
}
|
|
|
|
// FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
|
|
|
|
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
|
|
Status = get_fcb(Vcb, &fcb, &FileObject->FileName, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, Stack->Flags & SL_OPEN_TARGET_DIRECTORY);
|
|
ExReleaseResourceLite(&Vcb->fcb_lock);
|
|
|
|
if (NT_SUCCESS(Status) && fcb->deleted) {
|
|
free_fcb(fcb);
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (RequestedDisposition == FILE_CREATE) {
|
|
TRACE("file %.*S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
|
|
TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
|
|
goto exit;
|
|
}
|
|
} else {
|
|
TRACE("get_fcb returned %08x\n", Status);
|
|
goto exit;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) { // file already exists
|
|
struct _fcb* sf;
|
|
|
|
if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
|
|
Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
|
|
RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
TRACE("deleted = %s\n", fcb->deleted ? "TRUE" : "FALSE");
|
|
|
|
sf = fcb;
|
|
while (sf) {
|
|
if (sf->delete_on_close) {
|
|
WARN("could not open as deletion pending\n");
|
|
Status = STATUS_DELETE_PENDING;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
sf = sf->par;
|
|
}
|
|
|
|
if (fcb->type == BTRFS_TYPE_SYMLINK && !(options & FILE_OPEN_REPARSE_POINT)) {
|
|
if (!follow_symlink(fcb, FileObject)) {
|
|
ERR("follow_symlink failed\n");
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
Status = STATUS_REPARSE;
|
|
Irp->IoStatus.Information = IO_REPARSE;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
if (!SeAccessCheck(fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
|
|
IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
|
|
WARN("SeAccessCheck failed, returning %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
if (fcb->open_count > 0) {
|
|
Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
WARN("IoCheckShareAccess failed, returning %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
} else {
|
|
IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access);
|
|
}
|
|
|
|
if (access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
|
|
if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForWrite)) {
|
|
Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
|
|
ULONG defda;
|
|
LIST_ENTRY changed_sector_list;
|
|
|
|
if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fcb->atts & FILE_ATTRIBUTE_READONLY) {
|
|
WARN("cannot overwrite readonly file\n");
|
|
Status = STATUS_ACCESS_DENIED;
|
|
free_fcb(fcb);
|
|
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;
|
|
// }
|
|
InitializeListHead(&changed_sector_list);
|
|
|
|
// FIXME - make sure not ADS!
|
|
Status = truncate_file(fcb, fcb->inode_item.st_size, rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("truncate_file returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
Status = update_inode_item(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("update_inode_item returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
|
|
|
|
if (RequestedDisposition == FILE_SUPERSEDE)
|
|
fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
|
|
else
|
|
fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
if (Stack->Parameters.Create.FileAttributes != defda) {
|
|
char val[64];
|
|
|
|
sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
|
|
|
|
Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("set_xattr returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
} else
|
|
delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
|
|
|
|
// FIXME - truncate streams
|
|
// FIXME - do we need to alter parent directory's times?
|
|
// FIXME - send notifications
|
|
|
|
Status = consider_write(fcb->Vcb);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("consider_write returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// fcb->Header.IsFastIoPossible = TRUE;
|
|
|
|
if (fcb->ads) {
|
|
fcb->Header.AllocationSize.QuadPart = fcb->adssize;
|
|
fcb->Header.FileSize.QuadPart = fcb->adssize;
|
|
fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
|
|
} else if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
|
|
fcb->Header.AllocationSize.QuadPart = 0;
|
|
fcb->Header.FileSize.QuadPart = 0;
|
|
fcb->Header.ValidDataLength.QuadPart = 0;
|
|
} else {
|
|
KEY searchkey;
|
|
traverse_ptr tp;
|
|
EXTENT_DATA* ed;
|
|
|
|
searchkey.obj_id = fcb->inode;
|
|
searchkey.obj_type = TYPE_EXTENT_DATA;
|
|
searchkey.offset = 0xffffffffffffffff;
|
|
|
|
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ERR("error - find_item returned %08x\n", Status);
|
|
free_fcb(fcb);
|
|
goto exit;
|
|
}
|
|
|
|
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
|
|
ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
|
|
free_traverse_ptr(&tp);
|
|
free_fcb(fcb);
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if (tp.item->size < sizeof(EXTENT_DATA)) {
|
|
ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
|
|
tp.item->size, sizeof(EXTENT_DATA));
|
|
free_traverse_ptr(&tp);
|
|
free_fcb(fcb);
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
ed = (EXTENT_DATA*)tp.item->data;
|
|
|
|
if (ed->type == EXTENT_TYPE_INLINE)
|
|
fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
|
|
else
|
|
fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
|
|
|
|
fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
|
|
fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
|
|
|
|
free_traverse_ptr(&tp);
|
|
}
|
|
|
|
if (options & FILE_NON_DIRECTORY_FILE && fcb->type == BTRFS_TYPE_DIRECTORY) {
|
|
free_fcb(fcb);
|
|
Status = STATUS_FILE_IS_A_DIRECTORY;
|
|
goto exit;
|
|
} else if (options & FILE_DIRECTORY_FILE && fcb->type != BTRFS_TYPE_DIRECTORY) {
|
|
TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, path = %.*S)\n", fcb->type, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
|
|
free_fcb(fcb);
|
|
Status = STATUS_NOT_A_DIRECTORY;
|
|
goto exit;
|
|
}
|
|
|
|
Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
|
|
|
|
if (options & FILE_DELETE_ON_CLOSE)
|
|
fcb->delete_on_close = TRUE;
|
|
|
|
ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
|
|
if (!ccb) {
|
|
ERR("out of memory\n");
|
|
free_fcb(fcb);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
RtlZeroMemory(ccb, sizeof(*ccb));
|
|
ccb->NodeType = BTRFS_NODE_TYPE_CCB;
|
|
ccb->NodeSize = sizeof(ccb);
|
|
ccb->disposition = RequestedDisposition;
|
|
ccb->options = options;
|
|
ccb->query_dir_offset = 0;
|
|
RtlInitUnicodeString(&ccb->query_string, NULL);
|
|
ccb->has_wildcard = FALSE;
|
|
ccb->specific_file = FALSE;
|
|
|
|
FileObject->FsContext2 = ccb;
|
|
|
|
FileObject->SectionObjectPointer = &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;
|
|
}
|
|
}
|
|
|
|
oc = InterlockedIncrement(&fcb->open_count);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
ERR("fcb %p: open_count now %i\n", fcb, oc);
|
|
#endif
|
|
} else {
|
|
Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
|
|
Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
|
|
|
|
// if (!NT_SUCCESS(Status))
|
|
// free_fcb(fcb);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
|
|
FileObject->Flags |= FO_CACHE_SUPPORTED;
|
|
|
|
exit:
|
|
if (NT_SUCCESS(Status)) {
|
|
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);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
device_extension* Vcb = NULL;
|
|
BOOL top_level;
|
|
LIST_ENTRY rollback;
|
|
|
|
TRACE("create (flags = %x)\n", Irp->Flags);
|
|
|
|
InitializeListHead(&rollback);
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
top_level = is_top_level(Irp);
|
|
|
|
/* return success if just called for FS device object */
|
|
if (DeviceObject == devobj) {
|
|
TRACE("create called for FS device object\n");
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
goto exit;
|
|
}
|
|
|
|
Vcb = DeviceObject->DeviceExtension;
|
|
ExAcquireResourceSharedLite(&Vcb->load_lock, 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");
|
|
|
|
// 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;
|
|
LONG rc, oc;
|
|
|
|
TRACE("open operation for volume\n");
|
|
|
|
if (RequestedDisposition != FILE_OPEN &&
|
|
RequestedDisposition != FILE_OPEN_IF)
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto exit;
|
|
}
|
|
|
|
if (RequestedOptions & FILE_DIRECTORY_FILE)
|
|
{
|
|
Status = STATUS_NOT_A_DIRECTORY;
|
|
goto exit;
|
|
}
|
|
|
|
rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
|
|
oc = InterlockedIncrement(&Vcb->volume_fcb->open_count);
|
|
#ifdef DEBUG_FCB_REFCOUNTS
|
|
WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);
|
|
WARN("fcb %p: open_count now %i (volume)\n", Vcb->volume_fcb, oc);
|
|
#endif
|
|
attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
|
|
// // NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
|
|
// // DeviceExt->VolumeFcb->RefCount++;
|
|
|
|
IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
BOOL exclusive;
|
|
ULONG disposition;
|
|
|
|
TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
|
|
|
|
if (IrpSp->FileObject->RelatedFileObject) {
|
|
fcb* relfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
|
|
|
|
if (relfcb)
|
|
TRACE("related file name = %.*S\n", relfcb->full_filename.Length / sizeof(WCHAR), relfcb->full_filename.Buffer);
|
|
}
|
|
|
|
disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
|
|
|
|
// We acquire the lock exclusively if there's the possibility we might be writing
|
|
exclusive = disposition != FILE_OPEN;
|
|
|
|
acquire_tree_lock(Vcb, exclusive);
|
|
|
|
// ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
|
|
// Status = NtfsCreateFile(DeviceObject,
|
|
// Irp);
|
|
Status = create_file(DeviceObject, Irp, &rollback);
|
|
// ExReleaseResourceLite(&Vpb->DirResource);
|
|
|
|
if (exclusive && !NT_SUCCESS(Status))
|
|
do_rollback(Vcb, &rollback);
|
|
else
|
|
clear_rollback(&rollback);
|
|
|
|
release_tree_lock(Vcb, exclusive);
|
|
|
|
// 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);
|
|
|
|
ExReleaseResourceLite(&Vcb->load_lock);
|
|
|
|
if (top_level)
|
|
IoSetTopLevelIrp(NULL);
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
}
|