2788 lines
63 KiB
C
2788 lines
63 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
StrucSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Cdfs in-memory data structure manipulation
|
||
routines
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Brian Andrew [BrianAn] 01-July-1995
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#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,
|
||
// IN PFCB Fcb
|
||
// );
|
||
//
|
||
// PFCB
|
||
// CdAllocateFcbIndex (
|
||
// IN PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateFcbIndex (
|
||
// IN PIRP_CONTEXT IrpContext,
|
||
// IN PFCB Fcb
|
||
// );
|
||
//
|
||
// PFCB_NONPAGED
|
||
// CdAllocateFcbNonpaged (
|
||
// IN PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateFcbNonpaged (
|
||
// IN PIRP_CONTEXT IrpContext,
|
||
// IN PFCB_NONPAGED FcbNonpaged
|
||
// );
|
||
//
|
||
// PCCB
|
||
// CdAllocateCcb (
|
||
// IN PIRP_CONTEXT IrpContext
|
||
// );
|
||
//
|
||
// VOID
|
||
// CdDeallocateCcb (
|
||
// IN PIRP_CONTEXT IrpContext,
|
||
// IN PCCB Ccb
|
||
// );
|
||
//
|
||
|
||
#define CdAllocateFcbData(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_DATA, TAG_FCB_DATA )
|
||
|
||
#define CdDeallocateFcbData(IC,F) \
|
||
ExFreePool( F )
|
||
|
||
#define CdAllocateFcbIndex(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_INDEX, TAG_FCB_INDEX )
|
||
|
||
#define CdDeallocateFcbIndex(IC,F) \
|
||
ExFreePool( F )
|
||
|
||
#define CdAllocateFcbNonpaged(IC) \
|
||
ExAllocatePoolWithTag( CdNonPagedPool, sizeof( FCB_NONPAGED ), TAG_FCB_NONPAGED )
|
||
|
||
#define CdDeallocateFcbNonpaged(IC,FNP) \
|
||
ExFreePool( FNP )
|
||
|
||
#define CdAllocateCcb(IC) \
|
||
FsRtlAllocatePoolWithTag( CdPagedPool, sizeof( CCB ), TAG_CCB )
|
||
|
||
#define CdDeallocateCcb(IC,C) \
|
||
ExFreePool( 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
|
||
);
|
||
|
||
RTL_GENERIC_COMPARE_RESULTS
|
||
CdFcbTableCompare (
|
||
IN PRTL_GENERIC_TABLE FcbTable,
|
||
IN PVOID Fid1,
|
||
IN PVOID Fid2
|
||
);
|
||
|
||
PVOID
|
||
CdAllocateFcbTable (
|
||
IN PRTL_GENERIC_TABLE FcbTable,
|
||
IN CLONG ByteSize
|
||
);
|
||
|
||
VOID
|
||
CdDeallocateFcbTable (
|
||
IN PRTL_GENERIC_TABLE FcbTable,
|
||
IN PVOID Buffer
|
||
);
|
||
|
||
ULONG
|
||
CdTocSerial (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PCDROM_TOC 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
|
||
|
||
|
||
VOID
|
||
CdInitializeVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PVCB Vcb,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PVPB Vpb,
|
||
IN PCDROM_TOC 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();
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
try {
|
||
|
||
Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPool,
|
||
sizeof( VPB ),
|
||
TAG_VPB );
|
||
}
|
||
finally {
|
||
|
||
if (AbnormalTermination()) {
|
||
|
||
FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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,
|
||
IN OUT PVCB Vcb,
|
||
IN PCHAR RawIsoVd OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
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 Shift;
|
||
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.
|
||
//
|
||
|
||
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 );
|
||
|
||
if ((Vcb->BlockSize > SECTOR_SIZE) || (0 == Vcb->BlockSize)) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
Vcb->BlocksPerSector = SECTOR_SIZE / Vcb->BlockSize;
|
||
Vcb->BlockMask = Vcb->BlockSize - 1;
|
||
Vcb->BlockInverseMask = ~Vcb->BlockMask;
|
||
|
||
//
|
||
// Initialize the BlockToSectorShift and BlockToByte by assuming
|
||
// the block size is the same as the sector size. Shift the
|
||
// blocks per sectors until it goes to zero.
|
||
//
|
||
|
||
Vcb->BlockToSectorShift = 0;
|
||
Vcb->BlockToByteShift = SECTOR_SHIFT;
|
||
|
||
Shift = Vcb->BlocksPerSector - 1;
|
||
|
||
while (Shift != 0) {
|
||
|
||
Vcb->BlockToSectorShift += 1;
|
||
Vcb->BlockToByteShift -= 1;
|
||
|
||
Shift >>= 1;
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (UnlockVcb) { CdUnlockVcb( IrpContext, Vcb ); }
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
CdDeleteVcb (
|
||
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_CDDATA;
|
||
ASSERT_EXCLUSIVE_VCB( Vcb );
|
||
|
||
//
|
||
// Chuck the backpocket Vpb we kept just in case.
|
||
//
|
||
|
||
if (Vcb->SwapVpb) {
|
||
|
||
ExFreePool( Vcb->SwapVpb );
|
||
}
|
||
|
||
//
|
||
// If there is a Vpb then we must delete it ourselves.
|
||
//
|
||
|
||
if (Vcb->Vpb != NULL) {
|
||
|
||
ExFreePool( Vcb->Vpb );
|
||
}
|
||
|
||
//
|
||
// Dereference our target if we haven't already done so.
|
||
//
|
||
|
||
if (Vcb->TargetDeviceObject != NULL) {
|
||
|
||
ObDereferenceObject( Vcb->TargetDeviceObject );
|
||
}
|
||
|
||
//
|
||
// Delete the XA Sector if allocated.
|
||
//
|
||
|
||
if (Vcb->XASector != NULL) {
|
||
|
||
ExFreePool( Vcb->XASector );
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
if (Vcb->CdromToc != NULL) {
|
||
|
||
ExFreePool( Vcb->CdromToc );
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
|
||
PFCB
|
||
CdCreateFcb (
|
||
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 = 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:
|
||
|
||
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) {
|
||
|
||
ExFreePool( NewFcb );
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
*FcbExisted = FALSE;
|
||
|
||
//
|
||
// Initialize Advanced FCB Header fields
|
||
//
|
||
|
||
ExInitializeFastMutex( &NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex );
|
||
FsRtlSetupAdvancedHeader( &NewFcb->Header,
|
||
&NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex );
|
||
} else {
|
||
|
||
*FcbExisted = TRUE;
|
||
}
|
||
|
||
return NewFcb;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdInitializeFcbFromPathEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL,
|
||
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_READONLY | 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,
|
||
IN 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 );
|
||
|
||
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 );
|
||
|
||
} finally {
|
||
|
||
CdUnlockFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
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();
|
||
|
||
//
|
||
// 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 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();
|
||
|
||
if (Ccb->SearchExpression.FileName.Buffer != NULL) {
|
||
|
||
ExFreePool( Ccb->SearchExpression.FileName.Buffer );
|
||
}
|
||
|
||
CdDeallocateCcb( IrpContext, Ccb );
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
CdCreateFileLock (
|
||
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.
|
||
//
|
||
|
||
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) {
|
||
|
||
ASSERT( ARGUMENT_PRESENT( IrpContext ));
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
Result = FALSE;
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
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).
|
||
//
|
||
|
||
if (IrpSp->DeviceObject == CdData.FileSystemDeviceObject) {
|
||
|
||
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 );
|
||
}
|
||
|
||
//
|
||
// 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( NonPagedPool, 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.
|
||
//
|
||
|
||
if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) {
|
||
|
||
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.
|
||
//
|
||
|
||
ExFreePool( 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;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdTeardownStructures (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN 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.
|
||
//
|
||
|
||
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);
|
||
|
||
} 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 );
|
||
}
|
||
|
||
*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();
|
||
|
||
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 CdromToc,
|
||
IN OUT PULONG Length,
|
||
OUT PULONG TrackCount,
|
||
OUT 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;
|
||
|
||
ULONG CurrentTrack;
|
||
ULONG LocalTrackCount;
|
||
ULONG LocalTocLength;
|
||
|
||
union {
|
||
|
||
UCHAR BigEndian[2];
|
||
USHORT Length;
|
||
|
||
} BiasedTocLength;
|
||
|
||
PTRACK_DATA Track;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Go ahead and read the table of contents
|
||
//
|
||
|
||
Status = CdPerformDevIoCtrl( IrpContext,
|
||
IOCTL_CDROM_READ_TOC,
|
||
TargetDeviceObject,
|
||
CdromToc,
|
||
sizeof( CDROM_TOC ),
|
||
FALSE,
|
||
TRUE,
|
||
&Iosb );
|
||
|
||
//
|
||
// Nothing to process if this request fails.
|
||
//
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
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.
|
||
//
|
||
|
||
Track->Address[1] -= 2;
|
||
Track->Address[2] += 30;
|
||
|
||
if (Track->Address[2] < 60) {
|
||
|
||
Track->Address[1] -= 1;
|
||
|
||
} else {
|
||
|
||
Track->Address[2] -= 60;
|
||
}
|
||
|
||
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.
|
||
//
|
||
|
||
ASSERT( Fcb->FcbCleanup == 0 );
|
||
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)) {
|
||
|
||
ExFreePool( Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Now look at the short name prefix.
|
||
//
|
||
|
||
if (Fcb->ShortNamePrefix != NULL) {
|
||
|
||
ExFreePool( Fcb->ShortNamePrefix );
|
||
}
|
||
|
||
//
|
||
// Now do the type specific structures.
|
||
//
|
||
|
||
switch (Fcb->NodeTypeCode) {
|
||
|
||
case CDFS_NTC_FCB_PATH_TABLE:
|
||
case CDFS_NTC_FCB_INDEX:
|
||
|
||
ASSERT( Fcb->FileObject == NULL );
|
||
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, Fcb );
|
||
break;
|
||
|
||
case CDFS_NTC_FCB_DATA :
|
||
|
||
if (Fcb->FileLock != NULL) {
|
||
|
||
FsRtlFreeFileLock( Fcb->FileLock );
|
||
}
|
||
|
||
FsRtlUninitializeOplock( &Fcb->Oplock );
|
||
|
||
if (Fcb == Fcb->Vcb->VolumeDasdFcb) {
|
||
|
||
Vcb = Fcb->Vcb;
|
||
Vcb->VolumeDasdFcb = NULL;
|
||
}
|
||
|
||
CdDeallocateFcbData( IrpContext, Fcb );
|
||
}
|
||
|
||
//
|
||
// Decrement the Vcb reference count if this is a system
|
||
// Fcb.
|
||
//
|
||
|
||
if (Vcb != NULL) {
|
||
|
||
InterlockedDecrement( &Vcb->VcbReference );
|
||
InterlockedDecrement( &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();
|
||
|
||
//
|
||
// 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();
|
||
|
||
ExDeleteResourceLite( &FcbNonpaged->FcbResource );
|
||
|
||
CdDeallocateFcbNonpaged( IrpContext, FcbNonpaged );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
RTL_GENERIC_COMPARE_RESULTS
|
||
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
|
||
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();
|
||
|
||
return( FsRtlAllocatePoolWithTag( CdPagedPool, ByteSize, TAG_FCB_TABLE ));
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
CdDeallocateFcbTable (
|
||
IN PRTL_GENERIC_TABLE FcbTable,
|
||
IN 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();
|
||
|
||
ExFreePool( Buffer );
|
||
|
||
return;
|
||
UNREFERENCED_PARAMETER( FcbTable );
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
ULONG
|
||
CdTocSerial (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PCDROM_TOC 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;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// 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) {
|
||
|
||
SerialNumber = (((LastTrack->Address[1] * 60) + LastTrack->Address[2]) * 75) + LastTrack->Address[3];
|
||
|
||
SerialNumber -= (((ThisTrack->Address[1] * 60) + ThisTrack->Address[2]) * 75) + ThisTrack->Address[3];
|
||
}
|
||
|
||
//
|
||
// Now find the starting offset of each track and add to the serial number.
|
||
//
|
||
|
||
while (ThisTrack != LastTrack) {
|
||
|
||
SerialNumber += (ThisTrack->Address[1] << 16);
|
||
SerialNumber += (ThisTrack->Address[2] << 8);
|
||
SerialNumber += ThisTrack->Address[3];
|
||
ThisTrack += 1;
|
||
}
|
||
|
||
return SerialNumber;
|
||
}
|
||
|