2786 lines
98 KiB
C
2786 lines
98 KiB
C
/*++
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
StrucSup.c
|
||
|
||
Abstract:
|
||
This module implements the Udfs in-memory data structure manipulation routines
|
||
|
||
Author:
|
||
Dan Lovinger [DanLo] 19-Jun-1996
|
||
--*/
|
||
|
||
#include "UdfProcs.h"
|
||
|
||
// The Bug check file id for this module
|
||
#define BugCheckFileId (UDFS_BUG_CHECK_STRUCSUP)
|
||
|
||
// The local debug trace level
|
||
#define Dbg (UDFS_DEBUG_LEVEL_STRUCSUP)
|
||
|
||
// Local structures
|
||
typedef struct _FCB_TABLE_ELEMENT {
|
||
FILE_ID FileId;
|
||
PFCB Fcb;
|
||
} FCB_TABLE_ELEMENT, *PFCB_TABLE_ELEMENT;
|
||
|
||
|
||
// Local macros
|
||
// PFCB UdfAllocateFcbData (IN PIRP_CONTEXT IrpContext);
|
||
// VOID UdfDeallocateFcbData (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb);
|
||
// PFCB UdfAllocateFcbIndex (IN PIRP_CONTEXT IrpContext);
|
||
// VOID UdfDeallocateFcbIndex (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb);
|
||
// PFCB_NONPAGED UdfAllocateFcbNonpaged (IN PIRP_CONTEXT IrpContext);
|
||
// VOID UdfDeallocateFcbNonpaged (IN PIRP_CONTEXT IrpContext, IN PFCB_NONPAGED FcbNonpaged);
|
||
// PCCB UdfAllocateCcb (IN PIRP_CONTEXT IrpContext);
|
||
// VOID UdfDeallocateCcb (IN PIRP_CONTEXT IrpContext, IN PCCB Ccb);
|
||
|
||
#define UdfAllocateFcbData(IC) ExAllocateFromPagedLookasideList( &UdfFcbDataLookasideList );
|
||
#define UdfDeallocateFcbData(IC,F) ExFreeToPagedLookasideList( &UdfFcbDataLookasideList, F );
|
||
#define UdfAllocateFcbIndex(IC) ExAllocateFromPagedLookasideList( &UdfFcbIndexLookasideList );
|
||
#define UdfDeallocateFcbIndex(IC,F) ExFreeToPagedLookasideList( &UdfFcbIndexLookasideList, F );
|
||
#define UdfAllocateFcbNonpaged(IC) ExAllocateFromNPagedLookasideList( &UdfFcbNonPagedLookasideList );
|
||
#define UdfDeallocateFcbNonpaged(IC,FNP) ExFreeToNPagedLookasideList( &UdfFcbNonPagedLookasideList, FNP );
|
||
#define UdfAllocateCcb(IC) ExAllocateFromPagedLookasideList( &UdfCcbLookasideList );
|
||
#define UdfDeallocateCcb(IC,C) ExFreeToPagedLookasideList( &UdfCcbLookasideList, C );
|
||
|
||
|
||
// VOID UdfInsertFcbTable (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb);
|
||
// VOID UdfDeleteFcbTable (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb);
|
||
|
||
|
||
#define UdfInsertFcbTable(IC,F) { \
|
||
FCB_TABLE_ELEMENT _Key; \
|
||
_Key.Fcb = (F); \
|
||
_Key.FileId = (F)->FileId; \
|
||
RtlInsertElementGenericTable( &(F)->Vcb->FcbTable, \
|
||
&_Key, \
|
||
sizeof( FCB_TABLE_ELEMENT ), \
|
||
NULL ); \
|
||
}
|
||
|
||
#define UdfDeleteFcbTable(IC,F) { \
|
||
FCB_TABLE_ELEMENT _Key; \
|
||
_Key.FileId = (F)->FileId; \
|
||
RtlDeleteElementGenericTable( &(F)->Vcb->FcbTable, &_Key ); \
|
||
}
|
||
|
||
|
||
// Discovers the partition the current allocation descriptor's referred extent
|
||
// is on, either explicitly throuigh the descriptor or implicitly through the
|
||
// mapped view.
|
||
|
||
|
||
INLINE USHORT UdfGetPartitionOfCurrentAllocation (IN PALLOC_ENUM_CONTEXT AllocContext)
|
||
{
|
||
if (AllocContext->AllocType == ICBTAG_F_ALLOC_LONG) {
|
||
return ((PLONGAD) AllocContext->Alloc)->Start.Partition;
|
||
} else {
|
||
return AllocContext->IcbContext->Active.Partition;
|
||
}
|
||
}
|
||
|
||
|
||
// Builds the Mcb in an Fcb. Use this after knowing that an Mcb is required
|
||
// for mapping information.
|
||
|
||
|
||
INLINE VOID UdfInitializeFcbMcb (IN PFCB Fcb)
|
||
{
|
||
// In certain rare situations, we may get called more than once.
|
||
// Just reset the allocations.
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_MCB_INITIALIZED )) {
|
||
FsRtlResetLargeMcb( &Fcb->Mcb, TRUE );
|
||
} else {
|
||
FsRtlInitializeLargeMcb( &Fcb->Mcb, UdfPagedPool );
|
||
SetFlag( Fcb->FcbState, FCB_STATE_MCB_INITIALIZED );
|
||
}
|
||
}
|
||
|
||
|
||
// Teardown an Fcb's Mcb as required.
|
||
|
||
|
||
INLINE VOID UdfUninitializeFcbMcb (IN PFCB Fcb)
|
||
{
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_MCB_INITIALIZED )) {
|
||
FsRtlUninitializeLargeMcb( &Fcb->Mcb );
|
||
ClearFlag( Fcb->FcbState, FCB_STATE_MCB_INITIALIZED );
|
||
}
|
||
}
|
||
|
||
|
||
// Local support routines
|
||
|
||
|
||
PVOID UdfAllocateTable (IN PRTL_GENERIC_TABLE Table, IN CLONG ByteSize);
|
||
PFCB_NONPAGED UdfCreateFcbNonPaged (IN PIRP_CONTEXT IrpContext);
|
||
VOID UdfDeleteFcbNonpaged (IN PIRP_CONTEXT IrpContext, IN PFCB_NONPAGED FcbNonpaged);
|
||
VOID UdfDeallocateTable (IN PRTL_GENERIC_TABLE Table, IN PVOID Buffer);
|
||
RTL_GENERIC_COMPARE_RESULTS UdfFcbTableCompare (IN PRTL_GENERIC_TABLE Table, IN PVOID id1, IN PVOID id2);
|
||
VOID UdfInitializeAllocationContext (IN PIRP_CONTEXT IrpContext,
|
||
IN PALLOC_ENUM_CONTEXT AllocContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext);
|
||
BOOLEAN UdfGetNextAllocation (IN PIRP_CONTEXT IrpContext, IN PALLOC_ENUM_CONTEXT AllocContext);
|
||
BOOLEAN UdfGetNextAllocationPostProcessing (IN PIRP_CONTEXT IrpContext, IN PALLOC_ENUM_CONTEXT AllocContext);
|
||
VOID UdfLookupActiveIcbInExtent (IN PIRP_CONTEXT IrpContext, IN PICB_SEARCH_CONTEXT IcbContext, IN ULONG Recurse);
|
||
VOID UdfInitializeEaContext (IN PIRP_CONTEXT IrpContext,
|
||
IN PEA_SEARCH_CONTEXT EaContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN ULONG EAType,
|
||
IN UCHAR EASubType);
|
||
BOOLEAN UdfLookupEa (IN PIRP_CONTEXT IrpContext, IN PEA_SEARCH_CONTEXT EaContext);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, UdfAllocateTable)
|
||
#pragma alloc_text(PAGE, UdfCleanupIcbContext)
|
||
#pragma alloc_text(PAGE, UdfCleanupIrpContext)
|
||
#pragma alloc_text(PAGE, UdfCreateCcb)
|
||
#pragma alloc_text(PAGE, UdfCreateFcb)
|
||
#pragma alloc_text(PAGE, UdfCreateFcbNonPaged)
|
||
#pragma alloc_text(PAGE, UdfCreateIrpContext)
|
||
#pragma alloc_text(PAGE, UdfDeallocateTable)
|
||
#pragma alloc_text(PAGE, UdfDeleteCcb)
|
||
#pragma alloc_text(PAGE, UdfDeleteFcb)
|
||
#pragma alloc_text(PAGE, UdfDeleteFcbNonpaged)
|
||
#pragma alloc_text(PAGE, UdfDeleteVcb)
|
||
#pragma alloc_text(PAGE, UdfFcbTableCompare)
|
||
#pragma alloc_text(PAGE, UdfFindInParseTable)
|
||
#pragma alloc_text(PAGE, UdfGetNextAllocation)
|
||
#pragma alloc_text(PAGE, UdfGetNextAllocationPostProcessing)
|
||
#pragma alloc_text(PAGE, UdfGetNextFcb)
|
||
#pragma alloc_text(PAGE, UdfInitializeAllocationContext)
|
||
#pragma alloc_text(PAGE, UdfInitializeAllocations)
|
||
#pragma alloc_text(PAGE, UdfInitializeEaContext)
|
||
#pragma alloc_text(PAGE, UdfInitializeFcbFromIcbContext)
|
||
#pragma alloc_text(PAGE, UdfInitializeIcbContext)
|
||
#pragma alloc_text(PAGE, UdfInitializeStackIrpContext)
|
||
#pragma alloc_text(PAGE, UdfInitializeVcb)
|
||
#pragma alloc_text(PAGE, UdfLookupActiveIcb)
|
||
#pragma alloc_text(PAGE, UdfLookupActiveIcbInExtent)
|
||
#pragma alloc_text(PAGE, UdfLookupEa)
|
||
#pragma alloc_text(PAGE, UdfLookupFcbTable)
|
||
#pragma alloc_text(PAGE, UdfTeardownStructures)
|
||
#pragma alloc_text(PAGE, UdfUpdateTimestampsFromIcbContext)
|
||
#pragma alloc_text(PAGE, UdfUpdateVcbPhase0)
|
||
#pragma alloc_text(PAGE, UdfUpdateVcbPhase1)
|
||
#pragma alloc_text(PAGE, UdfVerifyDescriptor)
|
||
#endif ALLOC_PRAGMA
|
||
|
||
|
||
BOOLEAN UdfInitializeVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PVCB Vcb,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PVPB Vpb,
|
||
IN PDISK_GEOMETRY DiskGeometry,
|
||
IN ULONG MediaChangeCount
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine initializes and inserts a new Vcb record into the in-memory
|
||
data structure. The Vcb record "hangs" off the end of the Volume device
|
||
object and must be allocated by our caller.
|
||
Arguments:
|
||
Vcb - Supplies the address of the Vcb record being initialized.
|
||
TargetDeviceObject - Supplies the address of the target device object to associate with the Vcb record.
|
||
Vpb - Supplies the address of the Vpb to associate with the Vcb record.
|
||
MediaChangeCount - Initial media change count of the target device
|
||
Return Value:
|
||
Boolean TRUE if the volume looks reasonable to continue mounting, FALSE
|
||
otherwise. This routine can raise on allocation failure.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// We start by first zeroing out all of the VCB, this will guarantee
|
||
// that any stale data is wiped clean.
|
||
RtlZeroMemory( Vcb, sizeof( VCB ));
|
||
|
||
// Set the proper node type code and node byte size.
|
||
Vcb->NodeTypeCode = UDFS_NTC_VCB;
|
||
Vcb->NodeByteSize = sizeof( VCB );
|
||
|
||
// Initialize the DirNotify structures. Do this first so there is
|
||
// no cleanup if it raises. Nothing else below will fail with a raise.
|
||
InitializeListHead( &Vcb->DirNotifyList );
|
||
FsRtlNotifyInitializeSync( &Vcb->NotifySync );
|
||
|
||
// Initialize the resource variable for the Vcb and files.
|
||
ExInitializeResource( &Vcb->VcbResource );
|
||
ExInitializeResource( &Vcb->FileResource );
|
||
ExInitializeFastMutex( &Vcb->VcbMutex );
|
||
|
||
// Insert this Vcb record on the UdfData.VcbQueue.
|
||
InsertHeadList( &UdfData.VcbQueue, &Vcb->VcbLinks );
|
||
|
||
// Set the Target Device Object and Vpb fields, referencing the target device.
|
||
ObReferenceObject( TargetDeviceObject );
|
||
Vcb->TargetDeviceObject = TargetDeviceObject;
|
||
Vcb->Vpb = Vpb;
|
||
|
||
// Set the removable media flag based on the real device's characteristics
|
||
if (FlagOn( Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA )) {
|
||
SetFlag( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA );
|
||
}
|
||
|
||
// Initialize the generic Fcb Table.
|
||
RtlInitializeGenericTable( &Vcb->FcbTable,
|
||
(PRTL_GENERIC_COMPARE_ROUTINE) UdfFcbTableCompare,
|
||
(PRTL_GENERIC_ALLOCATE_ROUTINE) UdfAllocateTable,
|
||
(PRTL_GENERIC_FREE_ROUTINE) UdfDeallocateTable,
|
||
NULL );
|
||
|
||
// Show that we have a mount in progress.
|
||
Vcb->VcbCondition = VcbMountInProgress;
|
||
|
||
// Refererence the Vcb for two reasons. The first is a reference
|
||
// that prevents the Vcb from going away on the last close unless
|
||
// dismount has already occurred. The second is to make sure
|
||
// we don't go into the dismount path on any error during mount until we get to the Mount cleanup.
|
||
Vcb->VcbResidualReference = UDFS_BASE_RESIDUAL_REFERENCE;
|
||
Vcb->VcbResidualUserReference = UDFS_BASE_RESIDUAL_USER_REFERENCE;
|
||
|
||
Vcb->VcbReference = 1 + Vcb->VcbResidualReference;
|
||
|
||
// Set the sector size.
|
||
Vcb->SectorSize = DiskGeometry->BytesPerSector;
|
||
|
||
// Set the sector shift amount.
|
||
Vcb->SectorShift = UdfHighBit( DiskGeometry->BytesPerSector );
|
||
|
||
// Set the media change count on the device
|
||
Vcb->MediaChangeCount = MediaChangeCount;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID UdfUpdateVcbPhase0 (IN PIRP_CONTEXT IrpContext, IN OUT PVCB Vcb)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to perform the initial spinup of the volume so that
|
||
we can do reads into it. Primarily, this is required since virtual partitions
|
||
make us lift the remapping table, and the final sets of descriptors from the volume
|
||
can be off in these virtual partitions.
|
||
|
||
So, we need to get everything set up to read.
|
||
Arguments:
|
||
Vcb - Vcb for the volume being mounted. We have already set up and completed the Pcb.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
ICB_SEARCH_CONTEXT IcbContext;
|
||
LONGLONG FileId = 0;
|
||
PICBFILE VatIcb = NULL;
|
||
PREGID RegId;
|
||
ULONG ThisPass;
|
||
ULONG Psn;
|
||
ULONG Vsn;
|
||
ULONG Lbn;
|
||
ULONG SectorCount;
|
||
USHORT Reference;
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
BOOLEAN CleanupIcbContext = FALSE;
|
||
PBCB Bcb = NULL;
|
||
LARGE_INTEGER Offset;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check input.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_VCB( Vcb );
|
||
|
||
DebugTrace(( +1, Dbg, "UdfUpdateVcbPhase0, Vcb %08x\n", Vcb ));
|
||
|
||
try {
|
||
// Create the Metadata Fcb and refererence it and the Vcb.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->MetadataFcb = UdfCreateFcb( IrpContext, *((PFILE_ID) &FileId), UDFS_NTC_FCB_INDEX, NULL );
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, Vcb->MetadataFcb, 1, 1 );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
// The metadata stream is grown lazily as we reference disk structures.
|
||
Vcb->MetadataFcb->FileSize.QuadPart =
|
||
Vcb->MetadataFcb->ValidDataLength.QuadPart =
|
||
Vcb->MetadataFcb->AllocationSize.QuadPart = 0;
|
||
|
||
// Initialize the volume Vmcb
|
||
UdfLockFcb( IrpContext, Vcb->MetadataFcb );
|
||
UdfInitializeVmcb( &Vcb->Vmcb, UdfPagedPool, MAXULONG, SectorSize(Vcb) );
|
||
UdfUnlockFcb( IrpContext, Vcb->MetadataFcb );
|
||
|
||
// Point to the file resource and set the flag that will cause mappings to go through the Vmcb
|
||
Vcb->MetadataFcb->Resource = &Vcb->FileResource;
|
||
SetFlag( Vcb->MetadataFcb->FcbState, FCB_STATE_VMCB_MAPPING | FCB_STATE_INITIALIZED );
|
||
|
||
// Create the stream file for this.
|
||
UdfCreateInternalStream( IrpContext, Vcb, Vcb->MetadataFcb );
|
||
|
||
// If this is a volume containing a virtual partition, set up the
|
||
// Virtual Allocation Table Fcb and adjust the residual reference counts comensurately.
|
||
if (FlagOn( Vcb->Pcb->Flags, PCB_FLAG_VIRTUAL_PARTITION )) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, handling VAT setup\n" ));
|
||
|
||
// Now if some dummy has stuck us in the situation of not giving us
|
||
// the tools to figure out where the end of the media is, tough luck.
|
||
|
||
if (!Vcb->BoundN || Vcb->BoundN < ANCHOR_SECTOR) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, no end bound was discoverable!\n" ));
|
||
UdfRaiseStatus( IrpContext, STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
|
||
// We take care of this first since the residuals must be in place
|
||
// if we raise while finding the VAT, else we will get horribly
|
||
// confused when the in-progress references are seen. We will think
|
||
// that the extra real referenes are indications that the volume can't be dismounted.
|
||
|
||
Vcb->VcbResidualReference += UDFS_CDUDF_RESIDUAL_REFERENCE;
|
||
Vcb->VcbResidualUserReference += UDFS_CDUDF_RESIDUAL_USER_REFERENCE;
|
||
Vcb->VcbReference += UDFS_CDUDF_RESIDUAL_REFERENCE;
|
||
|
||
// Now, we need to hunt about for the VAT ICB. This is defined, on
|
||
// closed media (meaning that the sessions have been finalized for use
|
||
// in CDROM drives), to be in the very last information sector on the
|
||
// media. Complicating this simple picture is that CDROMs tell us the
|
||
// "last sector" by telling us where the start of the leadout area is,
|
||
// not where the end of the informational sectors are. This is an
|
||
// important distinction because any combination of the following can
|
||
// be used in closing a CDROM session: 2 runout sectors, and/or 150
|
||
// sectors (2 seconds) of postgap, or nothing. Immediately after these
|
||
// "closing" writes is where the leadout begins.
|
||
|
||
// Runout is usually found on CD-E media and corresponds to the time it
|
||
// will take to turn the writing laser off. Postgap is what is used to
|
||
// generate audio pauses. It is easy to see that the kind of media and
|
||
// kind of mastering tool/system used will affect us here. There is no
|
||
// way to know either ahead of time.
|
||
|
||
// So, finally, these are the offsets from our previously discovered
|
||
// bounding information where we might find the last information sector:
|
||
|
||
// -152 runout + postgap
|
||
// -150 postgap
|
||
// -2 runout
|
||
// 0 nothing
|
||
|
||
// We must search these from low to high since it is extrememly expensive
|
||
// to guess wrong - CDROMs will sit there for tens of seconds trying to
|
||
// read unwritten/unreadable sectors. Hopefully we will find the VAT ICB beforehand.
|
||
|
||
// This should all be highly disturbing.
|
||
|
||
VatIcb = FsRtlAllocatePoolWithTag( UdfPagedPool,
|
||
UdfRawBufferSize( Vcb, BlockSize( Vcb )),
|
||
TAG_NSR_VDSD);
|
||
for (ThisPass = 0; ThisPass < 4; ThisPass++) {
|
||
// Lift the appropriate sector. The discerning reader will be confused that
|
||
// this is done in sector terms, not block. So is the implementor.
|
||
|
||
Psn = Vcb->BoundN - ( ThisPass == 0? 152 : ( ThisPass == 1? 150 : ( ThisPass == 2? 2 : 0 )));
|
||
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, looking at Psn 0x%08x\n", Psn ));
|
||
|
||
// Now, try to figure out what physical partition this sector lives in so
|
||
// that we can eventually establish workable metadata mappings to it and
|
||
// dereference short allocation descriptors it may use.
|
||
|
||
for (Reference = 0; Reference < Vcb->Pcb->Partitions; Reference++) {
|
||
if (Vcb->Pcb->Partition[Reference].Type == Physical &&
|
||
Vcb->Pcb->Partition[Reference].Physical.Start <= Psn &&
|
||
Vcb->Pcb->Partition[Reference].Physical.Start +
|
||
Vcb->Pcb->Partition[Reference].Physical.Length > Psn) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// If this sector is not contained in a partition, we do not need to look at it.
|
||
if (Reference == Vcb->Pcb->Partitions) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... but it isn't in a partition.\n" ));
|
||
continue;
|
||
}
|
||
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... in partition Ref %u.\n", Reference ));
|
||
|
||
// We must locate the Lbn of this Psn by figuring out the offset of it
|
||
// in the partition we already know that it is recorded in.
|
||
|
||
Lbn = BlocksFromSectors( Vcb, Psn - Vcb->Pcb->Partition[Reference].Physical.Start );
|
||
|
||
if (!NT_SUCCESS( UdfReadSectors( IrpContext,
|
||
LlBytesFromSectors( Vcb, Psn ),
|
||
UdfRawReadSize( Vcb, BlockSize( Vcb )),
|
||
TRUE,
|
||
VatIcb,
|
||
Vcb->TargetDeviceObject ))) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... but couldn't read it.\n" ));
|
||
continue;
|
||
}
|
||
|
||
// First make sure this looks vageuly like a file entry.
|
||
if (!UdfVerifyDescriptor( IrpContext,
|
||
(PDESTAG) VatIcb,
|
||
DESTAG_ID_NSR_FILE,
|
||
BlockSize( Vcb ),
|
||
Lbn,
|
||
TRUE )) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... but it didn't verify.\n" ));
|
||
continue;
|
||
}
|
||
|
||
// Make sure this is a NOTSPEC object. We can also presume that a VAT isn't
|
||
// linked into any directory, so it would be surprising if the link count was nonzero.
|
||
if (VatIcb->Icbtag.FileType != ICBTAG_FILE_T_NOTSPEC || VatIcb->LinkCount) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... but the type/linkcount is wrong.\n" ));
|
||
continue;
|
||
}
|
||
|
||
// The VAT must be at least large enough to contain the required information and
|
||
// be a multiple of 4byte elements in length. We also have defined a sanity upper
|
||
// bound beyond which we never expect to see a VAT go.
|
||
|
||
ASSERT( !LongOffset( UDF_CDUDF_MINIMUM_VAT_SIZE ));
|
||
|
||
if (VatIcb->InfoLength < UDF_CDUDF_MINIMUM_VAT_SIZE ||
|
||
VatIcb->InfoLength > UDF_CDUDF_MAXIMUM_VAT_SIZE ||
|
||
LongOffset( VatIcb->InfoLength )) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... but the size looks pretty bogus.\n" ));
|
||
continue;
|
||
}
|
||
|
||
// At this point we have to take a wild guess that this will be the guy. Since the only
|
||
// way to be sure is to look at the very end of the file an look for the regid, we've got
|
||
// to go map this thing.
|
||
|
||
// This is pretty ugly, but we have to cobble this maybe-Icb into the metadata stream
|
||
// so that initialization/use is possible (embedded data!). Normally regular Icb searches
|
||
// would have done this for us, but since we have to go through such an amusing search
|
||
// procedure that isn't possible. So, add it as a single sector mapping.
|
||
|
||
// Since this lives in a partition, we can just do the "lookup" in the metadata stream.
|
||
// If we did not have this guarantee, we'd need to do a bit more of this by hand.
|
||
|
||
// As this is at mount time, we are very sure we are the only person messing with the
|
||
// metadata stream.
|
||
|
||
// Zap the previous mapping and invalidate the metadata and VAT stream content.
|
||
|
||
if (Vcb->VatFcb) {
|
||
UdfResetVmcb( &Vcb->Vmcb );
|
||
UdfUnpinData( IrpContext, &Bcb );
|
||
CcPurgeCacheSection( Vcb->MetadataFcb->FileObject->SectionObjectPointer,
|
||
NULL,
|
||
0,
|
||
FALSE );
|
||
|
||
CcPurgeCacheSection( Vcb->VatFcb->FileObject->SectionObjectPointer,
|
||
NULL,
|
||
0,
|
||
FALSE );
|
||
}
|
||
|
||
Vsn = UdfLookupMetaVsnOfExtent( IrpContext,
|
||
Vcb,
|
||
Reference,
|
||
Lbn,
|
||
BlockSize( Vcb ),
|
||
TRUE );
|
||
if (Vcb->VatFcb == NULL) {
|
||
// Now stamp out the Fcb.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->VatFcb = UdfCreateFcb( IrpContext, *((PFILE_ID) &FileId), UDFS_NTC_FCB_INDEX, NULL );
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, Vcb->VatFcb, 1, 1 );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
// Point to the file resource and set the flag that will cause mappings
|
||
// to go through the Vmcb
|
||
Vcb->VatFcb->Resource = &Vcb->FileResource;
|
||
}
|
||
|
||
// Now size and try to pick up all of the allocation descriptors for this guy.
|
||
// We're going to need to conjure an IcbContext for this.
|
||
|
||
Vcb->VatFcb->AllocationSize.QuadPart = LlSectorAlign( Vcb, VatIcb->InfoLength );
|
||
|
||
Vcb->VatFcb->FileSize.QuadPart =
|
||
Vcb->VatFcb->ValidDataLength.QuadPart = VatIcb->InfoLength;
|
||
|
||
// Clean out any previous failed attempts.
|
||
if (CleanupIcbContext) {
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
} else {
|
||
RtlZeroMemory( &IcbContext, sizeof( ICB_SEARCH_CONTEXT ));
|
||
}
|
||
|
||
// Now construct the ICB search context we would have had
|
||
// made in the process of normal ICB discovery. Since we
|
||
// were unable to do that, gotta do it by hand.
|
||
|
||
IcbContext.Active.View = (PVOID) VatIcb;
|
||
IcbContext.Active.Partition = Reference;
|
||
IcbContext.Active.Lbn = Lbn;
|
||
CleanupIcbContext = TRUE;
|
||
|
||
UdfInitializeAllocations( IrpContext, Vcb->VatFcb, &IcbContext );
|
||
|
||
// Create or resize the stream file for the VAT as appropriate.
|
||
if (!FlagOn( Vcb->VatFcb->FcbState, FCB_STATE_INITIALIZED )) {
|
||
UdfCreateInternalStream( IrpContext, Vcb, Vcb->VatFcb );
|
||
SetFlag( Vcb->VatFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
} else {
|
||
CcSetFileSizes( Vcb->VatFcb->FileObject, (PCC_FILE_SIZES) &Vcb->VatFcb->AllocationSize );
|
||
}
|
||
|
||
// To complete VAT discovery, we now look for the regid at the end of the stream
|
||
// that will definitively tell us that this is really a VAT. Bias from the back
|
||
// by the previous VAT pointer and the regid itself. We already know the stream
|
||
// is big enough by virtue of our preliminary sanity checks.
|
||
|
||
Offset.QuadPart = Vcb->VatFcb->FileSize.QuadPart - UDF_CDUDF_TRAILING_DATA_SIZE;
|
||
|
||
CcMapData( Vcb->VatFcb->FileObject, &Offset, sizeof(REGID), TRUE, &Bcb, &RegId );
|
||
|
||
if (!UdfUdfIdentifierContained( RegId,
|
||
&UdfVatTableIdentifier,
|
||
UDF_VERSION_150,
|
||
UDF_VERSION_RECOGNIZED,
|
||
OSCLASS_INVALID,
|
||
OSIDENTIFIER_INVALID )) {
|
||
// Oh well, no go here.
|
||
continue;
|
||
}
|
||
|
||
// Got it!
|
||
break;
|
||
}
|
||
|
||
// If we didn't find anything ...
|
||
if (ThisPass == 4) {
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... and so we didn't find a VAT!\n" ));
|
||
UdfRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
// Go find the virtual reference so we can further update the Pcb with information from the VAT.
|
||
for (Reference = 0; Reference < Vcb->Pcb->Partitions; Reference++) {
|
||
if (Vcb->Pcb->Partition[Reference].Type == Virtual) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
ASSERT( Reference < Vcb->Pcb->Partitions );
|
||
|
||
// We note the length so we can easily do bounds checking for virtual mappings.
|
||
Offset.QuadPart = (Vcb->VatFcb->FileSize.QuadPart - UDF_CDUDF_TRAILING_DATA_SIZE) / sizeof(ULONG);
|
||
|
||
ASSERT( Offset.HighPart == 0 );
|
||
Vcb->Pcb->Partition[Reference].Virtual.Length = Offset.LowPart;
|
||
|
||
DebugTrace(( 0, Dbg, "UdfUpdateVcbPhase0, ... got it!\n" ));
|
||
}
|
||
} finally {
|
||
DebugUnwind( "UdfUpdateVcbPhase0" );
|
||
|
||
UdfUnpinData( IrpContext, &Bcb );
|
||
if (CleanupIcbContext) { UdfCleanupIcbContext( IrpContext, &IcbContext ); }
|
||
if (UnlockVcb) { UdfUnlockVcb( IrpContext, Vcb ); }
|
||
if (VatIcb) { ExFreePool( VatIcb ); }
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfUpdateVcbPhase0 -> VOID\n" ));
|
||
}
|
||
|
||
|
||
VOID UdfUpdateVcbPhase1 (IN PIRP_CONTEXT IrpContext, IN OUT PVCB Vcb, IN PNSR_FSD Fsd)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to perform the final initialization of a Vcb and Vpb
|
||
from the volume descriptors on the disk.
|
||
Arguments:
|
||
Vcb - Vcb for the volume being mounted. We have already done phase 0.
|
||
Fsd - The fileset descriptor for this volume.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
ICB_SEARCH_CONTEXT IcbContext;
|
||
LONGLONG FileId = 0;
|
||
PFCB Fcb;
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
BOOLEAN UnlockFcb = FALSE;
|
||
BOOLEAN CleanupIcbContext = FALSE;
|
||
ULONG Reference;
|
||
ULONG BoundSector = 0;
|
||
PAGED_CODE();
|
||
|
||
// Check input.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_VCB( Vcb );
|
||
|
||
DebugTrace(( +1, Dbg, "UdfUpdateVcbPhase1, Vcb %08x Fsd %08x\n", Vcb, Fsd ));
|
||
|
||
// Use a try-finally to facilitate cleanup.
|
||
try {
|
||
// Do the final internal Fcb's and other Vcb fields.
|
||
|
||
// Create the root index and reference it in the Vcb.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->RootIndexFcb = UdfCreateFcb( IrpContext, *((PFILE_ID) &FileId), UDFS_NTC_FCB_INDEX, NULL );
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, Vcb->RootIndexFcb, 1, 1 );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
// Create the File id by hand for this Fcb.
|
||
|
||
UdfSetFidFromLbAddr( Vcb->RootIndexFcb->FileId, Fsd->IcbRoot.Start );
|
||
UdfSetFidDirectory( Vcb->RootIndexFcb->FileId );
|
||
Vcb->RootIndexFcb->RootExtentLength = Fsd->IcbRoot.Length.Length;
|
||
|
||
// Get the direct entry for the root directory and initialize the Fcb from it.
|
||
UdfInitializeIcbContextFromFcb( IrpContext, &IcbContext, Vcb->RootIndexFcb );
|
||
CleanupIcbContext = TRUE;
|
||
|
||
UdfLookupActiveIcb( IrpContext, &IcbContext );
|
||
|
||
UdfInitializeFcbFromIcbContext( IrpContext, Vcb->RootIndexFcb, &IcbContext );
|
||
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
CleanupIcbContext = FALSE;
|
||
|
||
// Create the stream file for the root directory.
|
||
UdfCreateInternalStream( IrpContext, Vcb, Vcb->RootIndexFcb );
|
||
|
||
// Now do the volume dasd Fcb. Create this and reference it in the Vcb.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->VolumeDasdFcb = UdfCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
UDFS_NTC_FCB_DATA,
|
||
NULL );
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, Vcb->VolumeDasdFcb, 1, 1 );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
Fcb = Vcb->VolumeDasdFcb;
|
||
UdfLockFcb( IrpContext, Fcb );
|
||
UnlockFcb = TRUE;
|
||
|
||
// If we were unable to determine a last sector on the media, walk the Pcb and guess
|
||
// that it is probably OK to think of the last sector of the last partition as The
|
||
// Last Sector. Note that we couldn't do this before since the notion of a last
|
||
// sector has significance at mount time, if it had been possible to find one.
|
||
|
||
for ( Reference = 0; Reference < Vcb->Pcb->Partitions; Reference++ ) {
|
||
if (Vcb->Pcb->Partition[Reference].Type == Physical &&
|
||
Vcb->Pcb->Partition[Reference].Physical.Start +
|
||
Vcb->Pcb->Partition[Reference].Physical.Length > BoundSector) {
|
||
BoundSector = Vcb->Pcb->Partition[Reference].Physical.Start +
|
||
Vcb->Pcb->Partition[Reference].Physical.Length;
|
||
}
|
||
}
|
||
|
||
// Note that we cannot restrict the bound by the "physical" bound discovered
|
||
// eariler. This is because the MSF format of the TOC request we send is only
|
||
// capable of representing about 2.3gb, and a lot of media we will be on that
|
||
// responds to TOCs will be quite a bit larger - ex: DVD.
|
||
|
||
// This, of course, barring proper means of discovering media bounding, prohibits
|
||
// the possibility of having UDF virtual partitions on DVD-R.
|
||
|
||
// Build the mapping from [0, Bound). We have to initialize the Mcb by hand since
|
||
// this is usually left to when we lift retrieval information from an Icb in UdfInitializeAllocations.
|
||
|
||
UdfInitializeFcbMcb( Fcb );
|
||
|
||
FsRtlAddLargeMcbEntry( &Fcb->Mcb, (LONGLONG) 0, (LONGLONG) 0, (LONGLONG) BoundSector );
|
||
|
||
Fcb->FileSize.QuadPart += LlBytesFromSectors( Vcb, BoundSector );
|
||
Fcb->AllocationSize.QuadPart =
|
||
Fcb->ValidDataLength.QuadPart = Fcb->FileSize.QuadPart;
|
||
|
||
UdfUnlockFcb( IrpContext, Fcb );
|
||
UnlockFcb = FALSE;
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
// Point to the file resource.
|
||
Vcb->VolumeDasdFcb->Resource = &Vcb->FileResource;
|
||
Vcb->VolumeDasdFcb->FileAttributes = FILE_ATTRIBUTE_READONLY;
|
||
} finally {
|
||
DebugUnwind( "UdfUpdateVcbPhase1" );
|
||
|
||
if (CleanupIcbContext) { UdfCleanupIcbContext( IrpContext, &IcbContext ); }
|
||
if (UnlockFcb) { UdfUnlockFcb( IrpContext, Fcb ); }
|
||
if (UnlockVcb) { UdfUnlockVcb( IrpContext, Vcb ); }
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfUpdateVcbPhase1 -> VOID\n" ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfDeleteVcb (IN PIRP_CONTEXT IrpContext, IN OUT PVCB Vcb)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to delete a Vcb which failed mount or has been
|
||
dismounted. The dismount code should have already removed all of the
|
||
open Fcb's. We do nothing here but clean up other auxilary structures.
|
||
Arguments:
|
||
Vcb - Vcb to delete.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERT_EXCLUSIVE_UDFDATA;
|
||
ASSERT_EXCLUSIVE_VCB( Vcb );
|
||
|
||
// If there is a Vpb then we must delete it ourselves.
|
||
if (Vcb->Vpb != NULL) {
|
||
UdfFreePool( &Vcb->Vpb );
|
||
}
|
||
|
||
// Drop the Pcb.
|
||
if (Vcb->Pcb != NULL) {
|
||
UdfDeletePcb( Vcb->Pcb );
|
||
}
|
||
|
||
// Dereference our target if we haven't already done so.
|
||
if (Vcb->TargetDeviceObject != NULL) {
|
||
ObDereferenceObject( Vcb->TargetDeviceObject );
|
||
}
|
||
|
||
// Remove this entry from the global queue.
|
||
RemoveEntryList( &Vcb->VcbLinks );
|
||
|
||
// Delete the Vcb and File resources.
|
||
ExDeleteResource( &Vcb->VcbResource );
|
||
ExDeleteResource( &Vcb->FileResource );
|
||
|
||
// Uninitialize the notify structures.
|
||
if (Vcb->NotifySync != NULL) {
|
||
FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
|
||
}
|
||
|
||
// Now delete the volume device object.
|
||
IoDeleteDevice( (PDEVICE_OBJECT) CONTAINING_RECORD( Vcb, VOLUME_DEVICE_OBJECT, Vcb ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PIRP_CONTEXT UdfCreateIrpContext (IN PIRP Irp, IN BOOLEAN Wait)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to initialize an IrpContext for the current
|
||
UDFS request. We allocate the structure and then initialize it from the given Irp.
|
||
Arguments:
|
||
Irp - Irp for this request.
|
||
Wait - TRUE if this request is synchronous, FALSE otherwise.
|
||
Return Value:
|
||
PIRP_CONTEXT - Allocated IrpContext.
|
||
--*/
|
||
{
|
||
PIRP_CONTEXT NewIrpContext = NULL;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
BOOLEAN IsFsDo = FALSE;
|
||
ULONG Count;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (Count = 0; Count < NUMBER_OF_FS_OBJECTS; Count++) {
|
||
if (IrpSp->DeviceObject == UdfData.FileSystemDeviceObjects[Count]) {
|
||
IsFsDo = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// The only operations a filesystem device object should ever receive
|
||
// are create/teardown of fsdo handles and operations which do not
|
||
// occur in the context of fileobjects (i.e., mount).
|
||
if (IsFsDo) {
|
||
if (IrpSp->FileObject != NULL &&
|
||
IrpSp->MajorFunction != IRP_MJ_CREATE &&
|
||
IrpSp->MajorFunction != IRP_MJ_CLEANUP &&
|
||
IrpSp->MajorFunction != IRP_MJ_CLOSE) {
|
||
ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST );
|
||
}
|
||
|
||
ASSERT( IrpSp->FileObject != NULL ||
|
||
|
||
(IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
|
||
IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST &&
|
||
IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) ||
|
||
|
||
(IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
|
||
IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) ||
|
||
|
||
IrpSp->MajorFunction == IRP_MJ_SHUTDOWN );
|
||
}
|
||
|
||
NewIrpContext = ExAllocateFromNPagedLookasideList( &UdfIrpContextLookasideList );
|
||
|
||
RtlZeroMemory( NewIrpContext, sizeof( IRP_CONTEXT ));
|
||
|
||
// Set the proper node type code and node byte size
|
||
NewIrpContext->NodeTypeCode = UDFS_NTC_IRP_CONTEXT;
|
||
NewIrpContext->NodeByteSize = sizeof( IRP_CONTEXT );
|
||
|
||
// Set the originating Irp field
|
||
NewIrpContext->Irp = Irp;
|
||
|
||
// Copy RealDevice for workque algorithms. We will update this in the Mount or
|
||
// Verify since they have no file objects to use here.
|
||
if (IrpSp->FileObject != NULL) {
|
||
NewIrpContext->RealDevice = IrpSp->FileObject->DeviceObject;
|
||
}
|
||
|
||
// This may be one of our filesystem device objects. In that case don't
|
||
// initialize the Vcb field.
|
||
if (!IsFsDo) {
|
||
NewIrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject)->Vcb;
|
||
}
|
||
|
||
// Major/Minor Function codes
|
||
NewIrpContext->MajorFunction = IrpSp->MajorFunction;
|
||
NewIrpContext->MinorFunction = IrpSp->MinorFunction;
|
||
|
||
// Set the wait parameter
|
||
if (Wait) {
|
||
SetFlag( NewIrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
} else {
|
||
SetFlag( NewIrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
||
}
|
||
|
||
// return and tell the caller
|
||
return NewIrpContext;
|
||
}
|
||
|
||
|
||
VOID UdfCleanupIrpContext (IN PIRP_CONTEXT IrpContext, IN BOOLEAN Post)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to cleanup and possibly deallocate the Irp Context.
|
||
If the request is being posted or this Irp Context is possibly on the
|
||
stack then we only cleanup any auxilary structures.
|
||
Arguments:
|
||
Post - TRUE if we are posting this request, FALSE if we are deleting or retrying this in the current thread.
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
// If we aren't doing more processing then deallocate this as appropriate.
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING)) {
|
||
// If this context is the top level UDFS context then we need to
|
||
// restore the top level thread context.
|
||
if (IrpContext->ThreadContext != NULL) {
|
||
UdfRestoreThreadContext( IrpContext );
|
||
}
|
||
|
||
// Deallocate the Io context if allocated.
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) {
|
||
UdfFreeIoContext( IrpContext->IoContext );
|
||
}
|
||
|
||
// Deallocate the IrpContext if not from the stack.
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ON_STACK )) {
|
||
ExFreeToNPagedLookasideList( &UdfIrpContextLookasideList, IrpContext );
|
||
}
|
||
|
||
// Clear the appropriate flags.
|
||
} else if (Post) {
|
||
// If this context is the top level UDFS context then we need to
|
||
// restore the top level thread context.
|
||
if (IrpContext->ThreadContext != NULL) {
|
||
UdfRestoreThreadContext( IrpContext );
|
||
}
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST );
|
||
} else {
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfInitializeStackIrpContext (OUT PIRP_CONTEXT IrpContext, IN PIRP_CONTEXT_LITE IrpContextLite)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to initialize an IrpContext for the current
|
||
UDFS request. The IrpContext is on the stack and we need to initialize
|
||
it for the current request. The request is a close operation.
|
||
Arguments:
|
||
IrpContext - IrpContext to initialize.
|
||
IrpContextLite - Structure containing the details of this request.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERT_IRP_CONTEXT_LITE( IrpContextLite );
|
||
|
||
// Zero and then initialize the structure.
|
||
RtlZeroMemory( IrpContext, sizeof( IRP_CONTEXT ));
|
||
|
||
// Set the proper node type code and node byte size
|
||
IrpContext->NodeTypeCode = UDFS_NTC_IRP_CONTEXT;
|
||
IrpContext->NodeByteSize = sizeof( IRP_CONTEXT );
|
||
|
||
// Note that this is from the stack.
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ON_STACK );
|
||
|
||
// Copy RealDevice for workque algorithms.
|
||
IrpContext->RealDevice = IrpContextLite->RealDevice;
|
||
|
||
// The Vcb is found in the Fcb.
|
||
IrpContext->Vcb = IrpContextLite->Fcb->Vcb;
|
||
|
||
// Major/Minor Function codes
|
||
IrpContext->MajorFunction = IRP_MJ_CLOSE;
|
||
|
||
// Set the wait parameter
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfTeardownStructures (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB StartingFcb,
|
||
IN BOOLEAN Recursive,
|
||
OUT PBOOLEAN RemovedStartingFcb
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is used to walk from some starting point in the Fcb tree towards
|
||
the root. It will remove the Fcb and continue walking up the tree until
|
||
it finds a point where we can't remove an Fcb.
|
||
|
||
We look at the following fields in the Fcb to determine whether we can remove this.
|
||
1 - Handle count must be zero.
|
||
2 - If directory then only the only reference can be for a stream file.
|
||
3 - Reference count must either be zero or go to zero here.
|
||
|
||
We return immediately if we are recursively entering this routine.
|
||
|
||
Arguments:
|
||
StartingFcb - This is the Fcb node in the tree to begin with. This Fcb must currently be acquired exclusively.
|
||
Recursive - Indicates if this call is an intentional recursion.
|
||
RemovedStartingFcb - Address to store whether we removed the starting Fcb.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PVCB Vcb = StartingFcb->Vcb;
|
||
PFCB CurrentFcb = StartingFcb;
|
||
BOOLEAN AcquiredCurrentFcb = FALSE;
|
||
PFCB ParentFcb = NULL;
|
||
PLCB Lcb;
|
||
PLIST_ENTRY ListLinks;
|
||
BOOLEAN Abort = FALSE;
|
||
BOOLEAN Removed;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check input.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( StartingFcb );
|
||
|
||
*RemovedStartingFcb = FALSE;
|
||
|
||
// If this is not an intentionally recursive call we need to check if this
|
||
// is a layered close and we're already in another instance of teardown.
|
||
|
||
DebugTrace(( +1, Dbg,
|
||
"UdfTeardownStructures, StartingFcb %08x %s\n",
|
||
StartingFcb,
|
||
( Recursive? "Recursive" : "Flat" )));
|
||
|
||
if (!Recursive) {
|
||
// If this is a recursive call to TearDownStructures we return immediately doing no operation.
|
||
if (FlagOn( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN )) {
|
||
return;
|
||
}
|
||
|
||
SetFlag( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN );
|
||
}
|
||
|
||
// Use a try-finally to safely clear the top-level field.
|
||
try {
|
||
// Loop until we find an Fcb we can't remove.
|
||
do {
|
||
// See if there is an internal stream we should delete.
|
||
// Only do this if it is the last reference on the Fcb.
|
||
if ((SafeNodeType( CurrentFcb ) != UDFS_NTC_FCB_DATA) &&
|
||
(CurrentFcb->FcbUserReference == 0) &&
|
||
(CurrentFcb->FileObject != NULL)) {
|
||
// Go ahead and delete the stream file object.
|
||
UdfDeleteInternalStream( IrpContext, CurrentFcb );
|
||
}
|
||
|
||
// If the reference count is non-zero then break.
|
||
if (CurrentFcb->FcbReference != 0) {
|
||
break;
|
||
}
|
||
|
||
// It looks like we have a candidate for removal here. We
|
||
// will need to walk the list of prefixes and delete them
|
||
// from their parents. If it turns out that we have multiple
|
||
// parents of this Fcb, we are going to recursively teardown on each of these.
|
||
for ( ListLinks = CurrentFcb->ParentLcbQueue.Flink; ListLinks != &CurrentFcb->ParentLcbQueue; ) {
|
||
Lcb = CONTAINING_RECORD( ListLinks, LCB, ChildFcbLinks );
|
||
ASSERT_LCB( Lcb );
|
||
|
||
// We advance the pointer now because we will be toasting this guy,
|
||
// invalidating whatever is here.
|
||
ListLinks = ListLinks->Flink;
|
||
|
||
// We may have multiple parents through hard links. If the previous parent we
|
||
// dealt with is not the parent of this new Lcb, lets do some work.
|
||
if (ParentFcb != Lcb->ParentFcb) {
|
||
// We need to deal with the previous parent. It may now be the case that
|
||
// we deleted the last child reference and it wants to go away at this point.
|
||
if (ParentFcb) {
|
||
// It should never be the case that we have to recurse more than one level on
|
||
// any teardown since no cross-linkage of directories is possible.
|
||
|
||
ASSERT( !Recursive );
|
||
UdfTeardownStructures( IrpContext, ParentFcb, TRUE, &Removed );
|
||
if (!Removed) {
|
||
UdfReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
}
|
||
|
||
// Get this new parent Fcb to work on.
|
||
ParentFcb = Lcb->ParentFcb;
|
||
UdfAcquireFcbExclusive( IrpContext, ParentFcb, FALSE );
|
||
}
|
||
|
||
// Lock the Vcb so we can look at references.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
// Now check that the reference counts on the Lcb are zero.
|
||
if ( Lcb->Reference != 0 ) {
|
||
// A create is interested in getting in here, so we should stop right now.
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UdfReleaseFcb( IrpContext, ParentFcb );
|
||
Abort = TRUE;
|
||
break;
|
||
}
|
||
|
||
// Now remove this prefix and drop the references to the parent.
|
||
ASSERT( Lcb->ChildFcb == CurrentFcb );
|
||
ASSERT( Lcb->ParentFcb == ParentFcb );
|
||
|
||
DebugTrace(( +0, Dbg,
|
||
"UdfTeardownStructures, Lcb %08x P %08x <-> C %08x Vcb %d/%d PFcb %d/%d CFcb %d/%d\n",
|
||
Lcb,
|
||
ParentFcb,
|
||
CurrentFcb,
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
ParentFcb->FcbReference,
|
||
ParentFcb->FcbUserReference,
|
||
CurrentFcb->FcbReference,
|
||
CurrentFcb->FcbUserReference ));
|
||
|
||
UdfRemovePrefix( IrpContext, Lcb );
|
||
UdfDecrementReferenceCounts( IrpContext, ParentFcb, 1, 1 );
|
||
|
||
DebugTrace(( +0, Dbg,
|
||
"UdfTeardownStructures, Vcb %d/%d PFcb %d/%d\n",
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
ParentFcb->FcbReference,
|
||
ParentFcb->FcbUserReference ));
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
// Now really leave if we have to.
|
||
if (Abort) {
|
||
break;
|
||
}
|
||
|
||
// Now that we have removed all of the prefixes of this Fcb we can make the final check.
|
||
// Lock the Vcb again so we can inspect the child's references.
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
if (CurrentFcb->FcbReference != 0) {
|
||
DebugTrace(( +0, Dbg,
|
||
"UdfTeardownStructures, saving Fcb %08x %d/%d\n",
|
||
CurrentFcb,
|
||
CurrentFcb->FcbReference,
|
||
CurrentFcb->FcbUserReference ));
|
||
|
||
// Nope, nothing more to do. Stop right now.
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
if (ParentFcb != NULL) {
|
||
UdfReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
// This Fcb is toast. Remove it from the Fcb Table as appropriate and delete.
|
||
if (FlagOn( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE )) {
|
||
UdfDeleteFcbTable( IrpContext, CurrentFcb );
|
||
ClearFlag( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE );
|
||
}
|
||
|
||
// Unlock the Vcb but hold the parent in order to walk up the tree.
|
||
DebugTrace(( +0, Dbg,
|
||
"UdfTeardownStructures, toasting Fcb %08x %d/%d\n",
|
||
CurrentFcb,
|
||
CurrentFcb->FcbReference,
|
||
CurrentFcb->FcbUserReference ));
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UdfDeleteFcb( IrpContext, CurrentFcb );
|
||
|
||
// Move to the parent Fcb.
|
||
CurrentFcb = ParentFcb;
|
||
ParentFcb = NULL;
|
||
AcquiredCurrentFcb = TRUE;
|
||
} while (CurrentFcb != NULL);
|
||
} finally {
|
||
// Release the current Fcb if we have acquired it.
|
||
if (AcquiredCurrentFcb && (CurrentFcb != NULL)) {
|
||
UdfReleaseFcb( IrpContext, CurrentFcb );
|
||
}
|
||
|
||
// Clear the teardown flag.
|
||
if (!Recursive) {
|
||
ClearFlag( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN );
|
||
}
|
||
}
|
||
|
||
*RemovedStartingFcb = (CurrentFcb != StartingFcb);
|
||
|
||
DebugTrace(( -1, Dbg, "UdfTeardownStructures, RemovedStartingFcb -> %c\n", ( *RemovedStartingFcb? 'T' : 'F' )));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PFCB UdfLookupFcbTable (IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN FILE_ID FileId)
|
||
/*++
|
||
Routine Description:
|
||
This routine will look through the Fcb table looking for a matching entry.
|
||
Arguments:
|
||
Vcb - Vcb for this volume.
|
||
FileId - This is the key value to use for the search.
|
||
Return Value:
|
||
PFCB - A pointer to the matching entry or NULL otherwise.
|
||
--*/
|
||
{
|
||
FCB_TABLE_ELEMENT Key;
|
||
PFCB_TABLE_ELEMENT Hit;
|
||
PFCB ReturnFcb = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
Key.FileId = FileId;
|
||
|
||
Hit = (PFCB_TABLE_ELEMENT) RtlLookupElementGenericTable( &Vcb->FcbTable, &Key );
|
||
if (Hit != NULL) {
|
||
ReturnFcb = Hit->Fcb;
|
||
}
|
||
|
||
return ReturnFcb;
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
}
|
||
|
||
|
||
PFCB UdfGetNextFcb (IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PVOID *RestartKey)
|
||
/*++
|
||
Routine Description:
|
||
This routine will enumerate through all of the Fcb's in the Fcb table.
|
||
Arguments:
|
||
Vcb - Vcb for this volume.
|
||
RestartKey - This value is used by the table package to maintain
|
||
its position in the enumeration. It is initialized to NULL for the first search.
|
||
Return Value:
|
||
PFCB - A pointer to the next fcb or NULL if the enumeration is completed
|
||
--*/
|
||
{
|
||
PFCB Fcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
Fcb = (PFCB) RtlEnumerateGenericTableWithoutSplaying( &Vcb->FcbTable, RestartKey );
|
||
if (Fcb != NULL) {
|
||
Fcb = ((PFCB_TABLE_ELEMENT)(Fcb))->Fcb;
|
||
}
|
||
|
||
return Fcb;
|
||
}
|
||
|
||
|
||
PFCB UdfCreateFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN FILE_ID FileId,
|
||
IN NODE_TYPE_CODE NodeTypeCode,
|
||
OUT PBOOLEAN FcbExisted OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to find the Fcb for the given FileId. We will
|
||
look this up first in the Fcb table and if not found we will create
|
||
an Fcb. We don't initialize it or insert it into the FcbTable in this routine.
|
||
|
||
This routine is called while the Vcb is locked.
|
||
Arguments:
|
||
FileId - This is the Id for the target Fcb.
|
||
NodeTypeCode - Node type for this Fcb if we need to create.
|
||
FcbExisted - If specified, we store whether the Fcb existed.
|
||
Return Value:
|
||
PFCB - The Fcb found in the table or created if needed.
|
||
--*/
|
||
{
|
||
PFCB NewFcb;
|
||
BOOLEAN LocalFcbExisted;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Use the local boolean if one was not passed in.
|
||
if (!ARGUMENT_PRESENT( FcbExisted )) {
|
||
FcbExisted = &LocalFcbExisted;
|
||
}
|
||
|
||
// Maybe this is already in the table.
|
||
NewFcb = UdfLookupFcbTable( IrpContext, IrpContext->Vcb, FileId );
|
||
|
||
// If not then create the Fcb is requested by our caller.
|
||
if (NewFcb == NULL) {
|
||
// Use a try-finally for cleanup
|
||
try {
|
||
// Allocate and initialize the structure depending on the type code.
|
||
switch (NodeTypeCode) {
|
||
case UDFS_NTC_FCB_INDEX:
|
||
NewFcb = UdfAllocateFcbIndex( IrpContext );
|
||
RtlZeroMemory( NewFcb, SIZEOF_FCB_INDEX );
|
||
NewFcb->NodeByteSize = SIZEOF_FCB_INDEX;
|
||
break;
|
||
case UDFS_NTC_FCB_DATA :
|
||
NewFcb = UdfAllocateFcbData( IrpContext );
|
||
RtlZeroMemory( NewFcb, SIZEOF_FCB_DATA );
|
||
NewFcb->NodeByteSize = SIZEOF_FCB_DATA;
|
||
break;
|
||
default:
|
||
UdfBugCheck( 0, 0, 0 );
|
||
}
|
||
|
||
// Now do the common initialization.
|
||
NewFcb->NodeTypeCode = NodeTypeCode;
|
||
NewFcb->Vcb = IrpContext->Vcb;
|
||
NewFcb->FileId = FileId;
|
||
|
||
InitializeListHead( &NewFcb->ParentLcbQueue );
|
||
InitializeListHead( &NewFcb->ChildLcbQueue );
|
||
|
||
// Now create the non-paged section object.
|
||
NewFcb->FcbNonpaged = UdfCreateFcbNonPaged( IrpContext );
|
||
*FcbExisted = FALSE;
|
||
} finally {
|
||
DebugUnwind( "UdfCreateFcb" );
|
||
if (AbnormalTermination()) {
|
||
UdfFreePool( &NewFcb );
|
||
}
|
||
}
|
||
} else {
|
||
*FcbExisted = TRUE;
|
||
}
|
||
|
||
return NewFcb;
|
||
}
|
||
|
||
|
||
VOID UdfDeleteFcb (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to cleanup and deallocate an Fcb. We know there
|
||
are no references remaining. We cleanup any auxilary structures and deallocate this Fcb.
|
||
Arguments:
|
||
Fcb - This is the Fcb to deallcoate.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PVCB Vcb = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
// Sanity check the counts and Lcb lists.
|
||
ASSERT( Fcb->FcbCleanup == 0 );
|
||
ASSERT( Fcb->FcbReference == 0 );
|
||
ASSERT( IsListEmpty( &Fcb->ChildLcbQueue ));
|
||
ASSERT( IsListEmpty( &Fcb->ParentLcbQueue ));
|
||
|
||
// Start with the common structures.
|
||
UdfUninitializeFcbMcb( Fcb );
|
||
UdfDeleteFcbNonpaged( IrpContext, Fcb->FcbNonpaged );
|
||
|
||
// Now do the type specific structures.
|
||
switch (Fcb->NodeTypeCode) {
|
||
case UDFS_NTC_FCB_INDEX:
|
||
ASSERT( Fcb->FileObject == NULL );
|
||
if (Fcb == Fcb->Vcb->RootIndexFcb) {
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->RootIndexFcb = NULL;
|
||
} else if (Fcb == Fcb->Vcb->MetadataFcb) {
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->MetadataFcb = NULL;
|
||
UdfUninitializeVmcb( &Vcb->Vmcb );
|
||
} else if (Fcb == Fcb->Vcb->VatFcb) {
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->VatFcb = NULL;
|
||
}
|
||
|
||
UdfDeallocateFcbIndex( IrpContext, Fcb );
|
||
break;
|
||
case UDFS_NTC_FCB_DATA :
|
||
if (Fcb->FileLock != NULL) {
|
||
FsRtlFreeFileLock( Fcb->FileLock );
|
||
}
|
||
|
||
FsRtlUninitializeOplock( &Fcb->Oplock );
|
||
if (Fcb == Fcb->Vcb->VolumeDasdFcb) {
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->VolumeDasdFcb = NULL;
|
||
}
|
||
|
||
UdfDeallocateFcbData( IrpContext, Fcb );
|
||
break;
|
||
}
|
||
|
||
// Decrement the Vcb reference count if this is a system Fcb.
|
||
if (Vcb != NULL) {
|
||
InterlockedDecrement( &Vcb->VcbReference );
|
||
InterlockedDecrement( &Vcb->VcbUserReference );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfInitializeFcbFromIcbContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PICB_SEARCH_CONTEXT IcbContext
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to initialize an Fcb from a direct ICB. It should
|
||
only be called once in the lifetime of an Fcb and will fill in the Mcb
|
||
from the chained allocation descriptors of the ICB.
|
||
Arguments:
|
||
Fcb - The Fcb being initialized
|
||
IcbOontext - An search context containing the active direct ICB for the object
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
EA_SEARCH_CONTEXT EaContext;
|
||
PICBFILE Icb;
|
||
PVCB Vcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
// Directly reference for convenience
|
||
Icb = IcbContext->Active.View;
|
||
Vcb = Fcb->Vcb;
|
||
|
||
ASSERT( IcbContext->IcbType == DESTAG_ID_NSR_FILE && Icb->Destag.Ident == DESTAG_ID_NSR_FILE );
|
||
|
||
// Check that the full indicated size of the direct entry is sane and
|
||
// that the length of the EA segment is correctly aligned. A direct
|
||
// entry is less than a single logical block in size.
|
||
|
||
if (LongOffset( Icb->EALength ) ||
|
||
FIELD_OFFSET( ICBFILE, EAs ) + Icb->EALength + Icb->AllocLength > BlockSize( IcbContext->Vcb )) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
UdfLockFcb( IrpContext, Fcb );
|
||
|
||
// Try-finally for cleanup.
|
||
try {
|
||
// Verify that the types mesh and set state flags.
|
||
if (Fcb->NodeTypeCode == UDFS_NTC_FCB_INDEX && Icb->Icbtag.FileType == ICBTAG_FILE_T_DIRECTORY) {
|
||
SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
|
||
} else if (!(Fcb->NodeTypeCode == UDFS_NTC_FCB_DATA && Icb->Icbtag.FileType == ICBTAG_FILE_T_FILE)) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_READONLY );
|
||
|
||
// Initialize the common header in the Fcb.
|
||
Fcb->Resource = &Fcb->Vcb->FileResource;
|
||
|
||
// Size and lookup all allocations for this object.
|
||
Fcb->AllocationSize.QuadPart = LlBlockAlign( Vcb, Icb->InfoLength );
|
||
|
||
Fcb->FileSize.QuadPart =
|
||
Fcb->ValidDataLength.QuadPart = Icb->InfoLength;
|
||
|
||
UdfInitializeAllocations( IrpContext, Fcb, IcbContext );
|
||
|
||
// Lift all of the timestamps for this guy.
|
||
UdfUpdateTimestampsFromIcbContext( IrpContext, IcbContext, &Fcb->Timestamps );
|
||
|
||
// Pick up the link count.
|
||
Fcb->LinkCount = Icb->LinkCount;
|
||
|
||
// Link into the Fcb table. Someone else is responsible for the name linkage, which is
|
||
// all that remains. We also note that the Fcb is fully initialized at this point.
|
||
UdfInsertFcbTable( IrpContext, Fcb );
|
||
SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE | FCB_STATE_INITIALIZED );
|
||
} finally {
|
||
UdfUnlockFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PCCB UdfCreateCcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PLCB Lcb OPTIONAL,
|
||
IN ULONG Flags
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to allocate and initialize the Ccb structure.
|
||
Arguments:
|
||
Fcb - This is the Fcb for the file being opened.
|
||
Lcb - This is the Lcb the Fcb is opened by.
|
||
Flags - User flags to set in this Ccb.
|
||
Return Value:
|
||
PCCB - Pointer to the created Ccb.
|
||
--*/
|
||
{
|
||
PCCB NewCcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
ASSERT_OPTIONAL_LCB( Lcb );
|
||
|
||
// Allocate and initialize the structure.
|
||
NewCcb = UdfAllocateCcb( IrpContext );
|
||
|
||
// Set the proper node type code and node byte size
|
||
NewCcb->NodeTypeCode = UDFS_NTC_CCB;
|
||
NewCcb->NodeByteSize = sizeof( CCB );
|
||
|
||
// Set the initial value for the flags and Fcb/Lcb
|
||
NewCcb->Flags = Flags;
|
||
NewCcb->Fcb = Fcb;
|
||
NewCcb->Lcb = Lcb;
|
||
|
||
// Initialize the directory enumeration context
|
||
NewCcb->CurrentFileIndex = 0;
|
||
NewCcb->HighestReturnableFileIndex = 0;
|
||
|
||
NewCcb->SearchExpression.Length =
|
||
NewCcb->SearchExpression.MaximumLength = 0;
|
||
NewCcb->SearchExpression.Buffer = NULL;
|
||
|
||
return NewCcb;
|
||
}
|
||
|
||
|
||
VOID UdfDeleteCcb (IN PIRP_CONTEXT IrpContext, IN PCCB Ccb)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to cleanup and deallocate a Ccb structure.
|
||
Arguments:
|
||
Ccb - This is the Ccb to delete.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check inputs.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_CCB( Ccb );
|
||
|
||
if (Ccb->SearchExpression.Buffer != NULL) {
|
||
UdfFreePool( &Ccb->SearchExpression.Buffer );
|
||
}
|
||
|
||
UdfDeallocateCcb( IrpContext, Ccb );
|
||
return;
|
||
}
|
||
|
||
|
||
ULONG UdfFindInParseTable (IN PPARSE_KEYVALUE ParseTable, IN PCHAR Id, IN ULONG MaxIdLen)
|
||
/*++
|
||
Routine Description:
|
||
This routine walks a table of string key/value information for a match of the
|
||
input Id. MaxIdLen can be set to get a prefix match.
|
||
Arguments:
|
||
Table - This is the table being searched.
|
||
Id - Key value.
|
||
MaxIdLen - Maximum possible length of Id.
|
||
Return Value:
|
||
Value of matching entry, or the terminating (NULL) entry's value.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
while (ParseTable->Key != NULL) {
|
||
if (RtlEqualMemory(ParseTable->Key, Id, MaxIdLen)) {
|
||
break;
|
||
}
|
||
|
||
ParseTable++;
|
||
}
|
||
|
||
return ParseTable->Value;
|
||
}
|
||
|
||
|
||
#ifdef UDF_SANITY
|
||
|
||
|
||
// Enumerate the reasons why a descriptor might be bad.
|
||
typedef enum _VERIFY_FAILURE {
|
||
Nothing,
|
||
BadLbn,
|
||
BadTag,
|
||
BadChecksum,
|
||
BadCrcLength,
|
||
BadCrc,
|
||
BadDestagVersion
|
||
} VERIFY_FAILURE;
|
||
|
||
#endif
|
||
|
||
|
||
BOOLEAN UdfVerifyDescriptor (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDESTAG Descriptor,
|
||
IN USHORT Tag,
|
||
IN ULONG Size,
|
||
IN ULONG Lbn,
|
||
IN BOOLEAN ReturnError
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine verifies that a descriptor using a Descriptor tag (3/7.2) is
|
||
consistent with itself and the descriptor data.
|
||
Arguments:
|
||
Descriptor - This is the pointer to the descriptor tag, which is always at the front of a descriptor
|
||
Tag - The Tag Identifier this descriptor should have
|
||
Size - Size of this descriptor
|
||
Lbn - The logical block number this descriptor should claim it is recorded at
|
||
ReturnError - Whether this routine should return an error or raise
|
||
Return Value:
|
||
Boolean TRUE if the descriptor is consistent, FALSE or a raised status of STATUS_DISK_CORRUPT_ERROR otherwise.
|
||
--*/
|
||
{
|
||
UCHAR Checksum = 0;
|
||
PCHAR CheckPtr;
|
||
USHORT Crc;
|
||
|
||
#ifdef UDF_SANITY
|
||
VERIFY_FAILURE FailReason = Nothing;
|
||
#endif
|
||
|
||
// Check our inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
PAGED_CODE();
|
||
|
||
#ifdef UDF_SANITY
|
||
if (UdfNoisyVerifyDescriptor) {
|
||
goto BeNoisy;
|
||
}
|
||
|
||
RegularEntry:
|
||
#endif
|
||
|
||
// The version of the Descriptor Tag specified in ISO 13346 and used in
|
||
// UDF is a particular value; presumeably, previous versions were used
|
||
// in some older revision of the standard.
|
||
|
||
#ifdef UDF_SUPPORT_NONSTANDARD_ADAPTEC
|
||
|
||
// Let bad descriptor version numbers through.
|
||
// Reasons:
|
||
// ADAPTEC - early CDUDF wrote version 1 as opposed to version 2.
|
||
|
||
if (TRUE)
|
||
#else
|
||
if (Descriptor->Version == DESTAG_VER_CURRENT)
|
||
#endif
|
||
{
|
||
// A descriptor is stamped in four ways. First, the Lbn of the sector
|
||
// containing the descriptor is written here. (3/7.2.8)
|
||
|
||
#ifdef UDF_SUPPORT_NONSTANDARD_HP
|
||
// Let bad lbn through.
|
||
// Reasons:
|
||
// HP - Rob Sim;s CD-RW model disc doesn't record this reliably.
|
||
if (TRUE)
|
||
#else
|
||
if (Descriptor->Lbn == Lbn)
|
||
#endif
|
||
{
|
||
// Next, the descriptor tag itself has an identifier which should match
|
||
// the type we expect to find here (3/7.2.1)
|
||
if (Descriptor->Ident == Tag) {
|
||
// Next, the descriptor tag itself is checksumed, minus the byte
|
||
// used to store the checksum. (3/7.2.3)
|
||
for (CheckPtr = (PCHAR) Descriptor;
|
||
CheckPtr < (PCHAR) Descriptor + FIELD_OFFSET( DESTAG, Checksum );
|
||
CheckPtr++) {
|
||
Checksum += *CheckPtr;
|
||
}
|
||
|
||
for (CheckPtr = (PCHAR) Descriptor + FIELD_OFFSET( DESTAG, Checksum ) + sizeof(UCHAR);
|
||
CheckPtr < (PCHAR) Descriptor + sizeof(DESTAG);
|
||
CheckPtr++) {
|
||
Checksum += *CheckPtr;
|
||
}
|
||
|
||
if (Descriptor->Checksum == Checksum) {
|
||
// Now we check that the CRC in the Descriptor tag is sized sanely
|
||
// and matches the Descriptor data. (3/7.2.6)
|
||
|
||
#ifdef UDF_SUPPORT_NONSTANDARD_HP
|
||
|
||
// Let zero-length CRCs through.
|
||
// Reasons:
|
||
// HP - early CDUDF didn't CRC the terminationg descriptors (tag 8).
|
||
if (!Descriptor->CRCLen || Descriptor->CRCLen <= Size - sizeof(DESTAG))
|
||
#else
|
||
if (Descriptor->CRCLen && Descriptor->CRCLen <= Size - sizeof(DESTAG))
|
||
#endif
|
||
{
|
||
Crc = UdfComputeCrc16( (PCHAR) Descriptor + sizeof(DESTAG), Descriptor->CRCLen );
|
||
#ifdef UDF_SUPPORT_NONSTANDARD_HP
|
||
// Let zero-length CRCs through.
|
||
// Reasons: as above.
|
||
if (!Descriptor->CRCLen || Descriptor->CRC == Crc)
|
||
#else
|
||
if (Descriptor->CRC == Crc)
|
||
#endif
|
||
{
|
||
// This descriptor checks out.
|
||
#ifdef UDF_SANITY
|
||
if (UdfNoisyVerifyDescriptor) {
|
||
DebugTrace(( -1, Dbg, "UdfVerifyDescriptor -> TRUE\n" ));
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadCrc;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadCrcLength;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadChecksum;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadTag;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadLbn;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
} else {
|
||
#ifdef UDF_SANITY
|
||
FailReason = BadDestagVersion;
|
||
goto ReportFailure;
|
||
#endif
|
||
}
|
||
|
||
#ifdef UDF_SANITY
|
||
BeNoisy:
|
||
|
||
DebugTrace(( +1, Dbg,
|
||
"UdfVerifyDescriptor, Destag %08x, Tag %x, Size %x, Lbn %x\n",
|
||
Descriptor,
|
||
Tag,
|
||
Size,
|
||
Lbn ));
|
||
|
||
if (FailReason == Nothing) {
|
||
goto RegularEntry;
|
||
} else if (!UdfNoisyVerifyDescriptor) {
|
||
goto ReallyReportFailure;
|
||
}
|
||
|
||
ReportFailure:
|
||
|
||
if (!UdfNoisyVerifyDescriptor) {
|
||
goto BeNoisy;
|
||
}
|
||
|
||
ReallyReportFailure:
|
||
|
||
switch (FailReason) {
|
||
case BadLbn:
|
||
DebugTrace(( 0, Dbg, "Lbn mismatch - Lbn %x != expected %x\n", Descriptor->Lbn, Lbn ));
|
||
break;
|
||
case BadTag:
|
||
DebugTrace(( 0, Dbg, "Tag mismatch - Ident %x != expected %x\n", Descriptor->Ident, Tag ));
|
||
break;
|
||
case BadChecksum:
|
||
DebugTrace(( 0, Dbg, "Checksum mismatch - Checksum %x != descriptor's %x\n", Checksum, Descriptor->Checksum ));
|
||
break;
|
||
case BadCrcLength:
|
||
DebugTrace(( 0, Dbg, "CRC'd size bad - CrcLen %x is 0 or > max %x\n", Descriptor->CRCLen, Size - sizeof(DESTAG) ));
|
||
break;
|
||
case BadCrc:
|
||
DebugTrace(( 0, Dbg, "CRC mismatch - Crc %x != descriptor's %x\n", Crc, Descriptor->CRC ));
|
||
break;
|
||
case BadDestagVersion:
|
||
DebugTrace(( 0, Dbg, "Bad Destag Verion - %x != descriptor's %x\n", DESTAG_VER_CURRENT, Descriptor->Version ));
|
||
break;
|
||
default:
|
||
ASSERT( FALSE );
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfVerifyDescriptor -> FALSE\n" ));
|
||
|
||
#endif
|
||
|
||
if (!ReturnError) {
|
||
UdfRaiseStatus( IrpContext, STATUS_CRC_ERROR );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID UdfInitializeIcbContextFromFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN PFCB Fcb
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to initialize a context to search the Icb hierarchy associated with an Fcb.
|
||
Arguments:
|
||
Fcb - Fcb associated with the hierarchy to search.
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check input parameters.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
RtlZeroMemory( IcbContext, sizeof( ICB_SEARCH_CONTEXT ));
|
||
|
||
IcbContext->Vcb = Fcb->Vcb;
|
||
IcbContext->IcbType = DESTAG_ID_NSR_FILE;
|
||
|
||
// It is possible that we don't have an idea what the length of the root extent is.
|
||
// This will commonly happen in the OpenById case.
|
||
if (Fcb->RootExtentLength == 0) {
|
||
PICBFILE Icb;
|
||
|
||
// We have to lift the first entry from this (possibly bogus!) extent
|
||
// and find out how many entries can be recorded, then unmap/remap the view to try to get the extent.
|
||
UdfMapMetadataView( IrpContext,
|
||
&IcbContext->Current,
|
||
IcbContext->Vcb,
|
||
UdfGetFidPartition( Fcb->FileId ),
|
||
UdfGetFidLbn( Fcb->FileId ),
|
||
BlockSize( IcbContext->Vcb ));
|
||
|
||
Icb = IcbContext->Current.View;
|
||
|
||
// We can only accomplish the guess if we have a descriptor which contains an ICB
|
||
// Tag, which contains a field that can tell us what we need to know.
|
||
|
||
if (Icb->Destag.Ident == DESTAG_ID_NSR_ICBIND ||
|
||
Icb->Destag.Ident == DESTAG_ID_NSR_ICBTRM ||
|
||
Icb->Destag.Ident == DESTAG_ID_NSR_FILE ||
|
||
Icb->Destag.Ident == DESTAG_ID_NSR_UASE ||
|
||
Icb->Destag.Ident == DESTAG_ID_NSR_PINTEG) {
|
||
UdfVerifyDescriptor( IrpContext,
|
||
&Icb->Destag,
|
||
Icb->Destag.Ident,
|
||
BlockSize( IcbContext->Vcb ),
|
||
UdfGetFidLbn( Fcb->FileId ),
|
||
FALSE );
|
||
} else {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
// Now the MaxEntries (4/14.6.4) field of the Icb Tag should tell us how big the extent
|
||
// should be. The tail of this could be unrecorded. We could even have landed in the middle
|
||
// of an extent. This is only a guess. For whatever reason we are having to guess this
|
||
// information, any results are expected to be coming with few guarantees.
|
||
Fcb->RootExtentLength = Icb->Icbtag.MaxEntries * BlockSize( IcbContext->Vcb );
|
||
}
|
||
|
||
// Map the first extent into the current slot.
|
||
UdfMapMetadataView( IrpContext,
|
||
&IcbContext->Current,
|
||
IcbContext->Vcb,
|
||
UdfGetFidPartition( Fcb->FileId ),
|
||
UdfGetFidLbn( Fcb->FileId ),
|
||
Fcb->RootExtentLength );
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfInitializeIcbContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN PVCB Vcb,
|
||
IN USHORT IcbType,
|
||
IN USHORT Partition,
|
||
IN ULONG Lbn,
|
||
IN ULONG Length
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to initialize a context to search an Icb hierarchy.
|
||
Arguments:
|
||
Vcb - Vcb for the volume.
|
||
IcbType - Type of direct entry we expect to find (DESTAG_ID...)
|
||
Partition - partition of the hierarchy.
|
||
Lbn - lbn of the hierarchy.
|
||
Length - length of the root extent of the hierarchy.
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check input parameters.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
RtlZeroMemory( IcbContext, sizeof( ICB_SEARCH_CONTEXT ));
|
||
|
||
IcbContext->Vcb = Vcb;
|
||
IcbContext->IcbType = IcbType;
|
||
|
||
// Map the first extent into the current slot.
|
||
UdfMapMetadataView( IrpContext,
|
||
&IcbContext->Current,
|
||
Vcb,
|
||
Partition,
|
||
Lbn,
|
||
Length );
|
||
return;
|
||
}
|
||
|
||
|
||
VOID UdfLookupActiveIcb (IN PIRP_CONTEXT IrpContext, IN PICB_SEARCH_CONTEXT IcbContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to cause the active Icb for an Icb hierarchy to be mapped.
|
||
A context initialized by UdfInitializeIcbContext() is required.
|
||
Arguments:
|
||
IcbContext - Context which has been initialized to point into an Icb hierarchy.
|
||
Return Value:
|
||
None.
|
||
Raised status if the Icb hierarchy is invalid.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check input parameters.
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
// Travel the Icb hierarchy. Due to the design of ISO 13346, it is convenient to
|
||
// recursively descend the hierarchy. Place a limit on this recursion which will
|
||
// allow traversal of most reasonable hierarchies (this will tail recurse off of the end of extents).
|
||
|
||
UdfLookupActiveIcbInExtent( IrpContext, IcbContext, UDF_ICB_RECURSION_LIMIT );
|
||
|
||
// We must have found an active ICB. Drop the last mapped part of the enumeration at this point.
|
||
UdfUnpinView( IrpContext, &IcbContext->Current );
|
||
|
||
if (IcbContext->Active.View == NULL) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
}
|
||
|
||
|
||
VOID UdfCleanupIcbContext (IN PIRP_CONTEXT IrpContext, IN PICB_SEARCH_CONTEXT IcbContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine cleans an Icb search context for reuse/deletion.
|
||
Arguments:
|
||
IcbContext - context to clean
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
UdfUnpinView( IrpContext, &IcbContext->Active );
|
||
UdfUnpinView( IrpContext, &IcbContext->Current );
|
||
|
||
RtlZeroMemory( IcbContext, sizeof( ICB_SEARCH_CONTEXT ));
|
||
}
|
||
|
||
|
||
VOID UdfInitializeEaContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PEA_SEARCH_CONTEXT EaContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN ULONG EAType,
|
||
IN UCHAR EASubType
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine initializes a walk through the EA space of an Icb which has been previously discovered.
|
||
Note: only the embedded EA space is supported now.
|
||
Arguments:
|
||
EaContext - EA context to fill in
|
||
IcbContext - Elaborated ICB search structure
|
||
--*/
|
||
{
|
||
PICBFILE Icb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT( IcbContext->Active.Bcb && IcbContext->Active.View );
|
||
|
||
Icb = IcbContext->Active.View;
|
||
EaContext->IcbContext = IcbContext;
|
||
|
||
// Initialize to point at the first EA to return.
|
||
|
||
EaContext->Ea = Icb->EAs;
|
||
EaContext->Remaining = Icb->EALength;
|
||
|
||
EaContext->EAType = EAType;
|
||
EaContext->EASubType = EASubType;
|
||
}
|
||
|
||
|
||
BOOLEAN UdfLookupEa (IN PIRP_CONTEXT IrpContext, IN PEA_SEARCH_CONTEXT EaContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine finds an EA in the EA space of an ICB.
|
||
Arguments:
|
||
EaContext - an initialized EA search context containing an elaborated
|
||
ICB search context and a description of the EA to find.
|
||
Return Value:
|
||
BOOLEAN True if such an EA was found and returned, False otherwise.
|
||
--*/
|
||
{
|
||
PICBFILE Icb;
|
||
PNSR_EA_GENERIC GenericEa;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
// Quickly terminate if the EA space is empty or not capable of containing
|
||
// the header descriptor. A null EA space is perfectly legal.
|
||
if (EaContext->Remaining == 0) {
|
||
return FALSE;
|
||
} else if (EaContext->Remaining < sizeof( NSR_EAH )) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
// Verify the integrity of the EA header. This has a side effect of making
|
||
// very sure that we really have an EA sequence underneath us.
|
||
|
||
Icb = EaContext->IcbContext->Active.View;
|
||
UdfVerifyDescriptor( IrpContext,
|
||
&((PNSR_EAH) EaContext->Ea)->Destag,
|
||
DESTAG_ID_NSR_EA,
|
||
sizeof( NSR_EAH ),
|
||
Icb->Destag.Lbn,
|
||
FALSE );
|
||
|
||
// Push forward the start of the EA space and loop while we have more EAs to inspect.
|
||
// Since we only scan for ISO EA's right now, we don't need to open the EA header to
|
||
// jump forward to the Implementation Use or Application Use segments.
|
||
|
||
EaContext->Ea = Add2Ptr( EaContext->Ea, sizeof( NSR_EAH ), PVOID );
|
||
EaContext->Remaining -= sizeof( NSR_EAH );
|
||
|
||
while (EaContext->Remaining) {
|
||
GenericEa = EaContext->Ea;
|
||
|
||
// The EAs must appear on 4byte aligned boundaries, there must be room to find
|
||
// the generic EA preamble and the claimed length of the EA must fit in the remaining space.
|
||
|
||
if (LongOffsetPtr( EaContext->Ea ) ||
|
||
EaContext->Remaining < FIELD_OFFSET( NSR_EA_GENERIC, EAData ) ||
|
||
EaContext->Remaining < GenericEa->EALength ) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
if (GenericEa->EAType == EaContext->EAType && GenericEa->EASubType == EaContext->EASubType) {
|
||
return TRUE;
|
||
}
|
||
|
||
EaContext->Ea = Add2Ptr( EaContext->Ea, GenericEa->EALength, PVOID );
|
||
EaContext->Remaining -= GenericEa->EALength;
|
||
}
|
||
|
||
// If we failed to find the EA, we should have stopped at the precise end of the EA space.
|
||
if (EaContext->Remaining) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID UdfInitializeAllocations (IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PICB_SEARCH_CONTEXT IcbContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine fills in the data retrieval information for an Fcb.
|
||
Arguments:
|
||
Fcb - Fcb to add retrieval information to.
|
||
IcbContext - Elaborated ICB search context corresponding to this Fcb.
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PICBFILE Icb = IcbContext->Active.View;
|
||
PAD_GENERIC GenericAd;
|
||
ALLOC_ENUM_CONTEXT AllocContext;
|
||
LONGLONG RunningOffset;
|
||
ULONG Psn;
|
||
USHORT Partition;
|
||
PVCB Vcb = Fcb->Vcb;
|
||
BOOLEAN Result;
|
||
BOOLEAN InFileTail = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
// Immediately return for objects with zero information space. Note that
|
||
// passing this test does not indicate that the file has any recorded space.
|
||
if (Fcb->FileSize.QuadPart == 0) {
|
||
return;
|
||
}
|
||
|
||
UdfInitializeAllocationContext( IrpContext, &AllocContext, IcbContext );
|
||
|
||
// Handle the case of embedded data.
|
||
if (AllocContext.AllocType == ICBTAG_F_ALLOC_IMMEDIATE) {
|
||
// Teardown any existing mcb.
|
||
UdfUninitializeFcbMcb( Fcb );
|
||
|
||
// Establish a single block mapping to the Icb itself and mark the Fcb as
|
||
// having embedded data. Mapping will occur through the Metadata stream.
|
||
// Note that by virtue of having an Icb here we know it has already had
|
||
// a mapping established in the Metadata stream, so just retrieve that.
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_EMBEDDED_DATA );
|
||
|
||
Fcb->EmbeddedVsn = UdfLookupMetaVsnOfExtent( IrpContext,
|
||
Vcb,
|
||
IcbContext->Active.Partition,
|
||
Icb->Destag.Lbn,
|
||
BlockSize( Vcb ),
|
||
FALSE );
|
||
|
||
// Note the offset of the data in the Icb.
|
||
Fcb->EmbeddedOffset = FIELD_OFFSET( ICBFILE, EAs ) + Icb->EALength;
|
||
|
||
// Check that the information length agrees.
|
||
if (Icb->AllocLength != Fcb->FileSize.LowPart) {
|
||
DebugTrace(( 0, Dbg, "UdfInitializeAllocations, embedded alloc %08x != filesize %08x\n",
|
||
Icb->AllocLength,
|
||
Fcb->FileSize.LowPart ));
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// Now initialize the mapping structure for this Fcb.
|
||
UdfInitializeFcbMcb( Fcb );
|
||
|
||
// Now walk the chain of allocation descriptors for the object, adding them into the mapping.
|
||
RunningOffset = 0;
|
||
|
||
do {
|
||
// Do file tail consistency checking (4/12.1). We will have a file body
|
||
// which is exactly the size of the information length, and a run of allocated
|
||
// but unrecorded space following.
|
||
|
||
if (Fcb->FileSize.QuadPart == RunningOffset) {
|
||
InFileTail = TRUE;
|
||
}
|
||
|
||
// It is impermissible for an interior body extent of an object to not be
|
||
// an integral multiple of a logical block in size (note that the last
|
||
// will tend to be). Also check that the body didn't overshoot the information
|
||
// length and that the tail descriptor type is correct.
|
||
|
||
GenericAd = AllocContext.Alloc;
|
||
|
||
if ((!InFileTail && (BlockOffset( Vcb, RunningOffset ) ||
|
||
Fcb->FileSize.QuadPart < RunningOffset)) ||
|
||
(InFileTail && GenericAd->Length.Type != NSRLENGTH_TYPE_UNRECORDED)) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfInitializeAllocations, InFileTail == %u, bad alloc\n", InFileTail ));
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
// In the file tail we'll just slum along an be a nitpick. Technically, as a
|
||
// readonly implementation I don't have to care at all about what is here.
|
||
|
||
if (InFileTail) {
|
||
continue;
|
||
}
|
||
|
||
// Based on the descriptor type, pull it apart and add the mapping.
|
||
if (GenericAd->Length.Type == NSRLENGTH_TYPE_RECORDED) {
|
||
// Grab the Psn this extent starts at and add the allocation.
|
||
Psn = UdfLookupPsnOfExtent( IrpContext,
|
||
Vcb,
|
||
UdfGetPartitionOfCurrentAllocation( &AllocContext ),
|
||
GenericAd->Start,
|
||
GenericAd->Length.Length );
|
||
Result = FsRtlAddLargeMcbEntry( &Fcb->Mcb,
|
||
LlSectorsFromBytes( Vcb, RunningOffset ),
|
||
Psn,
|
||
SectorsFromBytes( Vcb, SectorAlign( Vcb, GenericAd->Length.Length ) ));
|
||
|
||
ASSERT( Result );
|
||
}
|
||
|
||
RunningOffset += GenericAd->Length.Length;
|
||
} while ( UdfGetNextAllocation( IrpContext, &AllocContext ));
|
||
|
||
// We must have had body allocation descriptors for the entire file information length.
|
||
if (Fcb->FileSize.QuadPart != RunningOffset) {
|
||
DebugTrace(( 0, Dbg, "UdfInitializeAllocations, total descriptors != filesize\n" ));
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
}
|
||
|
||
|
||
VOID UdfUpdateTimestampsFromIcbContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN PTIMESTAMP_BUNDLE Timestamps
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine converts the set of timestamps associated with a given ICB into an NT native form.
|
||
Arguments:
|
||
IcbOontext - An search context containing the active direct ICB for the object
|
||
Timestamps - the bundle of timestamps to receive the converted times.
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
EA_SEARCH_CONTEXT EaContext;
|
||
PICBFILE Icb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
// Directly reference for convenience.
|
||
Icb = IcbContext->Active.View;
|
||
|
||
ASSERT( Icb->Destag.Ident == DESTAG_ID_NSR_FILE );
|
||
|
||
// Initialize the timestamps for this object. Due to basic idiocy in ISO 13346,
|
||
// we must gather EAs and figure out which of several timestamps is most valid.
|
||
|
||
// Begin by using the fields in the ICB itself.
|
||
UdfConvertUdfTimeToNtTime( IrpContext,
|
||
&Icb->ModifyTime,
|
||
(PLARGE_INTEGER) &Timestamps->ModificationTime );
|
||
|
||
Timestamps->CreationTime = Timestamps->ModificationTime;
|
||
|
||
UdfConvertUdfTimeToNtTime( IrpContext,
|
||
&Icb->AccessTime,
|
||
(PLARGE_INTEGER) &Timestamps->AccessTime );
|
||
|
||
// Gather the File Times and Information Times EAs for this object, if they exist,
|
||
// and set the timestamps. Just override timestamps if they exist.
|
||
|
||
UdfInitializeEaContext( IrpContext, &EaContext, IcbContext, EA_TYPE_FILETIMES, EA_SUBTYPE_BASE );
|
||
|
||
if (UdfLookupEa( IrpContext, &EaContext )) {
|
||
PNSR_EA_FILETIMES FileTimes = EaContext.Ea;
|
||
|
||
if (FlagOn(FileTimes->Existence, EA_FILETIMES_E_CREATION)) {
|
||
UdfConvertUdfTimeToNtTime( IrpContext,
|
||
&FileTimes->Stamps[0],
|
||
(PLARGE_INTEGER) &Timestamps->CreationTime );
|
||
}
|
||
}
|
||
|
||
UdfInitializeEaContext( IrpContext, &EaContext, IcbContext, EA_TYPE_INFOTIMES, EA_SUBTYPE_BASE );
|
||
|
||
if (UdfLookupEa( IrpContext, &EaContext )) {
|
||
PNSR_EA_FILETIMES InfoTimes = EaContext.Ea;
|
||
BOOLEAN CreationPresent = FALSE;
|
||
|
||
if (FlagOn(InfoTimes->Existence, EA_INFOTIMES_E_CREATION)) {
|
||
UdfConvertUdfTimeToNtTime( IrpContext,
|
||
&InfoTimes->Stamps[0],
|
||
(PLARGE_INTEGER) &Timestamps->CreationTime );
|
||
|
||
CreationPresent = TRUE;
|
||
}
|
||
|
||
if (FlagOn(InfoTimes->Existence, EA_INFOTIMES_E_MODIFICATION)) {
|
||
UdfConvertUdfTimeToNtTime( IrpContext,
|
||
&InfoTimes->Stamps[CreationPresent? 1: 0],
|
||
(PLARGE_INTEGER) &Timestamps->CreationTime );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN UdfCreateFileLock (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PFCB Fcb,
|
||
IN BOOLEAN RaiseOnError
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called when we want to attach a file lock structure to the
|
||
given Fcb. It is possible the file lock is already attached.
|
||
|
||
This routine is sometimes called from the fast path and sometimes in the
|
||
Irp-based path. We don't want to raise in the fast path, just return FALSE.
|
||
|
||
Arguments:
|
||
Fcb - This is the Fcb to create the file lock for.
|
||
RaiseOnError - If TRUE, we will raise on an allocation failure. Otherwise we return FALSE on an allocation failure.
|
||
Return Value:
|
||
BOOLEAN - TRUE if the Fcb has a filelock, FALSE otherwise.
|
||
--*/
|
||
{
|
||
BOOLEAN Result = TRUE;
|
||
PFILE_LOCK FileLock;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Lock the Fcb and check if there is really any work to do.
|
||
UdfLockFcb( IrpContext, Fcb );
|
||
|
||
if (Fcb->FileLock != NULL) {
|
||
UdfUnlockFcb( IrpContext, Fcb );
|
||
return TRUE;
|
||
}
|
||
|
||
Fcb->FileLock = FileLock = FsRtlAllocateFileLock( NULL, NULL );
|
||
|
||
UdfUnlockFcb( IrpContext, Fcb );
|
||
|
||
// Return or raise as appropriate.
|
||
if (FileLock == NULL) {
|
||
if (RaiseOnError) {
|
||
ASSERT( ARGUMENT_PRESENT( IrpContext ));
|
||
UdfRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
Result = FALSE;
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
VOID UdfLookupActiveIcbInExtent (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext,
|
||
IN ULONG Recurse
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to traverse a single Icb hierarchy extent to discover
|
||
an active Icb. This is a recursive operation on indirect Icbs that may be found in the sequence.
|
||
Arguments:
|
||
IcbContext - Context which has been initialized to point into an Icb hierarchy.
|
||
Recurse - Recursion limit.
|
||
Return Value:
|
||
None.
|
||
Raised status if the Icb hierarchy is invalid.
|
||
--*/
|
||
{
|
||
PVCB Vcb = IcbContext->Vcb;
|
||
PFCB Fcb = Vcb->MetadataFcb;
|
||
ULONG Length;
|
||
ULONG Lbn;
|
||
USHORT Partition;
|
||
ULONG Vsn;
|
||
PICBIND Icb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Decrement our recursion allowance.
|
||
Recurse--;
|
||
|
||
// Grab our starting point
|
||
Partition = IcbContext->Current.Partition;
|
||
Lbn = IcbContext->Current.Lbn;
|
||
Length = IcbContext->Current.Length;
|
||
|
||
Icb = IcbContext->Current.View;
|
||
|
||
// Walk across the extent
|
||
do {
|
||
switch (Icb->Destag.Ident) {
|
||
case DESTAG_ID_NSR_ICBIND:
|
||
UdfVerifyDescriptor( IrpContext, &Icb->Destag, DESTAG_ID_NSR_ICBIND, sizeof( ICBIND ), Lbn, FALSE );
|
||
|
||
// Go to the next extent if this indirect Icb actually points to something.
|
||
if (Icb->Icb.Length.Type == NSRLENGTH_TYPE_RECORDED) {
|
||
// If we are in the last entry of the Icb extent, we may tail recurse. This
|
||
// is very important for strategy 4096, which is a linked list of extents
|
||
// of depth equal to the number of times the direct Icb had to be re-recorded.
|
||
|
||
UdfMapMetadataView( IrpContext,
|
||
&IcbContext->Current,
|
||
Vcb,
|
||
Icb->Icb.Start.Partition,
|
||
Icb->Icb.Start.Lbn,
|
||
Icb->Icb.Length.Length );
|
||
|
||
if (Length != BlockSize( Vcb )) {
|
||
// We have to give up on this if we're going too deep.
|
||
if (Recurse == 0) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
UdfLookupActiveIcbInExtent( IrpContext, IcbContext, Recurse );
|
||
|
||
// Need to remap the extent we were working on.
|
||
UdfMapMetadataView( IrpContext, &IcbContext->Current, Vcb, Partition, Lbn, Length );
|
||
} else {
|
||
// Tail recursion was possible so adjust our pointers and restart the scan.
|
||
Partition = IcbContext->Current.Partition;
|
||
Lbn = IcbContext->Current.Lbn;
|
||
Length = IcbContext->Current.Length;
|
||
|
||
Icb = IcbContext->Current.View;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
break;
|
||
case DESTAG_ID_NSR_ICBTRM:
|
||
UdfVerifyDescriptor( IrpContext, &Icb->Destag, DESTAG_ID_NSR_ICBTRM, sizeof( ICBTRM ), Lbn, FALSE );
|
||
|
||
// Terminate the current extent.
|
||
return;
|
||
break;
|
||
case DESTAG_ID_NOTSPEC:
|
||
// Perhaps this is an unrecorded sector. Treat this as terminating the current extent.
|
||
return;
|
||
break;
|
||
default:
|
||
// This is a data-full Icb. It must be of the expected type.
|
||
if (Icb->Destag.Ident != IcbContext->IcbType) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
// Since direct entries are of variable size, we must allow up to a block's worth of data.
|
||
UdfVerifyDescriptor( IrpContext,
|
||
&Icb->Destag,
|
||
Icb->Destag.Ident,
|
||
BlockSize( Vcb ),
|
||
Lbn,
|
||
FALSE );
|
||
|
||
// We perform an in-order traversal of the hierarchy. This is important since
|
||
// it means no tricks are neccesary to figure out the rightmost direct Icb -
|
||
// always stash the last one we see.
|
||
|
||
// Map this logical block into the active slot. We know that a direct entry
|
||
// must fit in a single logical block.
|
||
|
||
UdfMapMetadataView( IrpContext,
|
||
&IcbContext->Active,
|
||
Vcb,
|
||
Partition,
|
||
Lbn,
|
||
BlockSize( Vcb ) );
|
||
|
||
break;
|
||
}
|
||
|
||
// Advance our pointer set.
|
||
Lbn++;
|
||
Length -= BlockSize( Vcb );
|
||
|
||
Icb = Add2Ptr( Icb, BlockSize( Vcb ), PVOID );
|
||
} while (Length);
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
VOID UdfInitializeAllocationContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PALLOC_ENUM_CONTEXT AllocContext,
|
||
IN PICB_SEARCH_CONTEXT IcbContext
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initializes a walk of the allocation descriptors for an ICB which has already
|
||
been found. The first allocation descriptor will be avaliable after the call.
|
||
Arguments:
|
||
AllocContext - Allocation enumeration context to use
|
||
IcbContext - Elaborated ICB search context for the ICB to enumerate
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PICBFILE Icb;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT( IcbContext->Active.View );
|
||
|
||
AllocContext->IcbContext = IcbContext;
|
||
|
||
// Figure out what kind of descriptors will be here.
|
||
Icb = IcbContext->Active.View;
|
||
AllocContext->AllocType = FlagOn( Icb->Icbtag.Flags, ICBTAG_F_ALLOC_MASK );
|
||
|
||
// We are done if this is actually immediate data.
|
||
if (AllocContext->AllocType == ICBTAG_F_ALLOC_IMMEDIATE) {
|
||
return;
|
||
}
|
||
|
||
// The initial chunk of allocation descriptors is inline with the ICB and
|
||
// does not contain an Allocation Extent Descriptor.
|
||
AllocContext->Alloc = Add2Ptr( Icb->EAs, Icb->EALength, PVOID );
|
||
AllocContext->Remaining = Icb->AllocLength;
|
||
|
||
ASSERT( LongOffsetPtr( AllocContext->Alloc ) == 0 );
|
||
|
||
// Check that an integral number of the appropriate allocation descriptors fit in
|
||
// this extent and that the extent is not composed of extended allocation
|
||
// descriptors, which are illegal on UDF.
|
||
|
||
// If the common post-processing fails, we probably did not find any allocation
|
||
// descriptors (case of nothing but continuation). This is likewise bad.
|
||
|
||
if (AllocContext->Remaining == 0 ||
|
||
AllocContext->Remaining % ISOAllocationDescriptorSize( AllocContext->AllocType ) ||
|
||
AllocContext->AllocType == ICBTAG_F_ALLOC_EXTENDED ||
|
||
!UdfGetNextAllocationPostProcessing( IrpContext, AllocContext )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
BOOLEAN UdfGetNextAllocation (IN PIRP_CONTEXT IrpContext, IN PALLOC_ENUM_CONTEXT AllocContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine retrieves the next logical allocation descriptor given an enumeration context.
|
||
Arguments:
|
||
AllocContext - Context to advance to the next descriptor
|
||
Return Value:
|
||
BOOLEAN - TRUE if one is found, FALSE if the enumeration is complete.
|
||
This routine will raise if malformation is discovered.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check inputs
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
AllocContext->Remaining -= ISOAllocationDescriptorSize( AllocContext->AllocType );
|
||
AllocContext->Alloc = Add2Ptr( AllocContext->Alloc, ISOAllocationDescriptorSize( AllocContext->AllocType ), PVOID );
|
||
|
||
return UdfGetNextAllocationPostProcessing( IrpContext, AllocContext );
|
||
}
|
||
|
||
|
||
BOOLEAN UdfGetNextAllocationPostProcessing (IN PIRP_CONTEXT IrpContext, IN PALLOC_ENUM_CONTEXT AllocContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine retrieves the next logical allocation descriptor given an enumeration context.
|
||
Arguments:
|
||
AllocContext - Context to advance to the next descriptor
|
||
Return Value:
|
||
BOOLEAN - TRUE if one is found, FALSE if the enumeration is complete.
|
||
This routine will raise if malformation is discovered.
|
||
--*/
|
||
{
|
||
PAD_GENERIC GenericAd;
|
||
PNSR_ALLOC AllocDesc;
|
||
PVCB Vcb = AllocContext->IcbContext->Vcb;
|
||
|
||
// There are three ways to reach the end of the current block of allocation
|
||
// descriptors, per ISO 13346 4/12:
|
||
|
||
// reach the end of the field (kept track of in the Remaining bytes)
|
||
// reach an allocation descriptor with an extent length of zero
|
||
// reach a continuation extent descriptor
|
||
|
||
// We are done in the first two cases.
|
||
if (AllocContext->Remaining == 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
while (TRUE) {
|
||
GenericAd = AllocContext->Alloc;
|
||
if (GenericAd->Length.Length == 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Check if this descriptor is a pointer to another extent of descriptors.
|
||
if (GenericAd->Length.Type != NSRLENGTH_TYPE_CONTINUATION) {
|
||
break;
|
||
}
|
||
|
||
// UDF allocation extents are restricted to a single logical block.
|
||
if (GenericAd->Length.Length > BlockSize( Vcb )) {
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
UdfMapMetadataView( IrpContext,
|
||
&AllocContext->IcbContext->Current,
|
||
Vcb,
|
||
UdfGetPartitionOfCurrentAllocation( AllocContext ),
|
||
GenericAd->Start,
|
||
BlockSize( Vcb ) );
|
||
|
||
// Now check that the allocation descriptor is valid.
|
||
AllocDesc = (PNSR_ALLOC) AllocContext->IcbContext->Current.View;
|
||
|
||
UdfVerifyDescriptor( IrpContext,
|
||
&AllocDesc->Destag,
|
||
DESTAG_ID_NSR_ALLOC,
|
||
BlockSize( Vcb ),
|
||
AllocContext->IcbContext->Current.Lbn,
|
||
FALSE );
|
||
|
||
// Note that a full logical block is mapped, but only the claimed number of bytes are valid.
|
||
AllocContext->Remaining = AllocDesc->AllocLen;
|
||
AllocContext->Alloc = Add2Ptr( AllocContext->IcbContext->Current.View, sizeof( NSR_ALLOC ), PVOID );
|
||
|
||
// Check that the size is sane and that an integral number of the appropriate
|
||
// allocation descriptors fit in this extent.
|
||
if (AllocContext->Remaining == 0 ||
|
||
AllocContext->Remaining > BlockSize( Vcb ) - sizeof( NSR_ALLOC ) ||
|
||
AllocContext->Remaining % ISOAllocationDescriptorSize( AllocContext->AllocType )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
PFCB_NONPAGED UdfCreateFcbNonPaged (IN PIRP_CONTEXT IrpContext)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to create and initialize the non-paged portion of an Fcb.
|
||
Return Value:
|
||
PFCB_NONPAGED - Pointer to the created nonpaged Fcb. NULL if not created.
|
||
--*/
|
||
{
|
||
PFCB_NONPAGED FcbNonpaged;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Allocate the non-paged pool and initialize the various synchronization objects.
|
||
FcbNonpaged = UdfAllocateFcbNonpaged( IrpContext );
|
||
|
||
RtlZeroMemory( FcbNonpaged, sizeof( FCB_NONPAGED ));
|
||
|
||
FcbNonpaged->NodeTypeCode = UDFS_NTC_FCB_NONPAGED;
|
||
FcbNonpaged->NodeByteSize = sizeof( FCB_NONPAGED );
|
||
|
||
ExInitializeResource( &FcbNonpaged->FcbResource );
|
||
ExInitializeFastMutex( &FcbNonpaged->FcbMutex );
|
||
|
||
return FcbNonpaged;
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
VOID UdfDeleteFcbNonpaged (IN PIRP_CONTEXT IrpContext, IN PFCB_NONPAGED FcbNonpaged)
|
||
/*++
|
||
Routine Description:
|
||
This routine is called to cleanup the non-paged portion of an Fcb.
|
||
Arguments:
|
||
FcbNonpaged - Structure to clean up.
|
||
Return Value:
|
||
None
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExDeleteResource( &FcbNonpaged->FcbResource );
|
||
|
||
UdfDeallocateFcbNonpaged( IrpContext, FcbNonpaged );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
RTL_GENERIC_COMPARE_RESULTS UdfFcbTableCompare (IN PRTL_GENERIC_TABLE Table, IN PVOID id1, IN PVOID id2)
|
||
/*++
|
||
Routine Description:
|
||
This routine is the Udfs compare routine called by the generic table package.
|
||
If will compare the two File Id values and return a comparison result.
|
||
Arguments:
|
||
Table - This is the table being searched.
|
||
id1 - First key value.
|
||
id2 - Second key value.
|
||
Return Value:
|
||
RTL_GENERIC_COMPARE_RESULTS - The results of comparing the two input structures
|
||
--*/
|
||
{
|
||
FILE_ID Id1, Id2;
|
||
PAGED_CODE();
|
||
|
||
Id1 = *((FILE_ID UNALIGNED *) id1);
|
||
Id2 = *((FILE_ID UNALIGNED *) id2);
|
||
|
||
if (Id1.QuadPart < Id2.QuadPart) {
|
||
return GenericLessThan;
|
||
} else if (Id1.QuadPart > Id2.QuadPart) {
|
||
return GenericGreaterThan;
|
||
} else {
|
||
return GenericEqual;
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER( Table );
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
PVOID UdfAllocateTable (IN PRTL_GENERIC_TABLE Table, IN CLONG ByteSize)
|
||
/*++
|
||
Routine Description:
|
||
This is a generic table support routine to allocate memory
|
||
Arguments:
|
||
Table - Supplies the generic table being used
|
||
ByteSize - Supplies the number of bytes to allocate
|
||
Return Value:
|
||
PVOID - Returns a pointer to the allocated data
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
return( FsRtlAllocatePoolWithTag( UdfPagedPool, ByteSize, TAG_GENERIC_TABLE ));
|
||
}
|
||
|
||
|
||
// Local support routine
|
||
|
||
|
||
VOID UdfDeallocateTable (IN PRTL_GENERIC_TABLE Table, IN PVOID Buffer)
|
||
/*++
|
||
Routine Description:
|
||
This is a generic table support routine that deallocates memory
|
||
Arguments:
|
||
Table - Supplies the generic table being used
|
||
Buffer - Supplies the buffer being deallocated
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExFreePool( Buffer );
|
||
|
||
return;
|
||
UNREFERENCED_PARAMETER( Table );
|
||
}
|