xbox-kernel/private/ntos/udfx/fcbsup.c
2020-09-30 17:17:25 +02:00

460 lines
12 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
fcbsup.c
Abstract:
This module implements routines which provide support for file control
blocks.
--*/
#include "udfx.h"
NTSTATUS
UdfxCreateFcbFromFileEntry(
IN PUDF_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PLONGAD IcbExtent,
IN PUDF_FCB ParentFcb OPTIONAL,
IN POBJECT_STRING FileName OPTIONAL,
OUT PUDF_FCB *ReturnedFcb
)
/*++
Routine Description:
This routine reads the file entry descriptor at the supplied extent and
constructs a file control block that represents the file.
Arguments:
VolumeExtension - Specifies the volume to read the file entry descriptor
from.
Irp - Specifies an IRP that can be used for cache I/O.
IcbExtent - Specifies the extent of the file entry descriptor.
ParentFcb - Specifies the parent directory that contains the supplied file.
FileName - Specifies the name of the file.
ReturnedFcb - Specifies the buffer to receive the created file control
block.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
SIZE_T FcbSize;
PUDF_FCB Fcb;
PICBFILE IcbFile;
ULONG AllocationDescriptorType;
PLONGAD LongExtent;
PSHORTAD ShortExtent;
*ReturnedFcb = NULL;
Fcb = NULL;
IcbFile = NULL;
//
// Verify that the extent specifies the single supported partition.
//
if (IcbExtent->Start.Partition != 0) {
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Compute the size of the file control block.
//
FcbSize = sizeof(UDF_FCB) + ((FileName != NULL) ? FileName->Length : 0);
//
// Allocate and initialize the file control block.
//
Fcb = ExAllocatePoolWithTag(FcbSize, 'cFxU');
if (Fcb == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupAndExit;
}
RtlZeroMemory(Fcb, FcbSize);
InitializeListHead(&Fcb->ChildFcbList);
//
// Copy the file name into the file control block.
//
if (FileName != NULL) {
RtlCopyMemory(Fcb->FileName, FileName->Buffer, FileName->Length);
Fcb->FileNameLength = (UCHAR)FileName->Length;
}
//
// Map the file descriptor into the cache.
//
status = UdfxMapLogicalSector(VolumeExtension, Irp, IcbExtent->Start.Lbn,
(PVOID*)&IcbFile);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Verify that this is a file descriptor.
//
if (!UdfxVerifyDescriptor(&IcbFile->Destag, UDF_CD_SECTOR_SIZE,
DESTAG_ID_NSR_FILE, IcbExtent->Start.Lbn)) {
UdfxDbgPrint(("UDFX: file descriptor doesn't verify\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Verify that the size of the file descriptor's variable fields doesn't
// exceed the size of a sector. Avoid overflow problems by checking each
// field individually as well.
//
if ((IcbFile->EALength > UDF_CD_SECTOR_SIZE) ||
(IcbFile->AllocLength > UDF_CD_SECTOR_SIZE) ||
((FIELD_OFFSET(ICBFILE, EAs) + IcbFile->EALength + IcbFile->AllocLength) >
UDF_CD_SECTOR_SIZE)) {
UdfxDbgPrint(("UDFX: file descriptor has too large of variant structures\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Verify that this is a file type that we can support and mark the file
// control block accordingly.
//
if (IcbFile->Icbtag.FileType == ICBTAG_FILE_T_DIRECTORY) {
Fcb->Flags |= UDF_FCB_DIRECTORY;
} else if (IcbFile->Icbtag.FileType != ICBTAG_FILE_T_FILE) {
UdfxDbgPrint(("UDFX: file descriptor has unsupported file type\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Copy the file size to the file control block and verify that the length
// is limited to 32-bits. We only support a single allocation extent and
// that's limited to a single ULONG.
//
Fcb->FileSize.QuadPart = IcbFile->InfoLength;
if (Fcb->FileSize.HighPart != 0) {
UdfxDbgPrint(("UDFX: file descriptor has too large of file size\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Copy the modify timestamp to the file control block. We don't bother
// storing any of the other timestamps that are part of the file control
// block or stored as extended attributes.
//
Fcb->ModifyTime = IcbFile->ModifyTime;
//
// Verify that the strategy type is direct mode.
//
if (IcbFile->Icbtag.StratType != ICBTAG_STRAT_DIRECT) {
UdfxDbgPrint(("UDFX: file descriptor has unsupported ICB strategy\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Verify that we know how to process the allocation descriptor encoding.
//
AllocationDescriptorType = (IcbFile->Icbtag.Flags & ICBTAG_F_ALLOC_MASK);
if ((Fcb->FileSize.LowPart == 0) && (IcbFile->AllocLength == 0)) {
//
// If this is a zero length file, then there may not be any allocation
// descriptors for the file.
//
ASSERT(Fcb->AllocationSectorStart == 0);
ASSERT(Fcb->EmbeddedDataOffset == 0);
} else if (AllocationDescriptorType == ICBTAG_F_ALLOC_IMMEDIATE) {
//
// The size of the file should match the length of the allocation data.
//
if (Fcb->FileSize.LowPart != IcbFile->AllocLength) {
UdfxDbgPrint(("UDFX: file descriptor has invalid allocation length\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
Fcb->Flags |= UDF_FCB_EMBEDDED_DATA;
Fcb->AllocationSectorStart = IcbExtent->Start.Lbn;
Fcb->EmbeddedDataOffset = (USHORT)
(FIELD_OFFSET(ICBFILE, EAs) + IcbFile->EALength);
} else if (AllocationDescriptorType == ICBTAG_F_ALLOC_LONG) {
if (IcbFile->AllocLength != sizeof(LONGAD)) {
UdfxDbgPrint(("UDFX: file descriptor has invalid allocation length\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
LongExtent = (PLONGAD)((PUCHAR)IcbFile + FIELD_OFFSET(ICBFILE, EAs) +
IcbFile->EALength);
//
// Verify that the extent specifies the single supported partition.
//
if (LongExtent->Start.Partition != 0) {
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// The size of the file should match the length of the extent.
//
if (Fcb->FileSize.LowPart != LongExtent->Length.Length) {
UdfxDbgPrint(("UDFX: file descriptor has invalid extent length\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
Fcb->AllocationSectorStart = LongExtent->Start.Lbn;
ASSERT(Fcb->EmbeddedDataOffset == 0);
} else if (AllocationDescriptorType == ICBTAG_F_ALLOC_SHORT) {
if (IcbFile->AllocLength != sizeof(SHORTAD)) {
UdfxDbgPrint(("UDFX: file descriptor has invalid allocation length\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
ShortExtent = (PSHORTAD)((PUCHAR)IcbFile + FIELD_OFFSET(ICBFILE, EAs) +
IcbFile->EALength);
//
// The size of the file should match the length of the extent.
//
if (Fcb->FileSize.LowPart != ShortExtent->Length.Length) {
UdfxDbgPrint(("UDFX: file descriptor has invalid extent length\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
Fcb->AllocationSectorStart = ShortExtent->Start;
ASSERT(Fcb->EmbeddedDataOffset == 0);
} else {
UdfxDbgPrint(("UDFX: file descriptor has unsupported allocation type\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// A file control block starts with the single reference for the caller.
//
Fcb->ReferenceCount = 1;
//
// Increment the reference count for the parent file control block and
// attach it to this file control block.
//
if (ParentFcb != NULL) {
ParentFcb->ReferenceCount++;
Fcb->ParentFcb = ParentFcb;
InsertHeadList(&ParentFcb->ChildFcbList, &Fcb->SiblingFcbLink);
}
//
// The file control block is successfully filled in. Return the results to
// the caller.
//
*ReturnedFcb = Fcb;
Fcb = NULL;
status = STATUS_SUCCESS;
CleanupAndExit:
if (IcbFile != NULL) {
FscUnmapBuffer(IcbFile);
}
if (Fcb != NULL) {
ExFreePool(Fcb);
}
return status;
}
BOOLEAN
UdfxFindOpenChildFcb(
IN PUDF_FCB DirectoryFcb,
IN POBJECT_STRING FileName,
OUT PUDF_FCB *ReturnedFcb
)
/*++
Routine Description:
This routine searches the child file control block for the supplied
directory for a file that has the supplied file name.
Arguments:
DirectoryFcb - Specifies the file control block of the directory to search.
FileName - Specifies the name to search for in the directory.
ReturnedFcb - Specifies the buffer to receive the found file control block.
Return Value:
Returns TRUE if the file control block was found, else FALSE.
--*/
{
PLIST_ENTRY NextFcbLink;
PUDF_FCB Fcb;
OBJECT_STRING FcbFileName;
//
// Walk through the file control blocks actively in use by the volume and
// find a match.
//
NextFcbLink = DirectoryFcb->ChildFcbList.Flink;
while (NextFcbLink != &DirectoryFcb->ChildFcbList) {
Fcb = CONTAINING_RECORD(NextFcbLink, UDF_FCB, SiblingFcbLink);
FcbFileName.Length = Fcb->FileNameLength;
FcbFileName.Buffer = Fcb->FileName;
if ((FcbFileName.Length == FileName->Length) &&
RtlEqualObjectString(&FcbFileName, FileName, TRUE)) {
*ReturnedFcb = Fcb;
return TRUE;
}
NextFcbLink = Fcb->SiblingFcbLink.Flink;
}
*ReturnedFcb = NULL;
return FALSE;
}
VOID
UdfxDereferenceFcb(
IN PUDF_FCB Fcb
)
/*++
Routine Description:
This routine decrements the reference count on the supplied file control
block. If the reference count reaches zero, then the file control block is
deleted.
Arguments:
Fcb - Specifies the file control block to dereference.
Return Value:
None.
--*/
{
PUDF_FCB ParentFcb;
ASSERT(Fcb->ReferenceCount > 0);
do {
//
// Decrement the reference count and bail out if there are still
// outstanding references to the file control block.
//
if (--Fcb->ReferenceCount != 0) {
return;
}
//
// Verify that the child file control block list is empty if this is a
// directory.
//
if (UdfxIsFlagSet(Fcb->Flags, UDF_FCB_DIRECTORY)) {
ASSERT(IsListEmpty(&Fcb->ChildFcbList));
}
//
// Save off the parent file control block so that we can dereference it
// in a bit.
//
ParentFcb = Fcb->ParentFcb;
//
// Remove this file control block from the list of siblings.
//
if (ParentFcb != NULL) {
RemoveEntryList(&Fcb->SiblingFcbLink);
}
//
// Free the file control block.
//
ExFreePool(Fcb);
//
// Switch to the parent file control block and restart the loop to
// dereference this object.
//
Fcb = ParentFcb;
} while (Fcb != NULL);
}