mirror of
https://github.com/reactos/reactos.git
synced 2024-07-06 04:35:07 +00:00
606 lines
21 KiB
C
606 lines
21 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 "btrfs_drv.h"
|
||
|
|
||
|
BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject) {
|
||
|
NTSTATUS Status;
|
||
|
ULONG len, stringlen;
|
||
|
USHORT newlen;
|
||
|
OBJECT_NAME_INFORMATION* oni;
|
||
|
UINT8* data;
|
||
|
UINT32 i;
|
||
|
BOOL success = FALSE;
|
||
|
WCHAR* utf16;
|
||
|
UNICODE_STRING abspath;
|
||
|
|
||
|
if (fcb->inode_item.st_size == 0) {
|
||
|
WARN("can't follow symlink with no data\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Status = ObQueryNameString(FileObject->DeviceObject, NULL, 0, &len);
|
||
|
|
||
|
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
|
||
|
ERR("ObQueryNameString 1 returned %08x\n", Status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
oni = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
|
||
|
if (!oni) {
|
||
|
ERR("out of memory\n");
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = ObQueryNameString(FileObject->DeviceObject, oni, len, &len);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("ObQueryNameString 2 returned %08x\n", Status);
|
||
|
ExFreePool(oni);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
|
||
|
if (!data) {
|
||
|
ERR("out of memory\n");
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, 0, fcb->inode_item.st_size, NULL);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("read_file returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < fcb->inode_item.st_size; i++) {
|
||
|
if (data[i] == '/')
|
||
|
data[i] = '\\';
|
||
|
}
|
||
|
|
||
|
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)data, fcb->inode_item.st_size);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
|
||
|
if (!utf16) {
|
||
|
ERR("out of memory\n");
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, (char*)data, fcb->inode_item.st_size);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
|
||
|
ExFreePool(utf16);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (utf16[0] == '\\') { // absolute path
|
||
|
abspath.MaximumLength = abspath.Length = stringlen;
|
||
|
abspath.Buffer = utf16;
|
||
|
} else { // relative path
|
||
|
abspath.MaximumLength = fcb->par->full_filename.Length + sizeof(WCHAR) + stringlen;
|
||
|
abspath.Buffer = ExAllocatePoolWithTag(PagedPool, abspath.MaximumLength, ALLOC_TAG);
|
||
|
if (!abspath.Buffer) {
|
||
|
ERR("out of memory\n");
|
||
|
ExFreePool(utf16);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(abspath.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
|
||
|
abspath.Length = fcb->par->full_filename.Length;
|
||
|
|
||
|
if (fcb->par->par) {
|
||
|
abspath.Buffer[abspath.Length / sizeof(WCHAR)] = '\\';
|
||
|
abspath.Length += sizeof(WCHAR);
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(&abspath.Buffer[abspath.Length / sizeof(WCHAR)], utf16, stringlen);
|
||
|
abspath.Length += stringlen;
|
||
|
|
||
|
ExFreePool(utf16);
|
||
|
}
|
||
|
|
||
|
Status = FsRtlRemoveDotsFromPath(abspath.Buffer, abspath.Length, &newlen);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("FsRtlRemoveDotsFromPath returned %08x\n", Status);
|
||
|
ExFreePool(abspath.Buffer);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
abspath.Length = newlen;
|
||
|
|
||
|
TRACE("abspath = %.*S\n", abspath.Length / sizeof(WCHAR), abspath.Buffer);
|
||
|
|
||
|
TRACE("filename was %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
|
||
|
|
||
|
if (FileObject->FileName.MaximumLength < oni->Name.Length + abspath.Length) {
|
||
|
ExFreePool(FileObject->FileName.Buffer);
|
||
|
FileObject->FileName.MaximumLength = oni->Name.Length + abspath.Length;
|
||
|
FileObject->FileName.Buffer = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.MaximumLength, ALLOC_TAG);
|
||
|
|
||
|
if (!FileObject->FileName.Buffer) {
|
||
|
ERR("out of memory\n");
|
||
|
ExFreePool(abspath.Buffer);
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(FileObject->FileName.Buffer, oni->Name.Buffer, oni->Name.Length);
|
||
|
RtlCopyMemory(&FileObject->FileName.Buffer[oni->Name.Length / sizeof(WCHAR)], abspath.Buffer, abspath.Length);
|
||
|
FileObject->FileName.Length = oni->Name.Length + abspath.Length;
|
||
|
|
||
|
TRACE("filename now %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
|
||
|
|
||
|
ExFreePool(abspath.Buffer);
|
||
|
|
||
|
success = TRUE;
|
||
|
end:
|
||
|
ExFreePool(data);
|
||
|
ExFreePool(oni);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
|
||
|
USHORT subnamelen, printnamelen, i;
|
||
|
ULONG stringlen;
|
||
|
DWORD reqlen;
|
||
|
REPARSE_DATA_BUFFER* rdb = buffer;
|
||
|
fcb* fcb = FileObject->FsContext;
|
||
|
char* data;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
|
||
|
|
||
|
acquire_tree_lock(fcb->Vcb, FALSE);
|
||
|
|
||
|
if (fcb->type != BTRFS_TYPE_SYMLINK) {
|
||
|
Status = STATUS_NOT_A_REPARSE_POINT;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
|
||
|
if (!data) {
|
||
|
ERR("out of memory\n");
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
|
||
|
Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("read_file returned %08x\n", Status);
|
||
|
ExFreePool(data);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
|
||
|
ExFreePool(data);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
subnamelen = stringlen;
|
||
|
printnamelen = stringlen;
|
||
|
|
||
|
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
|
||
|
|
||
|
if (buflen < reqlen) {
|
||
|
Status = STATUS_BUFFER_OVERFLOW;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||
|
rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
|
||
|
rdb->Reserved = 0;
|
||
|
|
||
|
rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
||
|
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
|
||
|
rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
|
||
|
rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
|
||
|
rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
|
||
|
|
||
|
Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
|
||
|
stringlen, &stringlen, data, fcb->inode_item.st_size);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
|
||
|
ExFreePool(data);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
|
||
|
if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
|
||
|
rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
|
||
|
&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
|
||
|
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
||
|
|
||
|
*retlen = reqlen;
|
||
|
|
||
|
ExFreePool(data);
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
|
||
|
end:
|
||
|
release_tree_lock(fcb->Vcb, FALSE);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type, LIST_ENTRY* rollback) {
|
||
|
KEY searchkey;
|
||
|
UINT32 crc32;
|
||
|
traverse_ptr tp;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
|
||
|
|
||
|
searchkey.obj_id = parinode;
|
||
|
searchkey.obj_type = TYPE_DIR_ITEM;
|
||
|
searchkey.offset = crc32;
|
||
|
|
||
|
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - find_item returned %08x\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
if (!keycmp(&tp.item->key, &searchkey)) {
|
||
|
if (tp.item->size < sizeof(DIR_ITEM)) {
|
||
|
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
|
||
|
} else {
|
||
|
DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
|
||
|
BOOL found = FALSE;
|
||
|
ULONG len = tp.item->size;
|
||
|
|
||
|
if (!di) {
|
||
|
ERR("out of memory\n");
|
||
|
free_traverse_ptr(&tp);
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(di, tp.item->data, tp.item->size);
|
||
|
|
||
|
di2 = di;
|
||
|
do {
|
||
|
if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di2->m + di2->n) {
|
||
|
ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
|
||
|
di2->type = type;
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) {
|
||
|
len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n;
|
||
|
di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n];
|
||
|
} else
|
||
|
break;
|
||
|
} while (len > 0);
|
||
|
|
||
|
if (found) {
|
||
|
delete_tree_item(Vcb, &tp, rollback);
|
||
|
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL, rollback);
|
||
|
} else
|
||
|
ExFreePool(di);
|
||
|
}
|
||
|
} else {
|
||
|
WARN("search for DIR_ITEM by crc32 failed\n");
|
||
|
}
|
||
|
|
||
|
free_traverse_ptr(&tp);
|
||
|
|
||
|
searchkey.obj_id = parinode;
|
||
|
searchkey.obj_type = TYPE_DIR_INDEX;
|
||
|
searchkey.offset = index;
|
||
|
|
||
|
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - find_item returned %08x\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
if (!keycmp(&tp.item->key, &searchkey)) {
|
||
|
if (tp.item->size < sizeof(DIR_ITEM)) {
|
||
|
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
|
||
|
} else {
|
||
|
DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
|
||
|
DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
|
||
|
if (!di2) {
|
||
|
ERR("out of memory\n");
|
||
|
free_traverse_ptr(&tp);
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(di2, di, tp.item->size);
|
||
|
di2->type = type;
|
||
|
|
||
|
delete_tree_item(Vcb, &tp, rollback);
|
||
|
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL, rollback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free_traverse_ptr(&tp);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
||
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||
|
void* buffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
fcb* fcb;
|
||
|
ULONG tag, minlen;
|
||
|
UNICODE_STRING subname;
|
||
|
ANSI_STRING target;
|
||
|
REPARSE_DATA_BUFFER* rdb = buffer;
|
||
|
KEY searchkey;
|
||
|
traverse_ptr tp, next_tp;
|
||
|
BOOL b;
|
||
|
LARGE_INTEGER offset;
|
||
|
USHORT i;
|
||
|
LIST_ENTRY rollback;
|
||
|
|
||
|
// FIXME - send notification if this succeeds? The attributes will have changed.
|
||
|
|
||
|
TRACE("(%p, %p)\n", DeviceObject, Irp);
|
||
|
|
||
|
InitializeListHead(&rollback);
|
||
|
|
||
|
if (!FileObject) {
|
||
|
ERR("FileObject was NULL\n");
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
fcb = FileObject->FsContext;
|
||
|
|
||
|
ERR("filename: %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
|
||
|
|
||
|
acquire_tree_lock(fcb->Vcb, TRUE);
|
||
|
|
||
|
if (fcb->type == BTRFS_TYPE_SYMLINK) {
|
||
|
WARN("tried to set a reparse point on an existing symlink\n");
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (!fcb->utf8.Buffer) {
|
||
|
ERR("error - utf8 on FCB not set\n");
|
||
|
Status = STATUS_INTERNAL_ERROR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
// FIXME - die if not file
|
||
|
// FIXME - die if ADS
|
||
|
|
||
|
if (buflen < sizeof(ULONG)) {
|
||
|
WARN("buffer was not long enough to hold tag\n");
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(&tag, buffer, sizeof(ULONG));
|
||
|
|
||
|
if (tag != IO_REPARSE_TAG_SYMLINK) {
|
||
|
FIXME("FIXME: handle arbitrary reparse tags (%08x)\n", tag);
|
||
|
Status = STATUS_NOT_IMPLEMENTED;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
|
||
|
if (buflen < minlen) {
|
||
|
WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (!(rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) {
|
||
|
FIXME("FIXME: support non-relative symlinks\n");
|
||
|
Status = STATUS_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
|
||
|
subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||
|
|
||
|
ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
|
||
|
|
||
|
fcb->type = BTRFS_TYPE_SYMLINK;
|
||
|
|
||
|
searchkey.obj_id = fcb->inode;
|
||
|
searchkey.obj_type = TYPE_INODE_REF;
|
||
|
searchkey.offset = 0;
|
||
|
|
||
|
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - find_item returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) {
|
||
|
if (tp.item->size < sizeof(INODE_REF)) {
|
||
|
WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
|
||
|
} else {
|
||
|
INODE_REF* ir;
|
||
|
ULONG size = tp.item->size;
|
||
|
ANSI_STRING utf8;
|
||
|
|
||
|
ir = (INODE_REF*)tp.item->data;
|
||
|
|
||
|
do {
|
||
|
if (size < sizeof(INODE_REF) || size < sizeof(INODE_REF) - 1 + ir->n) {
|
||
|
WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
utf8.Buffer = ir->name;
|
||
|
utf8.Length = utf8.MaximumLength = ir->n;
|
||
|
|
||
|
Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - change_file_type returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (size > sizeof(INODE_REF) - 1 + ir->n) {
|
||
|
size -= sizeof(INODE_REF) - 1 + ir->n;
|
||
|
|
||
|
ir = (INODE_REF*)&ir->name[ir->n];
|
||
|
} else
|
||
|
break;
|
||
|
} while (TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
|
||
|
if (b) {
|
||
|
free_traverse_ptr(&tp);
|
||
|
tp = next_tp;
|
||
|
|
||
|
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
|
||
|
}
|
||
|
} while (b);
|
||
|
|
||
|
free_traverse_ptr(&tp);
|
||
|
|
||
|
if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
|
||
|
searchkey.obj_id = fcb->inode;
|
||
|
searchkey.obj_type = TYPE_INODE_EXTREF;
|
||
|
searchkey.offset = 0;
|
||
|
|
||
|
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - find_item returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
|
||
|
if (tp.item->size < sizeof(INODE_EXTREF)) {
|
||
|
WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_EXTREF));
|
||
|
} else {
|
||
|
INODE_EXTREF* ier;
|
||
|
ULONG size = tp.item->size;
|
||
|
ANSI_STRING utf8;
|
||
|
|
||
|
ier = (INODE_EXTREF*)tp.item->data;
|
||
|
|
||
|
do {
|
||
|
if (size < sizeof(INODE_EXTREF) || size < sizeof(INODE_EXTREF) - 1 + ier->n) {
|
||
|
WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
utf8.Buffer = ier->name;
|
||
|
utf8.Length = utf8.MaximumLength = ier->n;
|
||
|
|
||
|
Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("error - change_file_type returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
|
||
|
size -= sizeof(INODE_EXTREF) - 1 + ier->n;
|
||
|
|
||
|
ier = (INODE_EXTREF*)&ier->name[ier->n];
|
||
|
} else
|
||
|
break;
|
||
|
} while (TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
|
||
|
if (b) {
|
||
|
free_traverse_ptr(&tp);
|
||
|
tp = next_tp;
|
||
|
|
||
|
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
|
||
|
}
|
||
|
} while (b);
|
||
|
|
||
|
free_traverse_ptr(&tp);
|
||
|
}
|
||
|
|
||
|
fcb->inode_item.st_mode |= __S_IFLNK;
|
||
|
|
||
|
Status = truncate_file(fcb, 0, &rollback);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("truncate_file returned %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
target.MaximumLength = target.Length;
|
||
|
target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
|
||
|
if (!target.Buffer) {
|
||
|
ERR("out of memory\n");
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < target.Length; i++) {
|
||
|
if (target.Buffer[i] == '\\')
|
||
|
target.Buffer[i] = '/';
|
||
|
}
|
||
|
|
||
|
offset.QuadPart = 0;
|
||
|
Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
|
||
|
|
||
|
ExFreePool(target.Buffer);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
Status = consider_write(fcb->Vcb);
|
||
|
|
||
|
end:
|
||
|
if (NT_SUCCESS(Status))
|
||
|
clear_rollback(&rollback);
|
||
|
else
|
||
|
do_rollback(fcb->Vcb, &rollback);
|
||
|
|
||
|
release_tree_lock(fcb->Vcb, TRUE);
|
||
|
|
||
|
return Status;
|
||
|
}
|