mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 03:48:17 +00:00
2850 lines
66 KiB
C
2850 lines
66 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
StrucSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Cdfs in-memory data structure manipulation
|
||
routines
|
||
|
||
|
||
--*/
|
||
|
||
#include "cdprocs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (CDFS_BUG_CHECK_STRUCSUP)
|
||
|
||
//
|
||
// Local macros
|
||
//
|
||
|
||
//
|
||
// PFCB
|
||
// CdAllocateFcbData (
|
||
// _In_ PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateFcbData (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _Inout_ PFCB Fcb
|
||
// );
|
||
//
|
||
// PFCB
|
||
// CdAllocateFcbIndex (
|
||
// _In_ PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateFcbIndex (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _Inout_ PFCB Fcb
|
||
// );
|
||
//
|
||
// PFCB_NONPAGED
|
||
// CdAllocateFcbNonpaged (
|
||
// _In_ PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateFcbNonpaged (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _Inout_ PFCB_NONPAGED FcbNonpaged
|
||
// );
|
||
//
|
||
// PCCB
|
||
// CdAllocateCcb (
|
||
// _In_ PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateCcb (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _Inout_ PCCB Ccb
|
||
// );
|
||
//
|
||
|
||
#define CdAllocateFcbData(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_DATA, TAG_FCB_DATA )
|
||
|
||
#define CdDeallocateFcbData(IC,F) \
|
||
CdFreePool( &(F) )
|
||
|
||
#define CdAllocateFcbIndex(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_INDEX, TAG_FCB_INDEX )
|
||
|
||
#define CdDeallocateFcbIndex(IC,F) \
|
||
CdFreePool( &(F) )
|
||
|
||
#define CdAllocateFcbNonpaged(IC) \
|
||
ExAllocatePoolWithTag( CdNonPagedPool, sizeof( FCB_NONPAGED ), TAG_FCB_NONPAGED )
|
||
|
||
#define CdDeallocateFcbNonpaged(IC,FNP) \
|
||
CdFreePool( &(FNP) )
|
||
|
||
#define CdAllocateCcb(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, sizeof( CCB ), TAG_CCB )
|
||
|
||
#define CdDeallocateCcb(IC,C) \
|
||
CdFreePool( &(C) )
|
||
|
||
//
|
||
// Local structures
|
||
//
|
||
|
||
typedef struct _FCB_TABLE_ELEMENT {
|
||
|
||
FILE_ID FileId;
|
||
PFCB Fcb;
|
||
|
||
} FCB_TABLE_ELEMENT, *PFCB_TABLE_ELEMENT;
|
||
|
||
//
|
||
// Local macros
|
||
//
|
||
|
||
//
|
||
// VOID
|
||
// CdInsertFcbTable (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _In_ PFCB Fcb
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeleteFcbTable (
|
||
// _In_ PIRP_CONTEXT IrpContext,
|
||
// _In_ PFCB Fcb
|
||
// );
|
||
//
|
||
|
||
|
||
#define CdInsertFcbTable(IC,F) { \
|
||
FCB_TABLE_ELEMENT _Key; \
|
||
_Key.Fcb = (F); \
|
||
_Key.FileId = (F)->FileId; \
|
||
RtlInsertElementGenericTable( &(F)->Vcb->FcbTable, \
|
||
&_Key, \
|
||
sizeof( FCB_TABLE_ELEMENT ), \
|
||
NULL ); \
|
||
}
|
||
|
||
#define CdDeleteFcbTable(IC,F) { \
|
||
FCB_TABLE_ELEMENT _Key; \
|
||
_Key.FileId = (F)->FileId; \
|
||
RtlDeleteElementGenericTable( &(F)->Vcb->FcbTable, &_Key ); \
|
||
}
|
||
|
||
//
|
||
// Local support routines
|
||
//
|
||
|
||
VOID
|
||
CdDeleteFcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB Fcb
|
||
);
|
||
|
||
PFCB_NONPAGED
|
||
CdCreateFcbNonpaged (
|
||
_In_ PIRP_CONTEXT IrpContext
|
||
);
|
||
|
||
VOID
|
||
CdDeleteFcbNonpaged (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB_NONPAGED FcbNonpaged
|
||
);
|
||
|
||
// Inform prefast that this is a compare routine.
|
||
RTL_GENERIC_COMPARE_ROUTINE CdFcbTableCompare;
|
||
|
||
RTL_GENERIC_COMPARE_RESULTS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdFcbTableCompare (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ PVOID Fid1,
|
||
_In_ PVOID Fid2
|
||
);
|
||
|
||
// Inform prefast that this is an alloc reoutine.
|
||
RTL_GENERIC_ALLOCATE_ROUTINE CdAllocateFcbTable;
|
||
|
||
PVOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdAllocateFcbTable (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ CLONG ByteSize
|
||
);
|
||
|
||
// Inform prefast that this is a free reoutine.
|
||
RTL_GENERIC_FREE_ROUTINE CdDeallocateFcbTable;
|
||
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdDeallocateFcbTable (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
|
||
);
|
||
|
||
ULONG
|
||
CdTocSerial (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PCDROM_TOC_LARGE CdromToc
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, CdAllocateFcbTable)
|
||
#pragma alloc_text(PAGE, CdCleanupIrpContext)
|
||
#pragma alloc_text(PAGE, CdCreateCcb)
|
||
#pragma alloc_text(PAGE, CdCreateFcb)
|
||
#pragma alloc_text(PAGE, CdCreateFcbNonpaged)
|
||
#pragma alloc_text(PAGE, CdCreateFileLock)
|
||
#pragma alloc_text(PAGE, CdCreateIrpContext)
|
||
#pragma alloc_text(PAGE, CdDeallocateFcbTable)
|
||
#pragma alloc_text(PAGE, CdDeleteCcb)
|
||
#pragma alloc_text(PAGE, CdDeleteFcb)
|
||
#pragma alloc_text(PAGE, CdDeleteFcbNonpaged)
|
||
#pragma alloc_text(PAGE, CdDeleteFileLock)
|
||
#pragma alloc_text(PAGE, CdDeleteVcb)
|
||
#pragma alloc_text(PAGE, CdFcbTableCompare)
|
||
#pragma alloc_text(PAGE, CdGetNextFcb)
|
||
#pragma alloc_text(PAGE, CdInitializeFcbFromFileContext)
|
||
#pragma alloc_text(PAGE, CdInitializeFcbFromPathEntry)
|
||
#pragma alloc_text(PAGE, CdInitializeStackIrpContext)
|
||
#pragma alloc_text(PAGE, CdInitializeVcb)
|
||
#pragma alloc_text(PAGE, CdLookupFcbTable)
|
||
#pragma alloc_text(PAGE, CdProcessToc)
|
||
#pragma alloc_text(PAGE, CdTeardownStructures)
|
||
#pragma alloc_text(PAGE, CdTocSerial)
|
||
#pragma alloc_text(PAGE, CdUpdateVcbFromVolDescriptor)
|
||
#endif
|
||
|
||
//
|
||
// Some static names for volume streams
|
||
//
|
||
|
||
UNICODE_STRING CdInternalStreamNames[] = {
|
||
{ 24, 24, L"$PATH_TABLE$"},
|
||
{ 2, 2, L"\\"}
|
||
};
|
||
|
||
|
||
VOID
|
||
CdInitializeVcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ PVCB Vcb,
|
||
_In_ __drv_aliasesMem PDEVICE_OBJECT TargetDeviceObject,
|
||
_In_ __drv_aliasesMem PVPB Vpb,
|
||
_In_ __drv_aliasesMem PCDROM_TOC_LARGE CdromToc,
|
||
_In_ ULONG TocLength,
|
||
_In_ ULONG TocTrackCount,
|
||
_In_ ULONG TocDiskFlags,
|
||
_In_ ULONG BlockFactor,
|
||
_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.
|
||
|
||
CdromToc - Buffer to hold table of contents. NULL if TOC command not
|
||
supported.
|
||
|
||
TocLength - Byte count length of TOC. We use this as the TOC length to
|
||
return on a user query.
|
||
|
||
TocTrackCount - Count of tracks in TOC. Used to create pseudo files for
|
||
audio disks.
|
||
|
||
TocDiskFlags - Flag field to indicate the type of tracks on the disk.
|
||
|
||
BlockFactor - Used to decode any multi-session information.
|
||
|
||
MediaChangeCount - Initial media change count of the target device
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// 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 = CDFS_NTC_VCB;
|
||
Vcb->NodeByteSize = sizeof( VCB );
|
||
|
||
//
|
||
// Initialize the DirNotify structs. FsRtlNotifyInitializeSync can raise.
|
||
//
|
||
|
||
InitializeListHead( &Vcb->DirNotifyList );
|
||
FsRtlNotifyInitializeSync( &Vcb->NotifySync );
|
||
|
||
//
|
||
// Pick up a VPB right now so we know we can pull this filesystem stack
|
||
// off of the storage stack on demand. This can raise - if it does,
|
||
// uninitialize the notify structures before returning.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
Vcb->SwapVpb = FsRtlAllocatePoolWithTag( CdNonPagedPool,
|
||
sizeof( VPB ),
|
||
TAG_VPB );
|
||
}
|
||
_SEH2_FINALLY {
|
||
|
||
if (_SEH2_AbnormalTermination()) {
|
||
|
||
FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
|
||
}
|
||
} _SEH2_END;
|
||
|
||
//
|
||
// Nothing beyond this point should raise.
|
||
//
|
||
|
||
RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) );
|
||
|
||
//
|
||
// Initialize the resource variable for the Vcb and files.
|
||
//
|
||
|
||
ExInitializeResourceLite( &Vcb->VcbResource );
|
||
ExInitializeResourceLite( &Vcb->FileResource );
|
||
ExInitializeFastMutex( &Vcb->VcbMutex );
|
||
|
||
//
|
||
// Insert this Vcb record on the CdData.VcbQueue.
|
||
//
|
||
|
||
InsertHeadList( &CdData.VcbQueue, &Vcb->VcbLinks );
|
||
|
||
//
|
||
// Set the Target Device Object and Vpb fields, referencing the
|
||
// Target device for the mount.
|
||
//
|
||
|
||
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) CdFcbTableCompare,
|
||
(PRTL_GENERIC_ALLOCATE_ROUTINE) CdAllocateFcbTable,
|
||
(PRTL_GENERIC_FREE_ROUTINE) CdDeallocateFcbTable,
|
||
NULL );
|
||
|
||
//
|
||
// Show that we have a mount in progress.
|
||
//
|
||
|
||
CdUpdateVcbCondition( Vcb, 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->VcbReference = 1 + CDFS_RESIDUAL_REFERENCE;
|
||
|
||
//
|
||
// Update the TOC information in the Vcb.
|
||
//
|
||
|
||
Vcb->CdromToc = CdromToc;
|
||
Vcb->TocLength = TocLength;
|
||
Vcb->TrackCount = TocTrackCount;
|
||
Vcb->DiskFlags = TocDiskFlags;
|
||
|
||
//
|
||
// If this disk contains audio tracks only then set the audio flag.
|
||
//
|
||
|
||
if (TocDiskFlags == CDROM_DISK_AUDIO_TRACK) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_AUDIO_DISK | VCB_STATE_CDXA );
|
||
}
|
||
|
||
//
|
||
// Set the block factor.
|
||
//
|
||
|
||
Vcb->BlockFactor = BlockFactor;
|
||
|
||
//
|
||
// Set the media change count on the device
|
||
//
|
||
|
||
CdUpdateMediaChangeCount( Vcb, MediaChangeCount);
|
||
}
|
||
|
||
|
||
VOID
|
||
CdUpdateVcbFromVolDescriptor (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ PVCB Vcb,
|
||
_In_reads_bytes_opt_(SECTOR_SIZE) PCHAR RawIsoVd
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to perform the final initialization of a Vcb from the
|
||
volume descriptor on the disk.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume being mounted. We have already set the flags for the
|
||
type of descriptor.
|
||
|
||
RawIsoVd - If specified this is the volume descriptor to use to mount the
|
||
volume. Not specified for a raw disk.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG StartingBlock;
|
||
ULONG ByteCount;
|
||
|
||
LONGLONG FileId = 0;
|
||
|
||
PRAW_DIRENT RawDirent;
|
||
PATH_ENTRY PathEntry;
|
||
PCD_MCB_ENTRY McbEntry;
|
||
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Copy the block size and compute the various block masks.
|
||
// Block size must not be larger than the sector size. We will
|
||
// use a default of the CD physical sector size if we are not
|
||
// on a data-full disc.
|
||
//
|
||
// This must always be set.
|
||
//
|
||
|
||
Vcb->BlockSize = ( ARGUMENT_PRESENT( RawIsoVd ) ?
|
||
CdRvdBlkSz( RawIsoVd, Vcb->VcbState ) :
|
||
SECTOR_SIZE );
|
||
|
||
//
|
||
// We no longer accept media where blocksize != sector size.
|
||
//
|
||
|
||
if (Vcb->BlockSize != SECTOR_SIZE) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
Vcb->BlocksPerSector = SECTOR_SIZE / Vcb->BlockSize;
|
||
Vcb->BlockMask = Vcb->BlockSize - 1;
|
||
Vcb->BlockInverseMask = ~Vcb->BlockMask;
|
||
|
||
Vcb->BlockToSectorShift = 0;
|
||
Vcb->BlockToByteShift = SECTOR_SHIFT;
|
||
|
||
//
|
||
// If there is a volume descriptor then do the internal Fcb's and
|
||
// other Vcb fields.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( RawIsoVd )) {
|
||
|
||
//
|
||
// Create the path table Fcb and refererence it and the Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->PathTableFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_PATH_TABLE,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->PathTableFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Compute the stream offset and size of this path table.
|
||
//
|
||
|
||
StartingBlock = CdRvdPtLoc( RawIsoVd, Vcb->VcbState );
|
||
|
||
ByteCount = CdRvdPtSz( RawIsoVd, Vcb->VcbState );
|
||
|
||
Vcb->PathTableFcb->StreamOffset = BytesFromBlocks( Vcb,
|
||
SectorBlockOffset( Vcb, StartingBlock ));
|
||
|
||
Vcb->PathTableFcb->FileSize.QuadPart = (LONGLONG) (Vcb->PathTableFcb->StreamOffset +
|
||
ByteCount);
|
||
|
||
Vcb->PathTableFcb->ValidDataLength.QuadPart = Vcb->PathTableFcb->FileSize.QuadPart;
|
||
|
||
Vcb->PathTableFcb->AllocationSize.QuadPart = LlSectorAlign( Vcb->PathTableFcb->FileSize.QuadPart );
|
||
|
||
//
|
||
// Now add the mapping information.
|
||
//
|
||
|
||
CdLockFcb( IrpContext, Vcb->PathTableFcb );
|
||
|
||
CdAddInitialAllocation( IrpContext,
|
||
Vcb->PathTableFcb,
|
||
StartingBlock,
|
||
Vcb->PathTableFcb->AllocationSize.QuadPart );
|
||
|
||
CdUnlockFcb( IrpContext, Vcb->PathTableFcb );
|
||
|
||
//
|
||
// Point to the file resource.
|
||
//
|
||
|
||
Vcb->PathTableFcb->Resource = &Vcb->FileResource;
|
||
|
||
//
|
||
// Mark the Fcb as initialized and create the stream file for this.
|
||
//
|
||
|
||
SetFlag( Vcb->PathTableFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
CdCreateInternalStream( IrpContext, Vcb, Vcb->PathTableFcb, &CdInternalStreamNames[0]);
|
||
|
||
//
|
||
// Create the root index and reference it in the Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
Vcb->RootIndexFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_INDEX,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->RootIndexFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Create the File id by hand for this Fcb.
|
||
//
|
||
|
||
CdSetFidPathTableOffset( Vcb->RootIndexFcb->FileId, Vcb->PathTableFcb->StreamOffset );
|
||
CdFidSetDirectory( Vcb->RootIndexFcb->FileId );
|
||
|
||
//
|
||
// Create a pseudo path table entry so we can call the initialization
|
||
// routine for the directory.
|
||
//
|
||
|
||
RawDirent = (PRAW_DIRENT) CdRvdDirent( RawIsoVd, Vcb->VcbState );
|
||
|
||
CopyUchar4( &PathEntry.DiskOffset, RawDirent->FileLoc );
|
||
|
||
PathEntry.DiskOffset += RawDirent->XarLen;
|
||
PathEntry.Ordinal = 1;
|
||
PathEntry.PathTableOffset = Vcb->PathTableFcb->StreamOffset;
|
||
|
||
CdInitializeFcbFromPathEntry( IrpContext,
|
||
Vcb->RootIndexFcb,
|
||
NULL,
|
||
&PathEntry );
|
||
|
||
//
|
||
// Create the stream file for the root directory.
|
||
//
|
||
|
||
CdCreateInternalStream( IrpContext, Vcb, Vcb->RootIndexFcb, &CdInternalStreamNames[1] );
|
||
|
||
//
|
||
// Now do the volume dasd Fcb. Create this and reference it in the
|
||
// Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->VolumeDasdFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_DATA,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->VolumeDasdFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// The file size is the full disk.
|
||
//
|
||
|
||
StartingBlock = CdRvdVolSz( RawIsoVd, Vcb->VcbState );
|
||
|
||
Vcb->VolumeDasdFcb->FileSize.QuadPart = LlBytesFromBlocks( Vcb, StartingBlock );
|
||
|
||
Vcb->VolumeDasdFcb->AllocationSize.QuadPart =
|
||
Vcb->VolumeDasdFcb->ValidDataLength.QuadPart = Vcb->VolumeDasdFcb->FileSize.QuadPart;
|
||
|
||
//
|
||
// Now add the extent representing the volume 'by hand'.
|
||
//
|
||
|
||
CdLockFcb( IrpContext, Vcb->VolumeDasdFcb );
|
||
|
||
McbEntry = Vcb->VolumeDasdFcb->Mcb.McbArray;
|
||
|
||
McbEntry->FileOffset =
|
||
McbEntry->DiskOffset = 0;
|
||
|
||
McbEntry->ByteCount = Vcb->VolumeDasdFcb->AllocationSize.QuadPart;
|
||
|
||
McbEntry->DataBlockByteCount =
|
||
McbEntry->TotalBlockByteCount = McbEntry->ByteCount;
|
||
|
||
Vcb->VolumeDasdFcb->Mcb.CurrentEntryCount = 1;
|
||
|
||
CdUnlockFcb( IrpContext, Vcb->VolumeDasdFcb );
|
||
|
||
//
|
||
// Point to the file resource.
|
||
//
|
||
|
||
Vcb->VolumeDasdFcb->Resource = &Vcb->FileResource;
|
||
|
||
Vcb->VolumeDasdFcb->FileAttributes = FILE_ATTRIBUTE_READONLY;
|
||
|
||
//
|
||
// Mark the Fcb as initialized.
|
||
//
|
||
|
||
SetFlag( Vcb->VolumeDasdFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
//
|
||
// Check and see if this is an XA disk.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_ISO | VCB_STATE_JOLIET)
|
||
&& RtlEqualMemory( CdXaId,
|
||
Add2Ptr( RawIsoVd, 0x400, PCHAR ),
|
||
8 )) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_CDXA );
|
||
}
|
||
|
||
//
|
||
// If this is a music disk then we want to mock this disk to make it
|
||
// look like ISO disk. We will create a pseudo root directory in
|
||
// that case.
|
||
//
|
||
|
||
} else if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) {
|
||
|
||
ULONG RootDirectorySize;
|
||
|
||
//
|
||
// Create the path table Fcb and refererence it and the Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->PathTableFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_PATH_TABLE,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->PathTableFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// We only create a pseudo entry for the root.
|
||
//
|
||
|
||
Vcb->PathTableFcb->FileSize.QuadPart = (LONGLONG) (FIELD_OFFSET( RAW_PATH_ISO, DirId ) + 2);
|
||
|
||
Vcb->PathTableFcb->ValidDataLength.QuadPart = Vcb->PathTableFcb->FileSize.QuadPart;
|
||
|
||
Vcb->PathTableFcb->AllocationSize.QuadPart = LlSectorAlign( Vcb->PathTableFcb->FileSize.QuadPart );
|
||
|
||
//
|
||
// Point to the file resource.
|
||
//
|
||
|
||
Vcb->PathTableFcb->Resource = &Vcb->FileResource;
|
||
|
||
//
|
||
// Mark the Fcb as initialized and create the stream file for this.
|
||
//
|
||
|
||
SetFlag( Vcb->PathTableFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
CdCreateInternalStream( IrpContext, Vcb, Vcb->PathTableFcb, &CdInternalStreamNames[0]);
|
||
|
||
//
|
||
// Create the root index and reference it in the Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
Vcb->RootIndexFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_INDEX,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->RootIndexFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Create the File id by hand for this Fcb.
|
||
//
|
||
|
||
CdSetFidPathTableOffset( Vcb->RootIndexFcb->FileId, Vcb->PathTableFcb->StreamOffset );
|
||
CdFidSetDirectory( Vcb->RootIndexFcb->FileId );
|
||
|
||
//
|
||
// Create a pseudo path table entry so we can call the initialization
|
||
// routine for the directory.
|
||
//
|
||
|
||
RtlZeroMemory( &PathEntry, sizeof( PATH_ENTRY ));
|
||
|
||
|
||
PathEntry.Ordinal = 1;
|
||
PathEntry.PathTableOffset = Vcb->PathTableFcb->StreamOffset;
|
||
|
||
CdInitializeFcbFromPathEntry( IrpContext,
|
||
Vcb->RootIndexFcb,
|
||
NULL,
|
||
&PathEntry );
|
||
|
||
//
|
||
// Set the sizes by hand for this Fcb. It should have an entry for each track plus an
|
||
// entry for the root and parent.
|
||
//
|
||
|
||
RootDirectorySize = (Vcb->TrackCount + 2) * CdAudioDirentSize;
|
||
RootDirectorySize = SectorAlign( RootDirectorySize );
|
||
|
||
Vcb->RootIndexFcb->AllocationSize.QuadPart =
|
||
Vcb->RootIndexFcb->ValidDataLength.QuadPart =
|
||
Vcb->RootIndexFcb->FileSize.QuadPart = RootDirectorySize;
|
||
|
||
SetFlag( Vcb->RootIndexFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
//
|
||
// Create the stream file for the root directory.
|
||
//
|
||
|
||
CdCreateInternalStream( IrpContext, Vcb, Vcb->RootIndexFcb, &CdInternalStreamNames[1] );
|
||
|
||
//
|
||
// Now do the volume dasd Fcb. Create this and reference it in the
|
||
// Vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
Vcb->VolumeDasdFcb = CdCreateFcb( IrpContext,
|
||
*((PFILE_ID) &FileId),
|
||
CDFS_NTC_FCB_DATA,
|
||
NULL );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, Vcb->VolumeDasdFcb, 1, 1 );
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// We won't allow raw reads on this Fcb so leave the size at
|
||
// zero.
|
||
//
|
||
|
||
//
|
||
// Point to the file resource.
|
||
//
|
||
|
||
Vcb->VolumeDasdFcb->Resource = &Vcb->FileResource;
|
||
|
||
Vcb->VolumeDasdFcb->FileAttributes = FILE_ATTRIBUTE_READONLY;
|
||
|
||
//
|
||
// Mark the Fcb as initialized.
|
||
//
|
||
|
||
SetFlag( Vcb->VolumeDasdFcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
//
|
||
// We will store a hard-coded name in the Vpb and use the toc as
|
||
// the serial number.
|
||
//
|
||
|
||
Vcb->Vpb->VolumeLabelLength = CdAudioLabelLength;
|
||
|
||
RtlCopyMemory( Vcb->Vpb->VolumeLabel,
|
||
CdAudioLabel,
|
||
CdAudioLabelLength );
|
||
|
||
//
|
||
// Find the serial number for the audio disk.
|
||
//
|
||
|
||
Vcb->Vpb->SerialNumber = CdTocSerial( IrpContext, Vcb->CdromToc );
|
||
|
||
//
|
||
// Set the ISO bit so we know how to treat the names.
|
||
//
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_ISO );
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
if (UnlockVcb) { CdUnlockVcb( IrpContext, Vcb ); }
|
||
} _SEH2_END;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdDeleteVcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ 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_CDDATA;
|
||
ASSERT_EXCLUSIVE_VCB( Vcb );
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// Chuck the backpocket Vpb we kept just in case.
|
||
//
|
||
|
||
CdFreePool( &Vcb->SwapVpb );
|
||
|
||
//
|
||
// If there is a Vpb then we must delete it ourselves.
|
||
//
|
||
|
||
CdFreePool( &Vcb->Vpb );
|
||
|
||
//
|
||
// Dereference our target if we haven't already done so.
|
||
//
|
||
|
||
if (Vcb->TargetDeviceObject != NULL) {
|
||
|
||
ObDereferenceObject( Vcb->TargetDeviceObject );
|
||
}
|
||
|
||
//
|
||
// Delete the XA Sector and sector cache buffer if allocated.
|
||
//
|
||
|
||
CdFreePool( &Vcb->XASector );
|
||
CdFreePool( &Vcb->SectorCacheBuffer);
|
||
|
||
if (Vcb->SectorCacheIrp != NULL) {
|
||
|
||
IoFreeIrp( Vcb->SectorCacheIrp);
|
||
Vcb->SectorCacheIrp = NULL;
|
||
|
||
ExDeleteResourceLite( &Vcb->SectorCacheResource);
|
||
}
|
||
|
||
//
|
||
// Remove this entry from the global queue.
|
||
//
|
||
|
||
RemoveEntryList( &Vcb->VcbLinks );
|
||
|
||
//
|
||
// Delete the Vcb and File resources.
|
||
//
|
||
|
||
ExDeleteResourceLite( &Vcb->VcbResource );
|
||
ExDeleteResourceLite( &Vcb->FileResource );
|
||
|
||
//
|
||
// Delete the TOC if present.
|
||
//
|
||
|
||
CdFreePool( &Vcb->CdromToc );
|
||
|
||
//
|
||
// Uninitialize the notify structures.
|
||
//
|
||
|
||
if (Vcb->NotifySync != NULL) {
|
||
|
||
FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
|
||
}
|
||
|
||
//
|
||
// Now delete the volume device object.
|
||
//
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: __WARNING_BUFFER_UNDERFLOW, "This is ok, the Vcb is embedded in our volume device object, and that is what we are really deleting." )
|
||
#endif
|
||
IoDeleteDevice( (PDEVICE_OBJECT) CONTAINING_RECORD( Vcb,
|
||
VOLUME_DEVICE_OBJECT,
|
||
Vcb ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PFCB
|
||
CdCreateFcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ FILE_ID FileId,
|
||
_In_ NODE_TYPE_CODE NodeTypeCode,
|
||
_Out_opt_ PBOOLEAN FcbExisted
|
||
)
|
||
|
||
/*++
|
||
|
||
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 = CdLookupFcbTable( IrpContext, IrpContext->Vcb, FileId );
|
||
|
||
//
|
||
// If not then create the Fcb is requested by our caller.
|
||
//
|
||
|
||
if (NewFcb == NULL) {
|
||
|
||
//
|
||
// Allocate and initialize the structure depending on the
|
||
// type code.
|
||
//
|
||
|
||
switch (NodeTypeCode) {
|
||
|
||
case CDFS_NTC_FCB_PATH_TABLE:
|
||
case CDFS_NTC_FCB_INDEX:
|
||
|
||
NewFcb = CdAllocateFcbIndex( IrpContext );
|
||
|
||
RtlZeroMemory( NewFcb, SIZEOF_FCB_INDEX );
|
||
|
||
NewFcb->NodeByteSize = SIZEOF_FCB_INDEX;
|
||
|
||
InitializeListHead( &NewFcb->FcbQueue );
|
||
|
||
break;
|
||
|
||
case CDFS_NTC_FCB_DATA :
|
||
|
||
NewFcb = CdAllocateFcbData( IrpContext );
|
||
|
||
RtlZeroMemory( NewFcb, SIZEOF_FCB_DATA );
|
||
|
||
NewFcb->NodeByteSize = SIZEOF_FCB_DATA;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: __WARNING_USE_OTHER_FUNCTION, "This is a bug." )
|
||
#endif
|
||
CdBugCheck( 0, 0, 0 );
|
||
}
|
||
|
||
//
|
||
// Now do the common initialization.
|
||
//
|
||
|
||
NewFcb->NodeTypeCode = NodeTypeCode;
|
||
|
||
NewFcb->Vcb = IrpContext->Vcb;
|
||
NewFcb->FileId = FileId;
|
||
|
||
CdInitializeMcb( IrpContext, NewFcb );
|
||
|
||
//
|
||
// Now create the non-paged section object.
|
||
//
|
||
|
||
NewFcb->FcbNonpaged = CdCreateFcbNonpaged( IrpContext );
|
||
|
||
//
|
||
// Deallocate the Fcb and raise if the allocation failed.
|
||
//
|
||
|
||
if (NewFcb->FcbNonpaged == NULL) {
|
||
|
||
CdFreePool( &NewFcb );
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
*FcbExisted = FALSE;
|
||
|
||
//
|
||
// Initialize Advanced FCB Header fields
|
||
//
|
||
|
||
ExInitializeFastMutex( &NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex );
|
||
FsRtlSetupAdvancedHeader( &NewFcb->Header,
|
||
&NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex );
|
||
|
||
if (NodeTypeCode == CDFS_NTC_FCB_DATA) {
|
||
|
||
FsRtlInitializeOplock( CdGetFcbOplock(NewFcb) );
|
||
}
|
||
|
||
} else {
|
||
|
||
*FcbExisted = TRUE;
|
||
}
|
||
|
||
return NewFcb;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdInitializeFcbFromPathEntry (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ PFCB Fcb,
|
||
_In_opt_ PFCB ParentFcb,
|
||
_In_ PPATH_ENTRY PathEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize an Fcb for a directory from
|
||
the path entry. Since we only have a starting point for the directory,
|
||
not the length, we can only speculate on the sizes.
|
||
|
||
The general initialization is performed in CdCreateFcb.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Newly created Fcb for this stream.
|
||
|
||
ParentFcb - Parent Fcb for this stream. It may not be present.
|
||
|
||
PathEntry - PathEntry for this Fcb in the Path Table.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Fill in the Index specific fields of the Fcb.
|
||
//
|
||
|
||
Fcb->StreamOffset = BytesFromBlocks( Fcb->Vcb,
|
||
SectorBlockOffset( Fcb->Vcb, PathEntry->DiskOffset ));
|
||
|
||
Fcb->Ordinal = PathEntry->Ordinal;
|
||
|
||
//
|
||
// Initialize the common header in the Fcb. The node type is already
|
||
// present.
|
||
//
|
||
|
||
Fcb->Resource = &Fcb->Vcb->FileResource;
|
||
|
||
//
|
||
// Always set the sizes to one sector until we read the self-entry.
|
||
//
|
||
|
||
Fcb->AllocationSize.QuadPart =
|
||
Fcb->FileSize.QuadPart =
|
||
Fcb->ValidDataLength.QuadPart = SECTOR_SIZE;
|
||
|
||
CdAddInitialAllocation( IrpContext,
|
||
Fcb,
|
||
PathEntry->DiskOffset,
|
||
SECTOR_SIZE );
|
||
//
|
||
// State flags for this Fcb.
|
||
//
|
||
|
||
SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
|
||
|
||
//
|
||
// Link into the other in-memory structures and into the Fcb table.
|
||
//
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
Fcb->ParentFcb = ParentFcb;
|
||
|
||
InsertTailList( &ParentFcb->FcbQueue, &Fcb->FcbLinks );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, ParentFcb, 1, 1 );
|
||
}
|
||
|
||
CdInsertFcbTable( IrpContext, Fcb );
|
||
SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdInitializeFcbFromFileContext (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ PFCB Fcb,
|
||
_In_ PFCB ParentFcb,
|
||
_In_ PFILE_ENUM_CONTEXT FileContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize an Fcb for a file from
|
||
the file context. We have looked up all of the dirents for this
|
||
stream and have the full file size. We will load the all of the allocation
|
||
for the file into the Mcb now.
|
||
|
||
The general initialization is performed in CdCreateFcb.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Newly created Fcb for this stream.
|
||
|
||
ParentFcb - Parent Fcb for this stream.
|
||
|
||
FileContext - FileContext for the file.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent;
|
||
PCOMPOUND_DIRENT CurrentCompoundDirent;
|
||
|
||
LONGLONG CurrentFileOffset;
|
||
ULONG CurrentMcbEntryOffset;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
CdLockFcb( IrpContext, Fcb );
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Initialize the common header in the Fcb. The node type is already
|
||
// present.
|
||
//
|
||
|
||
Fcb->Resource = &IrpContext->Vcb->FileResource;
|
||
|
||
//
|
||
// Allocation occurs in block-sized units.
|
||
//
|
||
|
||
Fcb->FileSize.QuadPart =
|
||
Fcb->ValidDataLength.QuadPart = FileContext->FileSize;
|
||
|
||
Fcb->AllocationSize.QuadPart = LlBlockAlign( Fcb->Vcb, FileContext->FileSize );
|
||
|
||
//
|
||
// Set the flags from the dirent. We always start with the read-only bit.
|
||
//
|
||
|
||
SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_READONLY );
|
||
if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_HIDDEN )) {
|
||
|
||
SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
|
||
}
|
||
|
||
//
|
||
// Convert the time to NT time.
|
||
//
|
||
|
||
CdConvertCdTimeToNtTime( IrpContext,
|
||
ThisDirent->CdTime,
|
||
(PLARGE_INTEGER) &Fcb->CreationTime );
|
||
|
||
//
|
||
// Set the flag indicating the type of extent.
|
||
//
|
||
|
||
if (ThisDirent->ExtentType != Form1Data) {
|
||
|
||
if (ThisDirent->ExtentType == Mode2Form2Data) {
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE );
|
||
|
||
} else {
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_DA_FILE );
|
||
}
|
||
|
||
Fcb->XAAttributes = ThisDirent->XAAttributes;
|
||
Fcb->XAFileNumber = ThisDirent->XAFileNumber;
|
||
}
|
||
|
||
//
|
||
// Read through all of the dirents for the file until we find the last
|
||
// and add the allocation into the Mcb.
|
||
//
|
||
|
||
CurrentCompoundDirent = FileContext->InitialDirent;
|
||
CurrentFileOffset = 0;
|
||
CurrentMcbEntryOffset = 0;
|
||
|
||
while (TRUE) {
|
||
|
||
CdAddAllocationFromDirent( IrpContext,
|
||
Fcb,
|
||
CurrentMcbEntryOffset,
|
||
CurrentFileOffset,
|
||
&CurrentCompoundDirent->Dirent );
|
||
|
||
//
|
||
// Break out if we are at the last dirent.
|
||
//
|
||
|
||
if (!FlagOn( CurrentCompoundDirent->Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) {
|
||
|
||
break;
|
||
}
|
||
|
||
CurrentFileOffset += CurrentCompoundDirent->Dirent.DataLength;
|
||
CurrentMcbEntryOffset += 1;
|
||
|
||
//
|
||
// We better be able to find the next dirent.
|
||
//
|
||
|
||
if (!CdLookupNextDirent( IrpContext,
|
||
ParentFcb,
|
||
&CurrentCompoundDirent->DirContext,
|
||
&FileContext->CurrentDirent->DirContext )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
CurrentCompoundDirent = FileContext->CurrentDirent;
|
||
|
||
CdUpdateDirentFromRawDirent( IrpContext,
|
||
ParentFcb,
|
||
&CurrentCompoundDirent->DirContext,
|
||
&CurrentCompoundDirent->Dirent );
|
||
}
|
||
|
||
//
|
||
// Show that the Fcb is initialized.
|
||
//
|
||
|
||
SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED );
|
||
|
||
//
|
||
// Link into the other in-memory structures and into the Fcb table.
|
||
//
|
||
|
||
Fcb->ParentFcb = ParentFcb;
|
||
|
||
InsertTailList( &ParentFcb->FcbQueue, &Fcb->FcbLinks );
|
||
|
||
CdIncrementReferenceCounts( IrpContext, ParentFcb, 1, 1 );
|
||
|
||
CdInsertFcbTable( IrpContext, Fcb );
|
||
SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
CdUnlockFcb( IrpContext, Fcb );
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PCCB
|
||
CdCreateCcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB Fcb,
|
||
_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.
|
||
|
||
Flags - User flags to set in this Ccb.
|
||
|
||
Return Value:
|
||
|
||
PCCB - Pointer to the created Ccb.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCCB NewCcb;
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// Allocate and initialize the structure.
|
||
//
|
||
|
||
NewCcb = CdAllocateCcb( IrpContext );
|
||
|
||
RtlZeroMemory( NewCcb, sizeof( CCB ));
|
||
|
||
//
|
||
// Set the proper node type code and node byte size
|
||
//
|
||
|
||
NewCcb->NodeTypeCode = CDFS_NTC_CCB;
|
||
NewCcb->NodeByteSize = sizeof( CCB );
|
||
|
||
//
|
||
// Set the initial value for the flags and Fcb
|
||
//
|
||
|
||
NewCcb->Flags = Flags;
|
||
NewCcb->Fcb = Fcb;
|
||
|
||
return NewCcb;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdDeleteCcb (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ __drv_freesMem( Pool ) 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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
if (Ccb->SearchExpression.FileName.Buffer != NULL) {
|
||
|
||
CdFreePool( &Ccb->SearchExpression.FileName.Buffer );
|
||
}
|
||
|
||
CdDeallocateCcb( IrpContext, Ccb );
|
||
return;
|
||
}
|
||
|
||
|
||
_When_(RaiseOnError || return, _At_(Fcb->FileLock, _Post_notnull_))
|
||
_When_(RaiseOnError, _At_(IrpContext, _Pre_notnull_))
|
||
BOOLEAN
|
||
CdCreateFileLock (
|
||
_In_opt_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ 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.
|
||
//
|
||
|
||
CdLockFcb( IrpContext, Fcb );
|
||
|
||
if (Fcb->FileLock != NULL) {
|
||
|
||
CdUnlockFcb( IrpContext, Fcb );
|
||
return TRUE;
|
||
}
|
||
|
||
Fcb->FileLock = FileLock =
|
||
FsRtlAllocateFileLock( NULL, NULL );
|
||
|
||
CdUnlockFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// Return or raise as appropriate.
|
||
//
|
||
|
||
if (FileLock == NULL) {
|
||
|
||
if (RaiseOnError) {
|
||
|
||
NT_ASSERT( ARGUMENT_PRESENT( IrpContext ));
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
Result = FALSE;
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
_Ret_valid_ PIRP_CONTEXT
|
||
CdCreateIrpContext (
|
||
_In_ PIRP Irp,
|
||
_In_ BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize an IrpContext for the current
|
||
CDFS 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 );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// 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).
|
||
//
|
||
|
||
#ifndef __REACTOS__
|
||
if (IrpSp->DeviceObject == CdData.FileSystemDeviceObject) {
|
||
#else
|
||
if (IrpSp->DeviceObject == CdData.FileSystemDeviceObject ||
|
||
IrpSp->DeviceObject == CdData.HddFileSystemDeviceObject) {
|
||
#endif
|
||
|
||
if (IrpSp->FileObject != NULL &&
|
||
IrpSp->MajorFunction != IRP_MJ_CREATE &&
|
||
IrpSp->MajorFunction != IRP_MJ_CLEANUP &&
|
||
IrpSp->MajorFunction != IRP_MJ_CLOSE) {
|
||
|
||
ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST );
|
||
}
|
||
|
||
NT_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 );
|
||
}
|
||
|
||
//
|
||
// Look in our lookaside list for an IrpContext.
|
||
//
|
||
|
||
if (CdData.IrpContextDepth) {
|
||
|
||
CdLockCdData();
|
||
NewIrpContext = (PIRP_CONTEXT) PopEntryList( &CdData.IrpContextList );
|
||
if (NewIrpContext != NULL) {
|
||
|
||
CdData.IrpContextDepth--;
|
||
}
|
||
|
||
CdUnlockCdData();
|
||
}
|
||
|
||
if (NewIrpContext == NULL) {
|
||
|
||
//
|
||
// We didn't get it from our private list so allocate it from pool.
|
||
//
|
||
|
||
NewIrpContext = FsRtlAllocatePoolWithTag( CdNonPagedPool, sizeof( IRP_CONTEXT ), TAG_IRP_CONTEXT );
|
||
}
|
||
|
||
RtlZeroMemory( NewIrpContext, sizeof( IRP_CONTEXT ));
|
||
|
||
//
|
||
// Set the proper node type code and node byte size
|
||
//
|
||
|
||
NewIrpContext->NodeTypeCode = CDFS_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;
|
||
}
|
||
|
||
//
|
||
// Locate the volume device object and Vcb that we are trying to access.
|
||
// This may be our filesystem device object. In that case don't initialize
|
||
// the Vcb field.
|
||
//
|
||
|
||
#ifndef __REACTOS__
|
||
if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) {
|
||
#else
|
||
if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject &&
|
||
IrpSp->DeviceObject != CdData.HddFileSystemDeviceObject) {
|
||
#endif
|
||
|
||
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
|
||
CdCleanupIrpContext (
|
||
_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();
|
||
|
||
//
|
||
// 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 CDFS context then we need to
|
||
// restore the top level thread context.
|
||
//
|
||
|
||
if (IrpContext->ThreadContext != NULL) {
|
||
|
||
CdRestoreThreadContext( IrpContext );
|
||
}
|
||
|
||
//
|
||
// Deallocate the Io context if allocated.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) {
|
||
|
||
CdFreeIoContext( IrpContext->IoContext );
|
||
}
|
||
|
||
//
|
||
// Deallocate the IrpContext if not from the stack.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ON_STACK )) {
|
||
|
||
if (CdData.IrpContextDepth < CdData.IrpContextMaxDepth) {
|
||
|
||
CdLockCdData();
|
||
|
||
PushEntryList( &CdData.IrpContextList, (PSINGLE_LIST_ENTRY) IrpContext );
|
||
CdData.IrpContextDepth++;
|
||
|
||
CdUnlockCdData();
|
||
|
||
} else {
|
||
|
||
//
|
||
// We couldn't add this to our lookaside list so free it to
|
||
// pool.
|
||
//
|
||
|
||
CdFreePool( &IrpContext );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Clear the appropriate flags.
|
||
//
|
||
|
||
} else if (Post) {
|
||
|
||
//
|
||
// If this context is the top level CDFS context then we need to
|
||
// restore the top level thread context.
|
||
//
|
||
|
||
if (IrpContext->ThreadContext != NULL) {
|
||
|
||
CdRestoreThreadContext( IrpContext );
|
||
}
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST );
|
||
|
||
} else {
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdInitializeStackIrpContext (
|
||
_Out_ PIRP_CONTEXT IrpContext,
|
||
_In_ PIRP_CONTEXT_LITE IrpContextLite
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize an IrpContext for the current
|
||
CDFS 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();
|
||
|
||
//
|
||
// Zero and then initialize the structure.
|
||
//
|
||
|
||
RtlZeroMemory( IrpContext, sizeof( IRP_CONTEXT ));
|
||
|
||
//
|
||
// Set the proper node type code and node byte size
|
||
//
|
||
|
||
IrpContext->NodeTypeCode = CDFS_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;
|
||
}
|
||
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
CdTeardownStructures (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_Inout_ PFCB StartingFcb,
|
||
_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.
|
||
|
||
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;
|
||
|
||
PAGED_CODE();
|
||
|
||
*RemovedStartingFcb = FALSE;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
_SEH2_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 ) != CDFS_NTC_FCB_DATA) &&
|
||
(CurrentFcb->FcbUserReference == 0) &&
|
||
(CurrentFcb->FileObject != NULL)) {
|
||
|
||
//
|
||
// Go ahead and delete the stream file object.
|
||
//
|
||
|
||
CdDeleteInternalStream( 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 acquire the parent, if present, in order to
|
||
// remove this from the parent prefix table.
|
||
//
|
||
|
||
ParentFcb = CurrentFcb->ParentFcb;
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
CdAcquireFcbExclusive( IrpContext, ParentFcb, FALSE );
|
||
}
|
||
|
||
//
|
||
// Now lock the vcb.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// Final check to see if the reference count is still zero.
|
||
//
|
||
|
||
if (CurrentFcb->FcbReference != 0) {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
CdReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If there is a parent then do the necessary cleanup for the parent.
|
||
//
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
CdRemovePrefix( IrpContext, CurrentFcb );
|
||
RemoveEntryList( &CurrentFcb->FcbLinks );
|
||
|
||
CdDecrementReferenceCounts( IrpContext, ParentFcb, 1, 1 );
|
||
}
|
||
|
||
if (FlagOn( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE )) {
|
||
|
||
CdDeleteFcbTable( IrpContext, CurrentFcb );
|
||
ClearFlag( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE );
|
||
|
||
}
|
||
|
||
//
|
||
// Unlock the Vcb but hold the parent in order to walk up
|
||
// the tree.
|
||
//
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
CdDeleteFcb( IrpContext, CurrentFcb );
|
||
|
||
//
|
||
// Move to the parent Fcb.
|
||
//
|
||
|
||
CurrentFcb = ParentFcb;
|
||
AcquiredCurrentFcb = TRUE;
|
||
|
||
} while (CurrentFcb != NULL);
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
//
|
||
// Release the current Fcb if we have acquired it.
|
||
//
|
||
|
||
if (AcquiredCurrentFcb && (CurrentFcb != NULL)) {
|
||
|
||
CdReleaseFcb( IrpContext, CurrentFcb );
|
||
}
|
||
|
||
//
|
||
// Clear the teardown flag.
|
||
//
|
||
|
||
ClearFlag( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN );
|
||
} _SEH2_END;
|
||
|
||
*RemovedStartingFcb = (CurrentFcb != StartingFcb);
|
||
return;
|
||
}
|
||
|
||
|
||
PFCB
|
||
CdLookupFcbTable (
|
||
_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
|
||
CdGetNextFcb (
|
||
_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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
Fcb = (PFCB) RtlEnumerateGenericTableWithoutSplaying( &Vcb->FcbTable, RestartKey );
|
||
|
||
if (Fcb != NULL) {
|
||
|
||
Fcb = ((PFCB_TABLE_ELEMENT)(Fcb))->Fcb;
|
||
}
|
||
|
||
return Fcb;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdProcessToc (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PDEVICE_OBJECT TargetDeviceObject,
|
||
_In_ PCDROM_TOC_LARGE CdromToc,
|
||
_Inout_ PULONG Length,
|
||
_Out_ PULONG TrackCount,
|
||
_Inout_ PULONG DiskFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to verify and process the TOC for this disk.
|
||
We hide a data track for a CD+ volume.
|
||
|
||
Arguments:
|
||
|
||
TargetDeviceObject - Device object to send TOC request to.
|
||
|
||
CdromToc - Pointer to TOC structure.
|
||
|
||
Length - On input this is the length of the TOC. On return is the TOC
|
||
length we will show to the user.
|
||
|
||
TrackCount - This is the count of tracks for the TOC. We use this
|
||
when creating a pseudo directory for a music disk.
|
||
|
||
DiskFlags - We return flags indicating what we know about this disk.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The result of trying to read the TOC.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
CDROM_READ_TOC_EX Command;
|
||
|
||
ULONG CurrentTrack;
|
||
ULONG LocalTrackCount;
|
||
ULONG LocalTocLength;
|
||
ULONG Address = 0;
|
||
BOOLEAN UseReadToc = FALSE;
|
||
|
||
union {
|
||
|
||
UCHAR BigEndian[2];
|
||
USHORT Length;
|
||
|
||
} BiasedTocLength;
|
||
|
||
PTRACK_DATA Track;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Zero the command block. This conveniently corresponds to an
|
||
// LBA mode READ_TOC request.
|
||
//
|
||
|
||
RtlZeroMemory( &Command, sizeof( Command));
|
||
|
||
RetryReadToc:
|
||
|
||
//
|
||
// Go ahead and read the table of contents
|
||
//
|
||
|
||
Status = CdPerformDevIoCtrlEx( IrpContext,
|
||
UseReadToc ? IOCTL_CDROM_READ_TOC : IOCTL_CDROM_READ_TOC_EX,
|
||
TargetDeviceObject,
|
||
&Command,
|
||
sizeof( Command ),
|
||
CdromToc,
|
||
sizeof( CDROM_TOC_LARGE ),
|
||
FALSE,
|
||
TRUE,
|
||
&Iosb );
|
||
|
||
//
|
||
// Nothing to process if this request fails.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// If the underlying device does not support READ_TOC_EX, try the old method.
|
||
//
|
||
|
||
if (!UseReadToc &&
|
||
((Status == STATUS_INVALID_DEVICE_REQUEST) ||
|
||
(Status == STATUS_NOT_IMPLEMENTED) || /* ReactOS Change: we return STATUS_NOT_IMPLEMENTED for IOCTL_CDROM_READ_TOC_EX */
|
||
(Status == STATUS_INVALID_PARAMETER))) {
|
||
|
||
UseReadToc = TRUE;
|
||
goto RetryReadToc;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Get the number of tracks and stated size of this structure.
|
||
//
|
||
|
||
CurrentTrack = 0;
|
||
LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1;
|
||
LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] );
|
||
|
||
//
|
||
// Get out if there is an immediate problem with the TOC.
|
||
//
|
||
|
||
if ((LocalTocLength > Iosb.Information) ||
|
||
(CdromToc->FirstTrack > CdromToc->LastTrack)) {
|
||
|
||
Status = STATUS_DISK_CORRUPT_ERROR;
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Walk through the individual tracks. Stop at the first data track after
|
||
// any lead-in audio tracks.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// Get the next track.
|
||
//
|
||
|
||
Track = &CdromToc->TrackData[CurrentTrack];
|
||
|
||
//
|
||
// If this is a data track then check if we have only seen audio tracks
|
||
// to this point.
|
||
//
|
||
|
||
if (FlagOn( Track->Control, TOC_DATA_TRACK )) {
|
||
|
||
//
|
||
// If we have only seen audio tracks then assume this is a
|
||
// CD+ disk. Hide the current data track and only return
|
||
// the previous audio tracks. Set the disk type to be mixed
|
||
// data/audio.
|
||
//
|
||
|
||
if (FlagOn( *DiskFlags, CDROM_DISK_AUDIO_TRACK ) &&
|
||
!FlagOn( *DiskFlags, CDROM_DISK_DATA_TRACK )) {
|
||
|
||
//
|
||
// Remove one track from the TOC.
|
||
//
|
||
|
||
CdromToc->LastTrack -= 1;
|
||
|
||
//
|
||
// Knock 2.5 minutes off the current track to hide the final leadin.
|
||
// 2.5 min = 150 sec = (x 75) 11250 frames (sectors).
|
||
//
|
||
|
||
SwapCopyUchar4( &Address, &Track->Address);
|
||
Address -= 11250;
|
||
SwapCopyUchar4( &Track->Address, &Address);
|
||
|
||
Track->TrackNumber = TOC_LAST_TRACK;
|
||
|
||
//
|
||
// Set the disk type to mixed data/audio.
|
||
//
|
||
|
||
SetFlag( *DiskFlags, CDROM_DISK_DATA_TRACK );
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Set the flag to indicate data tracks present.
|
||
//
|
||
|
||
SetFlag( *DiskFlags, CDROM_DISK_DATA_TRACK );
|
||
|
||
//
|
||
// If this is a audio track then set the flag indicating audio
|
||
// tracks.
|
||
//
|
||
|
||
} else {
|
||
|
||
SetFlag( *DiskFlags, CDROM_DISK_AUDIO_TRACK );
|
||
}
|
||
|
||
//
|
||
// Set our index for the next track.
|
||
//
|
||
|
||
CurrentTrack += 1;
|
||
|
||
} while (CurrentTrack < LocalTrackCount);
|
||
|
||
//
|
||
// Set the length to point just past the last track we looked at.
|
||
//
|
||
|
||
*TrackCount = CurrentTrack;
|
||
*Length = PtrOffset( CdromToc, &CdromToc->TrackData[CurrentTrack + 1] );
|
||
BiasedTocLength.Length = (USHORT) *Length - 2;
|
||
|
||
CdromToc->Length[0] = BiasedTocLength.BigEndian[1];
|
||
CdromToc->Length[1] = BiasedTocLength.BigEndian[0];
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
CdDeleteFcb (
|
||
_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();
|
||
|
||
//
|
||
// Sanity check the counts.
|
||
//
|
||
|
||
NT_ASSERT( Fcb->FcbCleanup == 0 );
|
||
NT_ASSERT( Fcb->FcbReference == 0 );
|
||
|
||
//
|
||
// Release any Filter Context structures associated with this FCB
|
||
//
|
||
|
||
FsRtlTeardownPerStreamContexts( &Fcb->Header );
|
||
|
||
//
|
||
// Start with the common structures.
|
||
//
|
||
|
||
CdUninitializeMcb( IrpContext, Fcb );
|
||
|
||
CdDeleteFcbNonpaged( IrpContext, Fcb->FcbNonpaged );
|
||
|
||
//
|
||
// Check if we need to deallocate the prefix name buffer.
|
||
//
|
||
|
||
if ((Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != (PWCHAR) Fcb->FileNamePrefix.FileNameBuffer) &&
|
||
(Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != NULL)) {
|
||
|
||
CdFreePool( &Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Now look at the short name prefix.
|
||
//
|
||
|
||
if (Fcb->ShortNamePrefix != NULL) {
|
||
|
||
CdFreePool( &Fcb->ShortNamePrefix );
|
||
}
|
||
|
||
//
|
||
// Now do the type specific structures.
|
||
//
|
||
|
||
switch (Fcb->NodeTypeCode) {
|
||
|
||
case CDFS_NTC_FCB_PATH_TABLE:
|
||
case CDFS_NTC_FCB_INDEX:
|
||
|
||
NT_ASSERT( Fcb->FileObject == NULL );
|
||
NT_ASSERT( IsListEmpty( &Fcb->FcbQueue ));
|
||
|
||
if (Fcb == Fcb->Vcb->RootIndexFcb) {
|
||
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->RootIndexFcb = NULL;
|
||
|
||
} else if (Fcb == Fcb->Vcb->PathTableFcb) {
|
||
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->PathTableFcb = NULL;
|
||
}
|
||
|
||
CdDeallocateFcbIndex( IrpContext, *(PVOID*)&Fcb );/* ReactOS Change: GCC "passing argument 1 from incompatible pointer type" */
|
||
break;
|
||
|
||
case CDFS_NTC_FCB_DATA :
|
||
|
||
if (Fcb->FileLock != NULL) {
|
||
|
||
FsRtlFreeFileLock( Fcb->FileLock );
|
||
}
|
||
|
||
FsRtlUninitializeOplock( CdGetFcbOplock(Fcb) );
|
||
|
||
if (Fcb == Fcb->Vcb->VolumeDasdFcb) {
|
||
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->VolumeDasdFcb = NULL;
|
||
}
|
||
|
||
CdDeallocateFcbData( IrpContext, *(PVOID*)&Fcb );/* ReactOS Change: GCC "passing argument 1 from incompatible pointer type" */
|
||
}
|
||
|
||
//
|
||
// Decrement the Vcb reference count if this is a system
|
||
// Fcb.
|
||
//
|
||
|
||
if (Vcb != NULL) {
|
||
|
||
InterlockedDecrement( (LONG*)&Vcb->VcbReference );
|
||
InterlockedDecrement( (LONG*)&Vcb->VcbUserReference );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
PFCB_NONPAGED
|
||
CdCreateFcbNonpaged (
|
||
_In_ PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to create and initialize the non-paged portion
|
||
of an Fcb.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
PFCB_NONPAGED - Pointer to the created nonpaged Fcb. NULL if not created.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB_NONPAGED FcbNonpaged;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// Allocate the non-paged pool and initialize the various
|
||
// synchronization objects.
|
||
//
|
||
|
||
FcbNonpaged = CdAllocateFcbNonpaged( IrpContext );
|
||
|
||
if (FcbNonpaged != NULL) {
|
||
|
||
RtlZeroMemory( FcbNonpaged, sizeof( FCB_NONPAGED ));
|
||
|
||
FcbNonpaged->NodeTypeCode = CDFS_NTC_FCB_NONPAGED;
|
||
FcbNonpaged->NodeByteSize = sizeof( FCB_NONPAGED );
|
||
|
||
ExInitializeResourceLite( &FcbNonpaged->FcbResource );
|
||
ExInitializeFastMutex( &FcbNonpaged->FcbMutex );
|
||
}
|
||
|
||
return FcbNonpaged;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
CdDeleteFcbNonpaged (
|
||
_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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
ExDeleteResourceLite( &FcbNonpaged->FcbResource );
|
||
|
||
CdDeallocateFcbNonpaged( IrpContext, *(PVOID*)&FcbNonpaged );/* ReactOS Change: GCC "passing argument 1 from incompatible pointer type" */
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
RTL_GENERIC_COMPARE_RESULTS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdFcbTableCompare (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ PVOID Fid1,
|
||
_In_ PVOID Fid2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the Cdfs compare routine called by the generic table package.
|
||
If will compare the two File Id values and return a comparison result.
|
||
|
||
Arguments:
|
||
|
||
FcbTable - This is the table being searched.
|
||
|
||
Fid1 - First key value.
|
||
|
||
Fid2 - 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 *) Fid1);
|
||
Id2 = *((FILE_ID UNALIGNED *) Fid2);
|
||
|
||
if (Id1.QuadPart < Id2.QuadPart) {
|
||
|
||
return GenericLessThan;
|
||
|
||
} else if (Id1.QuadPart > Id2.QuadPart) {
|
||
|
||
return GenericGreaterThan;
|
||
|
||
} else {
|
||
|
||
return GenericEqual;
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER( FcbTable );
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
PVOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdAllocateFcbTable (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ CLONG ByteSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a generic table support routine to allocate memory
|
||
|
||
Arguments:
|
||
|
||
FcbTable - 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();
|
||
|
||
UNREFERENCED_PARAMETER( FcbTable );
|
||
|
||
return( FsRtlAllocatePoolWithTag( CdPagedPool, ByteSize, TAG_FCB_TABLE ));
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
CdDeallocateFcbTable (
|
||
_In_ PRTL_GENERIC_TABLE FcbTable,
|
||
_In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a generic table support routine that deallocates memory
|
||
|
||
Arguments:
|
||
|
||
FcbTable - Supplies the generic table being used
|
||
|
||
Buffer - Supplies the buffer being deallocated
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
CdFreePool( &Buffer );
|
||
|
||
UNREFERENCED_PARAMETER( FcbTable );
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
ULONG
|
||
CdTocSerial (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PCDROM_TOC_LARGE CdromToc
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to generate a serial number for an audio disk.
|
||
The number is based on the starting positions of the tracks.
|
||
The following algorithm is used.
|
||
|
||
If the number of tracks is <= 2 then initialize the serial number to the
|
||
leadout block number.
|
||
|
||
Then add the starting address of each track (use 0x00mmssff format).
|
||
|
||
Arguments:
|
||
|
||
CdromToc - Valid table of contents to use for track information.
|
||
|
||
Return Value:
|
||
|
||
ULONG - 32 bit serial number based on TOC.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG SerialNumber = 0;
|
||
PTRACK_DATA ThisTrack;
|
||
PTRACK_DATA LastTrack;
|
||
ULONG Address;
|
||
ULONG MsfAddress = 0; // satisfy PREFIX
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// Check if there are two tracks or fewer.
|
||
//
|
||
|
||
LastTrack = &CdromToc->TrackData[ CdromToc->LastTrack - CdromToc->FirstTrack + 1];
|
||
ThisTrack = &CdromToc->TrackData[0];
|
||
|
||
if (CdromToc->LastTrack - CdromToc->FirstTrack <= 1) {
|
||
|
||
SwapCopyUchar4( &Address, LastTrack->Address);
|
||
CdLbnToMmSsFf( Address, (PUCHAR)&SerialNumber);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Add the starting offset of each track and add to the serial number.
|
||
//
|
||
|
||
while (ThisTrack != LastTrack) {
|
||
|
||
SwapCopyUchar4( &Address, ThisTrack->Address);
|
||
CdLbnToMmSsFf( Address, (PUCHAR)&MsfAddress);
|
||
|
||
SerialNumber += MsfAddress;
|
||
ThisTrack += 1;
|
||
}
|
||
}
|
||
|
||
return SerialNumber;
|
||
}
|
||
|
||
|