460 lines
12 KiB
C
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);
|
|
}
|