From 77a8db07f34e266360cefbe5db1c3130a8ce4138 Mon Sep 17 00:00:00 2001 From: ReactOS Portable Systems Group Date: Sun, 20 Jul 2008 19:52:30 +0000 Subject: [PATCH] - Add new CDFS driver. - Does not compile. svn path=/trunk/; revision=34615 --- .../drivers/filesystems/cdfs_new/allocsup.c | 903 +++++ .../drivers/filesystems/cdfs_new/cachesup.c | 644 +++ reactos/drivers/filesystems/cdfs_new/cd.h | 534 +++ reactos/drivers/filesystems/cdfs_new/cddata.c | 1151 ++++++ reactos/drivers/filesystems/cdfs_new/cddata.h | 278 ++ .../drivers/filesystems/cdfs_new/cdfs.rbuild | 37 + reactos/drivers/filesystems/cdfs_new/cdfs.rc | 7 + reactos/drivers/filesystems/cdfs_new/cdinit.c | 366 ++ .../drivers/filesystems/cdfs_new/cdprocs.h | 1888 +++++++++ .../drivers/filesystems/cdfs_new/cdstruc.h | 1763 ++++++++ .../drivers/filesystems/cdfs_new/cleanup.c | 332 ++ reactos/drivers/filesystems/cdfs_new/close.c | 1026 +++++ reactos/drivers/filesystems/cdfs_new/create.c | 2916 +++++++++++++ .../drivers/filesystems/cdfs_new/devctrl.c | 196 + .../drivers/filesystems/cdfs_new/deviosup.c | 3603 +++++++++++++++++ .../drivers/filesystems/cdfs_new/dirctrl.c | 1520 +++++++ reactos/drivers/filesystems/cdfs_new/dirsup.c | 1863 +++++++++ .../drivers/filesystems/cdfs_new/fieldoff.c | 478 +++ .../drivers/filesystems/cdfs_new/fileinfo.c | 1501 +++++++ .../drivers/filesystems/cdfs_new/filobsup.c | 234 ++ reactos/drivers/filesystems/cdfs_new/fsctrl.c | 3166 +++++++++++++++ .../drivers/filesystems/cdfs_new/fspdisp.c | 278 ++ .../drivers/filesystems/cdfs_new/lockctrl.c | 674 +++ .../drivers/filesystems/cdfs_new/namesup.c | 1044 +++++ .../drivers/filesystems/cdfs_new/nodetype.h | 103 + .../drivers/filesystems/cdfs_new/pathsup.c | 997 +++++ reactos/drivers/filesystems/cdfs_new/pnp.c | 793 ++++ .../drivers/filesystems/cdfs_new/prefxsup.c | 711 ++++ reactos/drivers/filesystems/cdfs_new/read.c | 550 +++ .../drivers/filesystems/cdfs_new/resrcsup.c | 332 ++ .../drivers/filesystems/cdfs_new/strucsup.c | 2766 +++++++++++++ .../drivers/filesystems/cdfs_new/verfysup.c | 954 +++++ .../drivers/filesystems/cdfs_new/volinfo.c | 502 +++ .../drivers/filesystems/cdfs_new/workque.c | 398 ++ 34 files changed, 34508 insertions(+) create mode 100755 reactos/drivers/filesystems/cdfs_new/allocsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/cachesup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/cd.h create mode 100755 reactos/drivers/filesystems/cdfs_new/cddata.c create mode 100755 reactos/drivers/filesystems/cdfs_new/cddata.h create mode 100644 reactos/drivers/filesystems/cdfs_new/cdfs.rbuild create mode 100644 reactos/drivers/filesystems/cdfs_new/cdfs.rc create mode 100755 reactos/drivers/filesystems/cdfs_new/cdinit.c create mode 100755 reactos/drivers/filesystems/cdfs_new/cdprocs.h create mode 100755 reactos/drivers/filesystems/cdfs_new/cdstruc.h create mode 100755 reactos/drivers/filesystems/cdfs_new/cleanup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/close.c create mode 100755 reactos/drivers/filesystems/cdfs_new/create.c create mode 100755 reactos/drivers/filesystems/cdfs_new/devctrl.c create mode 100755 reactos/drivers/filesystems/cdfs_new/deviosup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/dirctrl.c create mode 100755 reactos/drivers/filesystems/cdfs_new/dirsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/fieldoff.c create mode 100755 reactos/drivers/filesystems/cdfs_new/fileinfo.c create mode 100755 reactos/drivers/filesystems/cdfs_new/filobsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/fsctrl.c create mode 100755 reactos/drivers/filesystems/cdfs_new/fspdisp.c create mode 100755 reactos/drivers/filesystems/cdfs_new/lockctrl.c create mode 100755 reactos/drivers/filesystems/cdfs_new/namesup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/nodetype.h create mode 100755 reactos/drivers/filesystems/cdfs_new/pathsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/pnp.c create mode 100755 reactos/drivers/filesystems/cdfs_new/prefxsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/read.c create mode 100755 reactos/drivers/filesystems/cdfs_new/resrcsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/strucsup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/verfysup.c create mode 100755 reactos/drivers/filesystems/cdfs_new/volinfo.c create mode 100755 reactos/drivers/filesystems/cdfs_new/workque.c diff --git a/reactos/drivers/filesystems/cdfs_new/allocsup.c b/reactos/drivers/filesystems/cdfs_new/allocsup.c new file mode 100755 index 00000000000..1717aed5046 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/allocsup.c @@ -0,0 +1,903 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + AllocSup.c + +Abstract: + + This module implements the Allocation support routines for Cdfs. + + The data structure used here is the CD_MCB. There is an entry in + the Mcb for each dirent for a file. The entry will map the offset + within some file to a starting disk offset and number of bytes. + The Mcb also contains the interleave information for an extent. + An interleave consists of a number of blocks with data and a + (possibly different) number of blocks to skip. Any number of + data/skip pairs may exist in an extent but the data and skip sizes + are the same throughout the extent. + + We store the following information into an Mcb entry for an extent. + + FileOffset Offset in file for start of extent + DiskOffset Offset on disk for start of extent + ByteCount Number of file bytes in extent, no skip bytes + DataBlockByteCount Number of bytes in each data block + TotalBlockByteCount Number of bytes is data block and skip block + + The disk offset in the Mcb has already been biased by the size of + the Xar block if present. All of the byte count fields are aligned + on logical block boundaries. If this is a directory or path table + then the file offset has been biased to round the initial disk + offset down to a sector boundary. The biasing is done when loading + the values into an Mcb entry. + + An XA file has a header prepended to the file and each sector is 2352 + bytes. The allocation information ignores the header and only deals + with 2048 byte sectors. Callers into the allocation package have + adjusted the starting offset value to reflect 2048 sectors. On return + from this package the caller will have to convert from 2048 sector values + into raw XA sector values. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_ALLOCSUP) + +// +// Local support routines +// + +ULONG +CdFindMcbEntry ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG FileOffset + ); + +VOID +CdDiskOffsetFromMcbEntry ( + IN PIRP_CONTEXT IrpContext, + IN PCD_MCB_ENTRY McbEntry, + IN LONGLONG FileOffset, + IN PLONGLONG DiskOffset, + IN PULONG ByteCount + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdAddInitialAllocation) +#pragma alloc_text(PAGE, CdAddAllocationFromDirent) +#pragma alloc_text(PAGE, CdDiskOffsetFromMcbEntry) +#pragma alloc_text(PAGE, CdFindMcbEntry) +#pragma alloc_text(PAGE, CdInitializeMcb) +#pragma alloc_text(PAGE, CdLookupAllocation) +#pragma alloc_text(PAGE, CdTruncateAllocation) +#pragma alloc_text(PAGE, CdUninitializeMcb) +#endif + + +VOID +CdLookupAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG FileOffset, + OUT PLONGLONG DiskOffset, + OUT PULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine looks through the mapping information for the file + to find the logical diskoffset and number of bytes at that offset. + We only deal with logical 2048 byte sectors here. + + If the mapping isn't present we will look it up on disk now. + This routine assumes we are looking up a valid range in the file. This + routine raises if it can't find mapping for the file offset. + + The Fcb may not be locked prior to calling this routine. We will always + acquire it here. + +Arguments: + + Fcb - Fcb representing this stream. + + FileOffset - Lookup the allocation beginning at this point. + + DiskOffset - Address to store the logical disk offset. + + ByteCount - Address to store the number of contiguous bytes beginning + at DiskOffset above. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN FirstPass = TRUE; + ULONG McbEntryOffset; + PFCB ParentFcb; + BOOLEAN CleanupParent = FALSE; + + BOOLEAN UnlockFcb = FALSE; + + LONGLONG CurrentFileOffset; + ULONG CurrentMcbOffset; + PCD_MCB_ENTRY CurrentMcbEntry; + + DIRENT_ENUM_CONTEXT DirContext; + DIRENT Dirent; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Use a try finally to facilitate cleanup. + // + + try { + + // + // We use a loop to perform the lookup. If we don't find the mapping in the + // first pass then we look up all of the allocation and then look again. + + while (TRUE) { + + // + // + // Lookup the entry containing this file offset. + // + + CdLockFcb( IrpContext, Fcb ); + UnlockFcb = TRUE; + + McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, FileOffset ); + + // + // If within the Mcb then we use the data out of this entry and are + // done. + // + + if (McbEntryOffset < Fcb->Mcb.CurrentEntryCount) { + + CdDiskOffsetFromMcbEntry( IrpContext, + Fcb->Mcb.McbArray + McbEntryOffset, + FileOffset, + DiskOffset, + ByteCount ); + + break; + + // + // If this is not the first pass then the disk is corrupt. + // + + } else if (!FirstPass) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + CdUnlockFcb( IrpContext, Fcb ); + UnlockFcb = FALSE; + + // + // Initialize the search dirent structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + + // + // Otherwise we need to walk the dirents for this file until we find + // the one containing this entry. The parent Fcb should always be + // present. + // + + ParentFcb = Fcb->ParentFcb; + CdAcquireFileShared( IrpContext, ParentFcb ); + CleanupParent = TRUE; + + // + // Do an unsafe test to see if we need to create a file object. + // + + if (ParentFcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, ParentFcb->Vcb, ParentFcb ); + } + + // + // Initialize the local variables to indicate the first dirent + // and lookup the first dirent. + // + + CurrentFileOffset = 0; + CurrentMcbOffset = 0; + + CdLookupDirent( IrpContext, + ParentFcb, + CdQueryFidDirentOffset( Fcb->FileId ), + &DirContext ); + + // + // If we are adding allocation to the Mcb then add all of it. + // + + while (TRUE ) { + + // + // Update the dirent from the on-disk dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent ); + + // + // Add this dirent to the Mcb if not already present. + // + + CdLockFcb( IrpContext, Fcb ); + UnlockFcb = TRUE; + + if (CurrentMcbOffset >= Fcb->Mcb.CurrentEntryCount) { + + CdAddAllocationFromDirent( IrpContext, Fcb, CurrentMcbOffset, CurrentFileOffset, &Dirent ); + } + + CdUnlockFcb( IrpContext, Fcb ); + UnlockFcb = FALSE; + + // + // If this is the last dirent for the file then exit. + // + + if (!FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + // + // If we couldn't find another entry then the directory is corrupt because + // the last dirent for a file doesn't exist. + // + + if (!CdLookupNextDirent( IrpContext, ParentFcb, &DirContext, &DirContext )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Update our loop variables. + // + + CurrentMcbEntry = Fcb->Mcb.McbArray + CurrentMcbOffset; + CurrentFileOffset += CurrentMcbEntry->ByteCount; + CurrentMcbOffset += 1; + } + + // + // All of the allocation is loaded. Go back and look up the mapping again. + // It better be there this time. + // + + FirstPass = FALSE; + } + + } finally { + + if (CleanupParent) { + + // + // Release the parent and cleanup the dirent structures. + // + + CdReleaseFile( IrpContext, ParentFcb ); + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + } + + if (UnlockFcb) { CdUnlockFcb( IrpContext, Fcb ); } + } + + return; +} + + +VOID +CdAddAllocationFromDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG McbEntryOffset, + IN LONGLONG StartingFileOffset, + IN PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to add an entry into the Cd Mcb. We grow the Mcb + as necessary and update the new entry. + + NOTE - The Fcb has already been locked prior to makeing this call. + +Arguments: + + Fcb - Fcb containing the Mcb to update. + + McbEntryOffset - Offset into the Mcb array to add this data. + + StartingFileOffset - Offset in bytes from the start of the file. + + Dirent - Dirent containing the on-disk data for this entry. + +Return Value: + + None + +--*/ + +{ + ULONG NewArraySize; + PVOID NewMcbArray; + PCD_MCB_ENTRY McbEntry; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // If we need to grow the Mcb then do it now. + // + + if (McbEntryOffset >= Fcb->Mcb.MaximumEntryCount) { + + // + // Allocate a new buffer and copy the old data over. + // + + NewArraySize = Fcb->Mcb.MaximumEntryCount * 2 * sizeof( CD_MCB_ENTRY ); + + NewMcbArray = FsRtlAllocatePoolWithTag( CdPagedPool, + NewArraySize, + TAG_MCB_ARRAY ); + + RtlZeroMemory( NewMcbArray, NewArraySize ); + RtlCopyMemory( NewMcbArray, + Fcb->Mcb.McbArray, + Fcb->Mcb.MaximumEntryCount * sizeof( CD_MCB_ENTRY )); + + // + // Deallocate the current array unless it is embedded in the Fcb. + // + + if (Fcb->Mcb.MaximumEntryCount != 1) { + + CdFreePool( &Fcb->Mcb.McbArray ); + } + + // + // Now update the Mcb with the new array. + // + + Fcb->Mcb.MaximumEntryCount *= 2; + Fcb->Mcb.McbArray = NewMcbArray; + } + + // + // Update the new entry with the input data. + // + + McbEntry = Fcb->Mcb.McbArray + McbEntryOffset; + + // + // Start with the location and length on disk. + // + + McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, Dirent->StartingOffset ); + McbEntry->ByteCount = Dirent->DataLength; + + // + // Round the byte count up to a logical block boundary if this is + // the last extent. + // + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { + + McbEntry->ByteCount = BlockAlign( Fcb->Vcb, McbEntry->ByteCount ); + } + + // + // The file offset is the logical position within this file. + // We know this is correct regardless of whether we bias the + // file size or disk offset. + // + + McbEntry->FileOffset = StartingFileOffset; + + // + // Convert the interleave information from logical blocks to + // bytes. + // + + if (Dirent->FileUnitSize != 0) { + + McbEntry->DataBlockByteCount = LlBytesFromBlocks( Fcb->Vcb, Dirent->FileUnitSize ); + McbEntry->TotalBlockByteCount = McbEntry->DataBlockByteCount + + LlBytesFromBlocks( Fcb->Vcb, Dirent->InterleaveGapSize ); + + // + // If the file is not interleaved then the size of the data block + // and total block are the same as the byte count. + // + + } else { + + McbEntry->DataBlockByteCount = + McbEntry->TotalBlockByteCount = McbEntry->ByteCount; + } + + // + // Update the number of entries in the Mcb. The Mcb is never sparse + // so whenever we add an entry it becomes the last entry in the Mcb. + // + + Fcb->Mcb.CurrentEntryCount = McbEntryOffset + 1; + + return; +} + + +VOID +CdAddInitialAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG StartingBlock, + IN LONGLONG DataLength + ) + +/*++ + +Routine Description: + + This routine is called to set up the initial entry in an Mcb. + + This routine handles the single initial entry for a directory file. We will + round the start block down to a sector boundary. Our caller has already + biased the DataLength with any adjustments. This is used for the case + where there is a single entry and we want to align the data on a sector + boundary. + +Arguments: + + Fcb - Fcb containing the Mcb to update. + + StartingBlock - Starting logical block for this directory. This is + the start of the actual data. We will bias this by the sector + offset of the data. + + DataLength - Length of the data. + +Return Value: + + None + +--*/ + +{ + PCD_MCB_ENTRY McbEntry; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + ASSERT( 0 == Fcb->Mcb.CurrentEntryCount); + ASSERT( CDFS_NTC_FCB_DATA != Fcb->NodeTypeCode); + + // + // Update the new entry with the input data. + // + + McbEntry = Fcb->Mcb.McbArray; + + // + // Start with the location and length on disk. + // + + McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, StartingBlock ); + McbEntry->DiskOffset -= Fcb->StreamOffset; + + McbEntry->ByteCount = DataLength; + + // + // The file offset is the logical position within this file. + // We know this is correct regardless of whether we bias the + // file size or disk offset. + // + + McbEntry->FileOffset = 0; + + // + // If the file is not interleaved then the size of the data block + // and total block are the same as the byte count. + // + + McbEntry->DataBlockByteCount = + McbEntry->TotalBlockByteCount = McbEntry->ByteCount; + + // + // Update the number of entries in the Mcb. The Mcb is never sparse + // so whenever we add an entry it becomes the last entry in the Mcb. + // + + Fcb->Mcb.CurrentEntryCount = 1; + + return; +} + + +VOID +CdTruncateAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingFileOffset + ) + +/*++ + +Routine Description: + + This routine truncates the Mcb for a file by eliminating all of the Mcb + entries from the entry which contains the given offset. + + The Fcb should be locked when this routine is called. + +Arguments: + + Fcb - Fcb containing the Mcb to truncate. + + StartingFileOffset - Offset in the file to truncate the Mcb from. + +Return Value: + + None + +--*/ + +{ + ULONG McbEntryOffset; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // Find the entry containg this starting offset. + // + + McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, StartingFileOffset ); + + // + // Now set the current size of the mcb to this point. + // + + Fcb->Mcb.CurrentEntryCount = McbEntryOffset; + + return; +} + + +VOID +CdInitializeMcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to initialize the Mcb in an Fcb. We initialize + this with an entry count of one and point to the entry in the Fcb + itself. + + Fcb should be acquired exclusively when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to initialize. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Set the entry counts to show there is one entry in the array and + // it is unused. + // + + Fcb->Mcb.MaximumEntryCount = 1; + Fcb->Mcb.CurrentEntryCount = 0; + + Fcb->Mcb.McbArray = &Fcb->McbEntry; + + return; +} + + +VOID +CdUninitializeMcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to cleanup an Mcb in an Fcb. We look at the + maximum run count in the Fcb and if greater than one we will deallocate + the buffer. + + Fcb should be acquired exclusively when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to uninitialize. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // If the count is greater than one then this is an allocated buffer. + // + + if (Fcb->Mcb.MaximumEntryCount > 1) { + + CdFreePool( &Fcb->Mcb.McbArray ); + } + + return; +} + + +// +// Local suupport routine +// + +ULONG +CdFindMcbEntry ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG FileOffset + ) + +/*++ + +Routine Description: + + This routine is called to find the Mcb entry which contains the file + offset at the given point. If the file offset is not currently in the + Mcb then we return the offset of the entry to add. + + Fcb should be locked when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to uninitialize. + + FileOffset - Return the Mcb entry which contains this file offset. + +Return Value: + + ULONG - Offset in the Mcb of the entry for this offset. + +--*/ + +{ + ULONG CurrentMcbOffset; + PCD_MCB_ENTRY CurrentMcbEntry; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // We expect a linear search will be sufficient here. + // + + CurrentMcbOffset = 0; + CurrentMcbEntry = Fcb->Mcb.McbArray; + + while (CurrentMcbOffset < Fcb->Mcb.CurrentEntryCount) { + + // + // Check if the offset lies within the current Mcb position. + // + + if (FileOffset < CurrentMcbEntry->FileOffset + CurrentMcbEntry->ByteCount) { + + break; + } + + // + // Move to the next entry. + // + + CurrentMcbOffset += 1; + CurrentMcbEntry += 1; + } + + // + // This is the offset containing this file offset (or the point + // where an entry should be added). + // + + return CurrentMcbOffset; +} + + +// +// Local support routine +// + +VOID +CdDiskOffsetFromMcbEntry ( + IN PIRP_CONTEXT IrpContext, + IN PCD_MCB_ENTRY McbEntry, + IN LONGLONG FileOffset, + IN PLONGLONG DiskOffset, + IN PULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine is called to return the diskoffset and length of the file + data which begins at offset 'FileOffset'. We have the Mcb entry which + contains the mapping and interleave information. + + NOTE - This routine deals with data in 2048 byte logical sectors. If + this is an XA file then our caller has already converted from + 'raw' file bytes to 'cooked' file bytes. + +Arguments: + + McbEntry - Entry in the Mcb containing the allocation information. + + FileOffset - Starting Offset in the file to find the matching disk + offsets. + + DiskOffset - Address to store the starting disk offset for this operation. + + ByteCount - Address to store number of contiguous bytes starting at this + disk offset. + +Return Value: + + None + +--*/ + +{ + LONGLONG ExtentOffset; + + LONGLONG CurrentDiskOffset; + LONGLONG CurrentExtentOffset; + + LONGLONG LocalByteCount; + + PAGED_CODE(); + ASSERT_IRP_CONTEXT( IrpContext ); + + // + // Extent offset is the difference between the file offset and the start + // of the extent. + // + + ExtentOffset = FileOffset - McbEntry->FileOffset; + + // + // Optimize the non-interleave case. + // + + if (McbEntry->ByteCount == McbEntry->DataBlockByteCount) { + + *DiskOffset = McbEntry->DiskOffset + ExtentOffset; + + LocalByteCount = McbEntry->ByteCount - ExtentOffset; + + } else { + + // + // Walk though any interleave until we reach the current offset in + // this extent. + // + + CurrentExtentOffset = McbEntry->DataBlockByteCount; + CurrentDiskOffset = McbEntry->DiskOffset; + + while (CurrentExtentOffset <= ExtentOffset) { + + CurrentDiskOffset += McbEntry->TotalBlockByteCount; + CurrentExtentOffset += McbEntry->DataBlockByteCount; + } + + // + // We are now positioned at the data block containing the starting + // file offset we were given. The disk offset is the offset of + // the start of this block plus the extent offset into this block. + // The byte count is the data block byte count minus our offset into + // this block. + // + + *DiskOffset = CurrentDiskOffset + (ExtentOffset + McbEntry->DataBlockByteCount - CurrentExtentOffset); + + // + // Make sure we aren't past the end of the data length. This is possible + // if we only use part of the last data block on an interleaved file. + // + + if (CurrentExtentOffset > McbEntry->ByteCount) { + + CurrentExtentOffset = McbEntry->ByteCount; + } + + LocalByteCount = CurrentExtentOffset - ExtentOffset; + } + + // + // If the byte count exceeds our limit then cut it to fit in 32 bits. + // + + if (LocalByteCount > MAXULONG) { + + *ByteCount = MAXULONG; + + } else { + + *ByteCount = (ULONG) LocalByteCount; + } + + return; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/cachesup.c b/reactos/drivers/filesystems/cdfs_new/cachesup.c new file mode 100755 index 00000000000..b31422f97e0 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cachesup.c @@ -0,0 +1,644 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + Cache.c + +Abstract: + + This module implements the cache management routines for the Cdfs + FSD and FSP, by calling the Common Cache Manager. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CACHESUP) + +// +// Local debug trace level +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCompleteMdl) +#pragma alloc_text(PAGE, CdCreateInternalStream) +#pragma alloc_text(PAGE, CdDeleteInternalStream) +#pragma alloc_text(PAGE, CdPurgeVolume) +#endif + + +VOID +CdCreateInternalStream ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This function creates an internal stream file for interaction + with the cache manager. The Fcb here can be for either a + directory stream or for a path table stream. + +Arguments: + + Vcb - Vcb for this volume. + + Fcb - Points to the Fcb for this file. It is either an Index or + Path Table Fcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT StreamFile = NULL; + BOOLEAN DecrementReference = FALSE; + + BOOLEAN CleanupDirContext = FALSE; + BOOLEAN UpdateFcbSizes = FALSE; + + DIRENT Dirent; + DIRENT_ENUM_CONTEXT DirContext; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // We may only have the Fcb shared. Lock the Fcb and do a + // safe test to see if we need to really create the file object. + // + + CdLockFcb( IrpContext, Fcb ); + + if (Fcb->FileObject != NULL) { + + CdUnlockFcb( IrpContext, Fcb ); + return; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Create the internal stream. The Vpb should be pointing at our volume + // device object at this point. + // + + StreamFile = IoCreateStreamFileObject( NULL, Vcb->Vpb->RealDevice ); + + if (StreamFile == NULL) { + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Initialize the fields of the file object. + // + + StreamFile->ReadAccess = TRUE; + StreamFile->WriteAccess = FALSE; + StreamFile->DeleteAccess = FALSE; + + StreamFile->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; + + // + // Set the file object type and increment the Vcb counts. + // + + CdSetFileObject( IrpContext, + StreamFile, + StreamFileOpen, + Fcb, + NULL ); + + // + // We will reference the current Fcb twice to keep it from going + // away in the error path. Otherwise if we dereference it + // below in the finally clause a close could cause the Fcb to + // be deallocated. + // + + CdLockVcb( IrpContext, Vcb ); + CdIncrementReferenceCounts( IrpContext, Fcb, 2, 0 ); + CdUnlockVcb( IrpContext, Vcb ); + DecrementReference = TRUE; + + // + // Initialize the cache map for the file. + // + + CcInitializeCacheMap( StreamFile, + (PCC_FILE_SIZES)&Fcb->AllocationSize, + TRUE, + &CdData.CacheManagerCallbacks, + Fcb ); + + // + // Go ahead and store the stream file into the Fcb. + // + + Fcb->FileObject = StreamFile; + StreamFile = NULL; + + // + // If this is the first file object for a directory then we need to + // read the self entry for this directory and update the sizes + // in the Fcb. We know that the Fcb has been initialized so + // that we have a least one sector available to read. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { + + ULONG NewDataLength; + + // + // Initialize the search structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + CleanupDirContext = TRUE; + + // + // Read the dirent from disk and transfer the data to the + // in-memory dirent. + // + + CdLookupDirent( IrpContext, + Fcb, + Fcb->StreamOffset, + &DirContext ); + + CdUpdateDirentFromRawDirent( IrpContext, Fcb, &DirContext, &Dirent ); + + // + // Verify that this really for the self entry. We do this by + // updating the name in the dirent and then checking that it matches + // one of the hard coded names. + // + + CdUpdateDirentName( IrpContext, &Dirent, FALSE ); + + if (Dirent.CdFileName.FileName.Buffer != CdUnicodeSelfArray) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If the data sizes are different then update the header + // and Mcb for this Fcb. + // + + NewDataLength = BlockAlign( Vcb, Dirent.DataLength + Fcb->StreamOffset ); + + if (NewDataLength == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if (NewDataLength != Fcb->FileSize.QuadPart) { + + Fcb->AllocationSize.QuadPart = + Fcb->FileSize.QuadPart = + Fcb->ValidDataLength.QuadPart = NewDataLength; + + CcSetFileSizes( Fcb->FileObject, (PCC_FILE_SIZES) &Fcb->AllocationSize ); + + CdTruncateAllocation( IrpContext, Fcb, 0 ); + CdAddInitialAllocation( IrpContext, + Fcb, + Dirent.StartingOffset, + NewDataLength ); + + UpdateFcbSizes = TRUE; + } + + // + // Check for the existence flag and transform to hidden. + // + + if (FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_HIDDEN )) { + + SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); + } + + // + // Convert the time to NT time. + // + + CdConvertCdTimeToNtTime( IrpContext, + Dirent.CdTime, + (PLARGE_INTEGER) &Fcb->CreationTime ); + + // + // Update the Fcb flags to indicate we have read the + // self entry. + // + + SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // If we updated the sizes then we want to purge the file. Go + // ahead and unpin and then purge the first page. + // + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + CleanupDirContext = FALSE; + + if (UpdateFcbSizes) { + + CcPurgeCacheSection( &Fcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ); + } + } + + } finally { + + // + // Cleanup any dirent structures we may have used. + // + + if (CleanupDirContext) { + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + } + + // + // If we raised then we need to dereference the file object. + // + + if (StreamFile != NULL) { + + ObDereferenceObject( StreamFile ); + Fcb->FileObject = NULL; + } + + // + // Dereference and unlock the Fcb. + // + + if (DecrementReference) { + + CdLockVcb( IrpContext, Vcb ); + CdDecrementReferenceCounts( IrpContext, Fcb, 1, 0 ); + CdUnlockVcb( IrpContext, Vcb ); + } + + CdUnlockFcb( IrpContext, Fcb ); + } + + return; +} + + +VOID +CdDeleteInternalStream ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This function creates an internal stream file for interaction + with the cache manager. The Fcb here can be for either a + directory stream or for a path table stream. + +Arguments: + + Fcb - Points to the Fcb for this file. It is either an Index or + Path Table Fcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT FileObject; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Lock the Fcb. + // + + CdLockFcb( IrpContext, Fcb ); + + // + // Capture the file object. + // + + FileObject = Fcb->FileObject; + Fcb->FileObject = NULL; + + // + // It is now safe to unlock the Fcb. + // + + CdUnlockFcb( IrpContext, Fcb ); + + // + // Dereference the file object if present. + // + + if (FileObject != NULL) { + + if (FileObject->PrivateCacheMap != NULL) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + ObDereferenceObject( FileObject ); + } + + return; +} + + +NTSTATUS +CdCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the function of completing Mdl reads. + It should be called only from CdFsdRead. + +Arguments: + + Irp - Supplies the originating Irp. + +Return Value: + + NTSTATUS - Will always be STATUS_SUCCESS. + +--*/ + +{ + PFILE_OBJECT FileObject; + + PAGED_CODE(); + + // + // Do completion processing. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + CcMdlReadComplete( FileObject, Irp->MdlAddress ); + + // + // Mdl is now deallocated. + // + + Irp->MdlAddress = NULL; + + // + // Complete the request and exit right away. + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CdPurgeVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN DismountUnderway + ) + +/*++ + +Routine Description: + + This routine is called to purge the volume. The purpose is to make all the stale file + objects in the system go away in order to lock the volume. + + The Vcb is already acquired exclusively. We will lock out all file operations by + acquiring the global file resource. Then we will walk through all of the Fcb's and + perform the purge. + +Arguments: + + Vcb - Vcb for the volume to purge. + + DismountUnderway - Indicates that we are trying to delete all of the objects. + We will purge the Path Table and VolumeDasd and dereference all + internal streams. + +Return Value: + + NTSTATUS - The first failure of the purge operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PVOID RestartKey = NULL; + PFCB ThisFcb = NULL; + PFCB NextFcb; + + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_VCB( Vcb); + + // + // Force any remaining Fcb's in the delayed close queue to be closed. + // + + CdFspClose( Vcb ); + + // + // Acquire the global file resource. + // + + CdAcquireAllFiles( IrpContext, Vcb ); + + // + // Loop through each Fcb in the Fcb Table and perform the flush. + // + + while (TRUE) { + + // + // Lock the Vcb to lookup the next Fcb. + // + + CdLockVcb( IrpContext, Vcb ); + NextFcb = CdGetNextFcb( IrpContext, Vcb, &RestartKey ); + + // + // Reference the NextFcb if present. + // + + if (NextFcb != NULL) { + + NextFcb->FcbReference += 1; + } + + // + // If the last Fcb is present then decrement reference count and call teardown + // to see if it should be removed. + // + + if (ThisFcb != NULL) { + + ThisFcb->FcbReference -= 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Break out of the loop if no more Fcb's. + // + + if (NextFcb == NULL) { + + break; + } + + // + // Move to the next Fcb. + // + + ThisFcb = NextFcb; + + // + // If there is a image section then see if that can be closed. + // + + if (ThisFcb->FcbNonpaged->SegmentObject.ImageSectionObject != NULL) { + + MmFlushImageSection( &ThisFcb->FcbNonpaged->SegmentObject, MmFlushForWrite ); + } + + // + // If there is a data section then purge this. If there is an image + // section then we won't be able to. Remember this if it is our first + // error. + // + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + // + // Dereference the internal stream if dismounting. + // + + if (DismountUnderway && + (SafeNodeType( ThisFcb ) != CDFS_NTC_FCB_DATA) && + (ThisFcb->FileObject != NULL)) { + + CdDeleteInternalStream( IrpContext, ThisFcb ); + } + } + + // + // Now look at the path table and volume Dasd Fcb's. + // + + if (DismountUnderway) { + + if (Vcb->PathTableFcb != NULL) { + + ThisFcb = Vcb->PathTableFcb; + InterlockedIncrement( &Vcb->PathTableFcb->FcbReference ); + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + CdDeleteInternalStream( IrpContext, ThisFcb ); + + InterlockedDecrement( &ThisFcb->FcbReference ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + } + + if (Vcb->VolumeDasdFcb != NULL) { + + ThisFcb = Vcb->VolumeDasdFcb; + InterlockedIncrement( &ThisFcb->FcbReference ); + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + InterlockedDecrement( &ThisFcb->FcbReference ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + } + } + + // + // Release all of the files. + // + + CdReleaseAllFiles( IrpContext, Vcb ); + + return Status; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/cd.h b/reactos/drivers/filesystems/cdfs_new/cd.h new file mode 100755 index 00000000000..69dc11d5227 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cd.h @@ -0,0 +1,534 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cd.h + +Abstract: + + This module defines the on-disk structure of the Cdfs file system. + + +--*/ + +#ifndef _CDFS_ +#define _CDFS_ + +// +// Sector size on Cdrom disks is hard-coded to 2048 +// + +#ifndef SECTOR_SIZE +#define SECTOR_SIZE (2048) +#endif + +#define RAW_SECTOR_SIZE (2352) +#define SECTOR_MASK (SECTOR_SIZE - 1) +#define INVERSE_SECTOR_MASK ~(SECTOR_SIZE - 1) + +#ifndef SECTOR_SHIFT +#define SECTOR_SHIFT (11) +#endif + +#define XA_SECTOR_SIZE (2352) + +// +// Cdfs file id is a large integer. +// + +typedef LARGE_INTEGER FILE_ID; +typedef FILE_ID *PFILE_ID; + +// +// The following constants are values from the disk. +// + +#define FIRST_VD_SECTOR (16) + +#define VOL_ID_LEN (5) +#define ESC_SEQ_LEN (3) + +#define VERSION_1 (1) + +#define VD_TERMINATOR (255) +#define VD_PRIMARY (1) +#define VD_SECONDARY (2) + +#define VOLUME_ID_LENGTH (32) + +// +// Leave the following so that CdfsBoot.c will compile +// + +#define CD_SECTOR_SIZE (2048) + +#define ISO_VOL_ID "CD001" +#define HSG_VOL_ID "CDROM" + +#define ISO_ATTR_MULTI 0x0080 +#define ISO_ATTR_DIRECTORY 0x0002 + +#define MIN_DIR_REC_SIZE (sizeof( RAW_DIR_REC ) - MAX_FILE_ID_LENGTH) + +#define RVD_STD_ID( r, i ) (i ? r->StandardId : \ + ((PRAW_HSG_VD) r)->StandardId ) + +#define RVD_DESC_TYPE( r, i ) (i ? r->DescType : \ + ((PRAW_HSG_VD) r)->DescType ) + +#define RVD_VERSION( r, i ) (i ? r->Version : \ + ((PRAW_HSG_VD) r)->Version ) + +#define RVD_LB_SIZE( r, i ) (i ? r->LogicalBlkSzI : \ + ((PRAW_HSG_VD) r)->LogicalBlkSzI ) + +#define RVD_VOL_SIZE( r, i ) (i ? r->VolSpaceI : \ + ((PRAW_HSG_VD) r)->VolSpaceI ) + +#define RVD_ROOT_DE( r, i ) (i ? r->RootDe : \ + ((PRAW_HSG_VD) r)->RootDe ) + +#define DE_FILE_FLAGS( iso, de ) (iso ? de->FlagsISO : de->FlagsHSG) + +// +// Data track flag for track entries in TOC +// + +#define TOC_DATA_TRACK (0x04) +#define TOC_LAST_TRACK (0xaa) + + +// +// There is considerable rearrangement of the volume descriptors for +// ISO and HSG. However, within each standard the same structure can +// be used for both the primary and secondary descriptors. +// +// Both of these structures are aligned correctly so that no +// special macros will be needed to unpack them. +// + +// +// Declaration of length of root directory entry in volume descriptor +// + +#define LEN_ROOT_DE (34) + +// +// Maximum length of file ID on the disk. We allow file size beyond the ISO 9660 +// standard. +// + +#define MAX_FILE_ID_LENGTH (255) + + +typedef struct _RAW_ISO_VD { + + UCHAR DescType; // volume type: 1 = standard, 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CD001 + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[2]; // LBN of 2 path tables Intel + ULONG PathTabLocM[2]; // LBN of 2 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[37]; // file name of copyright notice + UCHAR Abstract[37]; // file name of abstract + UCHAR Bibliograph[37]; // file name of bibliography + UCHAR CreateDate[17]; // volume creation date and time + UCHAR ModDate[17]; // volume modification date and time + UCHAR ExpireDate[17]; // volume expiration date and time + UCHAR EffectDate[17]; // volume effective date and time + UCHAR FileStructVer; // file structure version number = 1 + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[653]; // remainder of 2048 bytes reserved + +} RAW_ISO_VD; +typedef RAW_ISO_VD *PRAW_ISO_VD; + + +typedef struct _RAW_HSG_VD { + + ULONG BlkNumI; // logical block number Intel + ULONG BlkNumM; // logical block number Motorola + UCHAR DescType; // volume type: 1 = standard, 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CDROM + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[4]; // LBN of 4 path tables Intel + ULONG PathTabLocM[4]; // LBN of 4 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[32]; // file name of copyright notice + UCHAR Abstract[32]; // file name of abstract + UCHAR CreateDate[16]; // volume creation date and time + UCHAR ModDate[16]; // volume modification date and time + UCHAR ExpireDate[16]; // volume expiration date and time + UCHAR EffectDate[16]; // volume effective date and time + UCHAR FileStructVer; // file structure version number + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[680]; // remainder of 2048 bytes reserved + +} RAW_HSG_VD; +typedef RAW_HSG_VD *PRAW_HSG_VD; + + +typedef struct _RAW_JOLIET_VD { + + UCHAR DescType; // volume type: 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CD001 + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII, Joliett Seq here + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[2]; // LBN of 2 path tables Intel + ULONG PathTabLocM[2]; // LBN of 2 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[37]; // file name of copyright notice + UCHAR Abstract[37]; // file name of abstract + UCHAR Bibliograph[37]; // file name of bibliography + UCHAR CreateDate[17]; // volume creation date and time + UCHAR ModDate[17]; // volume modification date and time + UCHAR ExpireDate[17]; // volume expiration date and time + UCHAR EffectDate[17]; // volume effective date and time + UCHAR FileStructVer; // file structure version number = 1 + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[653]; // remainder of 2048 bytes reserved + +} RAW_JOLIET_VD; +typedef RAW_JOLIET_VD *PRAW_JOLIET_VD; + +// +// Macros to access the different volume descriptors. +// + +#define CdRvdId(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->StandardId : \ + ((PRAW_ISO_VD) (R))->StandardId \ +) + +#define CdRvdVersion(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->Version : \ + ((PRAW_ISO_VD) (R))->Version \ +) + +#define CdRvdDescType(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->DescType : \ + ((PRAW_ISO_VD) (R))->DescType \ +) + +#define CdRvdEsc(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->CharSet : \ + ((PRAW_ISO_VD) (R))->CharSet \ +) + +#define CdRvdVolId(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->VolumeId : \ + ((PRAW_ISO_VD) (R))->VolumeId \ +) + +#define CdRvdBlkSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->LogicalBlkSzI :\ + ((PRAW_ISO_VD) (R))->LogicalBlkSzI \ +) + +#define CdRvdPtLoc(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->PathTabLocI[0]:\ + ((PRAW_ISO_VD) (R))->PathTabLocI[0] \ +) + +#define CdRvdPtSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->PathTableSzI : \ + ((PRAW_ISO_VD) (R))->PathTableSzI \ +) + +#define CdRvdDirent(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->RootDe : \ + ((PRAW_ISO_VD) (R))->RootDe \ +) + +#define CdRvdVolSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->VolSpaceI : \ + ((PRAW_ISO_VD) (R))->VolSpaceI \ +) + + +// +// This structure is used to overlay a region of a disk sector +// to retrieve a single directory entry. There is a difference +// in the file flags between the ISO and HSG version and a +// additional byte in the ISO for the offset from Greenwich time. +// +// The disk structure is aligned on a word boundary, so any 32 +// bit fields will be represented as an array of 16 bit fields. +// + +typedef struct _RAW_DIRENT { + + UCHAR DirLen; + UCHAR XarLen; + UCHAR FileLoc[4]; + UCHAR FileLocMot[4]; + UCHAR DataLen[4]; + UCHAR DataLenMot[4]; + UCHAR RecordTime[6]; + UCHAR FlagsHSG; + UCHAR FlagsISO; + UCHAR IntLeaveSize; + UCHAR IntLeaveSkip; + UCHAR Vssn[2]; + UCHAR VssnMot[2]; + UCHAR FileIdLen; + UCHAR FileId[MAX_FILE_ID_LENGTH]; + +} RAW_DIRENT; +typedef RAW_DIRENT RAW_DIR_REC; +typedef RAW_DIRENT *PRAW_DIR_REC; +typedef RAW_DIRENT *PRAW_DIRENT; + +#define CD_ATTRIBUTE_HIDDEN (0x01) +#define CD_ATTRIBUTE_DIRECTORY (0x02) +#define CD_ATTRIBUTE_ASSOC (0x04) +#define CD_ATTRIBUTE_MULTI (0x80) + +#define CD_BASE_YEAR (1900) + +#define MIN_RAW_DIRENT_LEN (FIELD_OFFSET( RAW_DIRENT, FileId ) + 1) + +#define BYTE_COUNT_8_DOT_3 (24) + +#define SHORT_NAME_SHIFT (5) + +// +// The following macro recovers the correct flag field. +// + +#define CdRawDirentFlags(IC,RD) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG) ? \ + (RD)->FlagsHSG : \ + (RD)->FlagsISO \ +) + +// +// The following macro converts from CD time to NT time. On ISO +// 9660 media, we now pay attention to the GMT offset (integer +// increments of 15 minutes offset from GMT). HSG does not record +// this field. +// +// The restriction to the interval [-48, 52] comes from 9660 8.4.26.1 +// +// VOID +// CdConvertCdTimeToNtTime ( +// IN PIRP_CONTEXT IrpContext, +// IN PCHAR CdTime, +// OUT PLARGE_INTEGER NtTime +// ); +// + +#define GMT_OFFSET_TO_NT ((LONGLONG) 15 * 60 * 1000 * 1000 * 10) + +#define CdConvertCdTimeToNtTime(IC,CD,NT) { \ + TIME_FIELDS _TimeField; \ + CHAR GmtOffset; \ + _TimeField.Year = (CSHORT) *((PCHAR) CD) + CD_BASE_YEAR; \ + _TimeField.Month = (CSHORT) *(Add2Ptr( CD, 1, PCHAR )); \ + _TimeField .Day = (CSHORT) *(Add2Ptr( CD, 2, PCHAR )); \ + _TimeField.Hour = (CSHORT) *(Add2Ptr( CD, 3, PCHAR )); \ + _TimeField.Minute = (CSHORT) *(Add2Ptr( CD, 4, PCHAR )); \ + _TimeField.Second = (CSHORT) *(Add2Ptr( CD, 5, PCHAR )); \ + _TimeField.Milliseconds = (CSHORT) 0; \ + RtlTimeFieldsToTime( &_TimeField, NT ); \ + if (!FlagOn((IC)->Vcb->VcbState, VCB_STATE_HSG) && \ + ((GmtOffset = *(Add2Ptr( CD, 6, PCHAR ))) != 0 ) && \ + (GmtOffset >= -48 && GmtOffset <= 52)) { \ + (NT)->QuadPart += -GmtOffset * GMT_OFFSET_TO_NT; \ + } \ +} + + +// +// The on-disk representation of a Path Table entry differs between +// the ISO version and the HSG version. The fields are the same +// and the same size, but the positions are different. +// + +typedef struct _RAW_PATH_ISO { + + UCHAR DirIdLen; + UCHAR XarLen; + USHORT DirLoc[2]; + USHORT ParentNum; + UCHAR DirId[MAX_FILE_ID_LENGTH]; + +} RAW_PATH_ISO; +typedef RAW_PATH_ISO *PRAW_PATH_ISO; +typedef RAW_PATH_ISO RAW_PATH_ENTRY; +typedef RAW_PATH_ISO *PRAW_PATH_ENTRY; + +typedef struct _RAW_PATH_HSG { + + USHORT DirLoc[2]; + UCHAR XarLen; + UCHAR DirIdLen; + USHORT ParentNum; + UCHAR DirId[MAX_FILE_ID_LENGTH]; + +} RAW_PATH_HSG; +typedef RAW_PATH_HSG *PRAW_PATH_HSG; + +#define MIN_RAW_PATH_ENTRY_LEN (FIELD_OFFSET( RAW_PATH_ENTRY, DirId ) + 1) + +// +// The following macros are used to recover the different fields of the +// Path Table entries. The macro to recover the disk location of the +// directory must copy it into a different variable for alignment reasons. +// +// CdRawPathIdLen - Length of directory name in bytes +// CdRawPathXar - Number of Xar blocks +// CdRawPathLoc - Address of unaligned ulong for disk offset in blocks +// + +#define CdRawPathIdLen(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->DirIdLen : \ + (RP)->DirIdLen \ +) + +#define CdRawPathXar(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->XarLen : \ + (RP)->XarLen \ +) + +#define CdRawPathLoc(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->DirLoc : \ + (RP)->DirLoc \ +) + + +// +// System use are for XA data. The following is the system use area for +// directory entries on XA data disks. +// + +typedef struct _SYSTEM_USE_XA { + + // + // Owner ID. Not used in this version. + // + + UCHAR OwnerId[4]; + + // + // Extent attributes. Only interested if mode2 form2 or digital audio. + // This is stored big endian. We will define the attribute flags so + // we can ignore this fact. + // + + USHORT Attributes; + + // + // XA signature. This value must be 'XA'. + // + + USHORT Signature; + + // + // File Number. + // + + UCHAR FileNumber; + + // + // Not used in this version. + // + + UCHAR Reserved[5]; + +} SYSTEM_USE_XA; +typedef SYSTEM_USE_XA *PSYSTEM_USE_XA; + +#define SYSTEM_USE_XA_FORM1 (0x0008) +#define SYSTEM_USE_XA_FORM2 (0x0010) +#define SYSTEM_USE_XA_DA (0x0040) + +#define SYSTEM_XA_SIGNATURE (0x4158) + +typedef enum _XA_EXTENT_TYPE { + + Form1Data = 0, + Mode2Form2Data, + CDAudio + +} XA_EXTENT_TYPE; +typedef XA_EXTENT_TYPE *PXA_EXTENT_TYPE; + +#endif // _CDFS_ + + + diff --git a/reactos/drivers/filesystems/cdfs_new/cddata.c b/reactos/drivers/filesystems/cdfs_new/cddata.c new file mode 100755 index 00000000000..f22975db30f --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cddata.c @@ -0,0 +1,1151 @@ + +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdData.c + +Abstract: + + This module declares the global data used by the Cdfs file system. + + This module also handles the dispath routines in the Fsd threads as well as + handling the IrpContext and Irp through the exception path. + + +--*/ + +#include "CdProcs.h" + +#ifdef CD_SANITY +BOOLEAN CdTestTopLevel = TRUE; +BOOLEAN CdTestRaisedStatus = TRUE; +BOOLEAN CdBreakOnAnyRaise = FALSE; +BOOLEAN CdTraceRaises = FALSE; +NTSTATUS CdInterestingExceptionCodes[] = { STATUS_DISK_CORRUPT_ERROR, + STATUS_FILE_CORRUPT_ERROR, + 0, 0, 0, 0, 0, 0, 0, 0 }; +#endif + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CDDATA) + +// +// Global data structures +// + +CD_DATA CdData; +FAST_IO_DISPATCH CdFastIoDispatch; + +// +// Reserved directory strings. +// + +WCHAR CdUnicodeSelfArray[] = { L'.' }; +WCHAR CdUnicodeParentArray[] = { L'.', L'.' }; + +UNICODE_STRING CdUnicodeDirectoryNames[] = { + { 2, 2, CdUnicodeSelfArray}, + { 4, 4, CdUnicodeParentArray} +}; + +// +// Volume descriptor identifier strings. +// + +CHAR CdHsgId[] = { 'C', 'D', 'R', 'O', 'M' }; +CHAR CdIsoId[] = { 'C', 'D', '0', '0', '1' }; +CHAR CdXaId[] = { 'C', 'D', '-', 'X', 'A', '0', '0', '1' }; + +// +// Volume label for audio disks. +// + +WCHAR CdAudioLabel[] = { L'A', L'u', L'd', L'i', L'o', L' ', L'C', L'D' }; +USHORT CdAudioLabelLength = sizeof( CdAudioLabel ); + +// +// Pseudo file names for audio disks. +// + +CHAR CdAudioFileName[] = { 'T', 'r', 'a', 'c', 'k', '0', '0', '.', 'c', 'd', 'a' }; +UCHAR CdAudioFileNameLength = sizeof( CdAudioFileName ); +ULONG CdAudioDirentSize = FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ) + sizeof( SYSTEM_USE_XA ); +ULONG CdAudioDirentsPerSector = SECTOR_SIZE / (FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ) + sizeof( SYSTEM_USE_XA )); +ULONG CdAudioSystemUseOffset = FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ); + +// +// Escape sequences for mounting Unicode volumes. +// + +PCHAR CdJolietEscape[] = { "%/@", "%/C", "%/E" }; + +// +// Audio Play Files consist completely of this header block. These +// files are readable in the root of any audio disc regardless of +// the capabilities of the drive. +// +// The "Unique Disk ID Number" is a calculated value consisting of +// a combination of parameters, including the number of tracks and +// the starting locations of those tracks. +// +// Applications interpreting CDDA RIFF files should be advised that +// additional RIFF file chunks may be added to this header in the +// future in order to add information, such as the disk and song title. +// + +LONG CdAudioPlayHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + 4 * 11 - 8, // Chunk Size = (file size - 8) + 0x41444443, // 'CDDA' + 0x20746d66, // 'fmt ' + 24, // Chunk Size (of 'fmt ' subchunk) = 24 + 0x00000001, // WORD Format Tag, WORD Track Number + 0x00000000, // DWORD Unique Disk ID Number + 0x00000000, // DWORD Track Starting Sector (LBN) + 0x00000000, // DWORD Track Length (LBN count) + 0x00000000, // DWORD Track Starting Sector (MSF) + 0x00000000 // DWORD Track Length (MSF) +}; + +// Audio Philes begin with this header block to identify the data as a +// PCM waveform. AudioPhileHeader is coded as if it has no data included +// in the waveform. Data must be added in 2352-byte multiples. +// +// Fields marked 'ADJUST' need to be adjusted based on the size of the +// data: Add (nSectors*2352) to the DWORDs at offsets 1*4 and 10*4. +// +// File Size of TRACK??.WAV = nSectors*2352 + sizeof(AudioPhileHeader) +// RIFF('WAVE' fmt(1, 2, 44100, 176400, 16, 4) data( ) +// +// The number of sectors in a CD-XA CD-DA file is (DataLen/2048). +// CDFS will expose these files to applications as if they were just +// 'WAVE' files, adjusting the file size so that the RIFF file is valid. +// +// NT NOTE: We do not do any fidelity adjustment. These are presented as raw +// 2352 byte sectors - 95 has the glimmer of an idea to allow CDFS to expose +// the CDXA CDDA data at different sampling rates in a virtual directory +// structure, but we will never do that. +// + +LONG CdXAAudioPhileHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + -8, // Chunk Size = (file size - 8) ADJUST1 + 0x45564157, // 'WAVE' + 0x20746d66, // 'fmt ' + 16, // Chunk Size (of 'fmt ' subchunk) = 16 + 0x00020001, // WORD Format Tag WORD nChannels + 44100, // DWORD nSamplesPerSecond + 2352 * 75, // DWORD nAvgBytesPerSec + 0x00100004, // WORD nBlockAlign WORD nBitsPerSample + 0x61746164, // 'data' + -44 // ADJUST2 +}; + +// +// XA Files begin with this RIFF header block to identify the data as +// raw CD-XA sectors. Data must be added in 2352-byte multiples. +// +// This header is added to all CD-XA files which are marked as having +// mode2form2 sectors. +// +// Fields marked 'ADJUST' need to be adjusted based on the size of the +// data: Add file size to the marked DWORDS. +// +// File Size of TRACK??.WAV = nSectors*2352 + sizeof(XAFileHeader) +// +// RIFF('CDXA' FMT(Owner, Attr, 'X', 'A', FileNum, 0) data ( ) +// + +LONG CdXAFileHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + -8, // Chunk Size = (file size - 8) ADJUST + 0x41584443, // 'CDXA' + 0x20746d66, // 'fmt ' + 16, // Chunk Size (of CDXA chunk) = 16 + 0, // DWORD Owner ID + 0x41580000, // WORD Attributes + // BYTE Signature byte 1 'X' + // BYTE Signature byte 2 'A' + 0, // BYTE File Number + 0, // BYTE Reserved[7] + 0x61746164, // 'data' + -44 // ADJUST +}; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFastIoCheckIfPossible) +#pragma alloc_text(PAGE, CdSerial32) +#endif + + +NTSTATUS +CdFsdDispatch ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the driver entry to all of the Fsd dispatch points. + + Conceptually the Io routine will call this routine on all requests + to the file system. We case on the type of request and invoke the + correct handler for this type of request. There is an exception filter + to catch any exceptions in the CDFS code as well as the CDFS process + exception routine. + + This routine allocates and initializes the IrpContext for this request as + well as updating the top-level thread context as necessary. We may loop + in this routine if we need to retry the request for any reason. The + status code STATUS_CANT_WAIT is used to indicate this. Suppose the disk + in the drive has changed. An Fsd request will proceed normally until it + recognizes this condition. STATUS_VERIFY_REQUIRED is raised at that point + and the exception code will handle the verify and either return + STATUS_CANT_WAIT or STATUS_PENDING depending on whether the request was + posted. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object for this request + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + THREAD_CONTEXT ThreadContext; + PIRP_CONTEXT IrpContext = NULL; + BOOLEAN Wait; + +#ifdef CD_SANITY + PVOID PreviousTopLevel; +#endif + + NTSTATUS Status; + +#if DBG + + KIRQL SaveIrql = KeGetCurrentIrql(); + +#endif + + ASSERT_OPTIONAL_IRP( Irp ); + + FsRtlEnterFileSystem(); + +#ifdef CD_SANITY + PreviousTopLevel = IoGetTopLevelIrp(); +#endif + + // + // Loop until this request has been completed or posted. + // + + do { + + // + // Use a try-except to handle the exception cases. + // + + try { + + // + // If the IrpContext is NULL then this is the first pass through + // this loop. + // + + if (IrpContext == NULL) { + + // + // Decide if this request is waitable an allocate the IrpContext. + // If the file object in the stack location is NULL then this + // is a mount which is always waitable. Otherwise we look at + // the file object flags. + // + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + IrpContext = CdCreateIrpContext( Irp, Wait ); + + // + // Update the thread context information. + // + + CdSetThreadContext( IrpContext, &ThreadContext ); + +#ifdef CD_SANITY + ASSERT( !CdTestTopLevel || + SafeNodeType( IrpContext->TopLevel ) == CDFS_NTC_IRP_CONTEXT ); +#endif + + // + // Otherwise cleanup the IrpContext for the retry. + // + + } else { + + // + // Set the MORE_PROCESSING flag to make sure the IrpContext + // isn't inadvertently deleted here. Then cleanup the + // IrpContext to perform the retry. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, FALSE ); + } + + // + // Case on the major irp code. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + Status = CdCommonCreate( IrpContext, Irp ); + break; + + case IRP_MJ_CLOSE : + + Status = CdCommonClose( IrpContext, Irp ); + break; + + case IRP_MJ_READ : + + // + // If this is an Mdl complete request, don't go through + // common read. + // + + if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) { + + Status = CdCompleteMdl( IrpContext, Irp ); + + } else { + + Status = CdCommonRead( IrpContext, Irp ); + } + + break; + + case IRP_MJ_QUERY_INFORMATION : + + Status = CdCommonQueryInfo( IrpContext, Irp ); + break; + + case IRP_MJ_SET_INFORMATION : + + Status = CdCommonSetInfo( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_VOLUME_INFORMATION : + + Status = CdCommonQueryVolInfo( IrpContext, Irp ); + break; + + case IRP_MJ_DIRECTORY_CONTROL : + + Status = CdCommonDirControl( IrpContext, Irp ); + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL : + + Status = CdCommonFsControl( IrpContext, Irp ); + break; + + case IRP_MJ_DEVICE_CONTROL : + + Status = CdCommonDevControl( IrpContext, Irp ); + break; + + case IRP_MJ_LOCK_CONTROL : + + Status = CdCommonLockControl( IrpContext, Irp ); + break; + + case IRP_MJ_CLEANUP : + + Status = CdCommonCleanup( IrpContext, Irp ); + break; + + case IRP_MJ_PNP : + + Status = CdCommonPnp( IrpContext, Irp ); + break; + + default : + + Status = STATUS_INVALID_DEVICE_REQUEST; + CdCompleteRequest( IrpContext, Irp, Status ); + } + + } except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + } while (Status == STATUS_CANT_WAIT); + +#ifdef CD_SANITY + ASSERT( !CdTestTopLevel || + (PreviousTopLevel == IoGetTopLevelIrp()) ); +#endif + + FsRtlExitFileSystem(); + + ASSERT( SaveIrql == KeGetCurrentIrql( )); + + return Status; +} + + +#ifdef CD_SANITY + +VOID +CdRaiseStatusEx( + IN PIRP_CONTEXT IrpContext, + IN NTSTATUS Status, + IN BOOLEAN NormalizeStatus, + IN OPTIONAL ULONG FileId, + IN OPTIONAL ULONG Line + ) +{ + BOOLEAN BreakIn = FALSE; + + AssertVerifyDevice( IrpContext, Status); + + if (CdTraceRaises) { + + DbgPrint( "%p CdRaiseStatusEx 0x%x @ fid %d, line %d\n", PsGetCurrentThread(), Status, FileId, Line); + } + + if (CdTestRaisedStatus && !CdBreakOnAnyRaise) { + + ULONG Index; + + for (Index = 0; + Index < (sizeof( CdInterestingExceptionCodes) / sizeof( CdInterestingExceptionCodes[0])); + Index++) { + + if ((STATUS_SUCCESS != CdInterestingExceptionCodes[Index]) && + (CdInterestingExceptionCodes[Index] == Status)) { + + BreakIn = TRUE; + break; + } + } + } + + if (BreakIn || CdBreakOnAnyRaise) { + + DbgPrint( "CDFS: Breaking on raised status %08x (BI=%d,BA=%d)\n", Status, BreakIn, CdBreakOnAnyRaise); + DbgPrint( "CDFS: (FILEID %d LINE %d)\n", FileId, Line); + DbgPrint( "CDFS: Contact CDFS.SYS component owner for triage.\n"); + DbgPrint( "CDFS: 'eb %p 0;eb %p 0' to disable this alert.\n", &CdTestRaisedStatus, &CdBreakOnAnyRaise); + + DbgBreakPoint(); + } + + if (NormalizeStatus) { + + IrpContext->ExceptionStatus = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR); + } + else { + + IrpContext->ExceptionStatus = Status; + } + + IrpContext->RaisedAtLineFile = (FileId << 16) | Line; + + ExRaiseStatus( IrpContext->ExceptionStatus); +} + +#endif + + +LONG +CdExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + This routine is used to decide whether we will handle a raised exception + status. If CDFS explicitly raised an error then this status is already + in the IrpContext. We choose which is the correct status code and + either indicate that we will handle the exception or bug-check the system. + +Arguments: + + ExceptionCode - Supplies the exception code to being checked. + +Return Value: + + ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks + +--*/ + +{ + NTSTATUS ExceptionCode; + BOOLEAN TestStatus = TRUE; + + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + + ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code + // from the exception record. + // + + if ((ExceptionCode == STATUS_IN_PAGE_ERROR) && + (ExceptionPointer->ExceptionRecord->NumberParameters >= 3)) { + + ExceptionCode = + (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2]; + } + + // + // If there is an Irp context then check which status code to use. + // + + if (ARGUMENT_PRESENT( IrpContext )) { + + if (IrpContext->ExceptionStatus == STATUS_SUCCESS) { + + // + // Store the real status into the IrpContext. + // + + IrpContext->ExceptionStatus = ExceptionCode; + + } else { + + // + // No need to test the status code if we raised it ourselves. + // + + TestStatus = FALSE; + } + } + + AssertVerifyDevice( IrpContext, IrpContext->ExceptionStatus ); + + // + // Bug check if this status is not supported. + // + + if (TestStatus && !FsRtlIsNtstatusExpected( ExceptionCode )) { + + CdBugCheck( (ULONG_PTR) ExceptionPointer->ExceptionRecord, + (ULONG_PTR) ExceptionPointer->ContextRecord, + (ULONG_PTR) ExceptionPointer->ExceptionRecord->ExceptionAddress ); + + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + +NTSTATUS +CdProcessException ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ) + +/*++ + +Routine Description: + + This routine processes an exception. It either completes the request + with the exception status in the IrpContext, sends this off to the Fsp + workque or causes it to be retried in the current thread if a verification + is needed. + + If the volume needs to be verified (STATUS_VERIFY_REQUIRED) and we can + do the work in the current thread we will translate the status code + to STATUS_CANT_WAIT to indicate that we need to retry the request. + +Arguments: + + Irp - Supplies the Irp being processed + + ExceptionCode - Supplies the normalized exception status being handled + +Return Value: + + NTSTATUS - Returns the results of either posting the Irp or the + saved completion status. + +--*/ + +{ + PDEVICE_OBJECT Device; + PVPB Vpb; + PETHREAD Thread; + + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If there is not an irp context, then complete the request with the + // current status code. + // + + if (!ARGUMENT_PRESENT( IrpContext )) { + + CdCompleteRequest( NULL, Irp, ExceptionCode ); + return ExceptionCode; + } + + // + // Get the real exception status from the IrpContext. + // + + ExceptionCode = IrpContext->ExceptionStatus; + + // + // If we are not a top level request then we just complete the request + // with the current status code. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + return ExceptionCode; + } + + // + // Check if we are posting this request. One of the following must be true + // if we are to post a request. + // + // - Status code is STATUS_CANT_WAIT and the request is asynchronous + // or we are forcing this to be posted. + // + // - Status code is STATUS_VERIFY_REQUIRED and we are at APC level + // or higher. Can't wait for IO in the verify path in this case. + // + // Set the MORE_PROCESSING flag in the IrpContext to keep if from being + // deleted if this is a retryable condition. + // + // + // Note that (children of) CdFsdPostRequest can raise (Mdl allocation). + // + + try { + + if (ExceptionCode == STATUS_CANT_WAIT) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST )) { + + ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); + } + + } else if (ExceptionCode == STATUS_VERIFY_REQUIRED) { + + if (KeGetCurrentIrql() >= APC_LEVEL) { + + ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); + } + } + } + except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + ExceptionCode = GetExceptionCode(); + } + + // + // If we posted the request or our caller will retry then just return here. + // + + if ((ExceptionCode == STATUS_PENDING) || + (ExceptionCode == STATUS_CANT_WAIT)) { + + return ExceptionCode; + } + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + + // + // Store this error into the Irp for posting back to the Io system. + // + + Irp->IoStatus.Status = ExceptionCode; + + if (IoIsErrorUserInduced( ExceptionCode )) { + + // + // Check for the various error conditions that can be caused by, + // and possibly resolved my the user. + // + + if (ExceptionCode == STATUS_VERIFY_REQUIRED) { + + // + // Now we are at the top level file system entry point. + // + // If we have already posted this request then the device to + // verify is in the original thread. Find this via the Irp. + // + + Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); + IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); + + // + // If there is no device in that location then check in the + // current thread. + // + + if (Device == NULL) { + + Device = IoGetDeviceToVerify( PsGetCurrentThread() ); + IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); + + ASSERT( Device != NULL ); + + // + // Let's not BugCheck just because the driver messes up. + // + + if (Device == NULL) { + + ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + } + + // + // CdPerformVerify() will do the right thing with the Irp. + // If we return STATUS_CANT_WAIT then the current thread + // can retry the request. + // + + return CdPerformVerify( IrpContext, Irp, Device ); + } + + // + // The other user induced conditions generate an error unless + // they have been disabled for this request. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS )) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + + } + // + // Generate a pop-up. + // + else { + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) { + + Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb; + + } else { + + Vpb = NULL; + } + + // + // The device to verify is either in my thread local storage + // or that of the thread that owns the Irp. + // + + Thread = Irp->Tail.Overlay.Thread; + Device = IoGetDeviceToVerify( Thread ); + + if (Device == NULL) { + + Thread = PsGetCurrentThread(); + Device = IoGetDeviceToVerify( Thread ); + + ASSERT( Device != NULL ); + + // + // Let's not BugCheck just because the driver messes up. + // + + if (Device == NULL) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + } + + // + // This routine actually causes the pop-up. It usually + // does this by queuing an APC to the callers thread, + // but in some cases it will complete the request immediately, + // so it is very important to IoMarkIrpPending() first. + // + + IoMarkIrpPending( Irp ); + IoRaiseHardError( Irp, Vpb, Device ); + + // + // We will be handing control back to the caller here, so + // reset the saved device object. + // + + IoSetDeviceToVerify( Thread, NULL ); + + // + // The Irp will be completed by Io or resubmitted. In either + // case we must clean up the IrpContext here. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + return STATUS_PENDING; + } + } + + // + // This is just a run of the mill error. + // + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; +} + + +VOID +CdCompleteRequest ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp OPTIONAL, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine completes a Irp and cleans up the IrpContext. Either or + both of these may not be specified. + +Arguments: + + Irp - Supplies the Irp being processed. + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + ASSERT_OPTIONAL_IRP( Irp ); + + // + // Cleanup the IrpContext if passed in here. + // + + if (ARGUMENT_PRESENT( IrpContext )) { + + CdCleanupIrpContext( IrpContext, FALSE ); + } + + // + // If we have an Irp then complete the irp. + // + + if (ARGUMENT_PRESENT( Irp )) { + + // + // Clear the information field in case we have used this Irp + // internally. + // + + if (NT_ERROR( Status ) && + FlagOn( Irp->Flags, IRP_INPUT_OPERATION )) { + + Irp->IoStatus.Information = 0; + } + + Irp->IoStatus.Status = Status; + + AssertVerifyDeviceIrp( Irp ); + + IoCompleteRequest( Irp, IO_CD_ROM_INCREMENT ); + } + + return; +} + + +VOID +CdSetThreadContext ( + IN PIRP_CONTEXT IrpContext, + IN PTHREAD_CONTEXT ThreadContext + ) + +/*++ + +Routine Description: + + This routine is called at each Fsd/Fsp entry point set up the IrpContext + and thread local storage to track top level requests. If there is + not a Cdfs context in the thread local storage then we use the input one. + Otherwise we use the one already there. This routine also updates the + IrpContext based on the state of the top-level context. + + If the TOP_LEVEL flag in the IrpContext is already set when we are called + then we force this request to appear top level. + +Arguments: + + ThreadContext - Address on stack for local storage if not already present. + + ForceTopLevel - We force this request to appear top level regardless of + any previous stack value. + +Return Value: + + None + +--*/ + +{ + PTHREAD_CONTEXT CurrentThreadContext; + ULONG_PTR StackTop; + ULONG_PTR StackBottom; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + + // + // Get the current top-level irp out of the thread storage. + // If NULL then this is the top-level request. + // + + CurrentThreadContext = (PTHREAD_CONTEXT) IoGetTopLevelIrp(); + + if (CurrentThreadContext == NULL) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ); + } + + // + // Initialize the input context unless we are using the current + // thread context block. We use the new block if our caller + // specified this or the existing block is invalid. + // + // The following must be true for the current to be a valid Cdfs context. + // + // Structure must lie within current stack. + // Address must be ULONG aligned. + // Cdfs signature must be present. + // + // If this is not a valid Cdfs context then use the input thread + // context and store it in the top level context. + // + + IoGetStackLimits( &StackTop, &StackBottom); + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) || + (((ULONG_PTR) CurrentThreadContext > StackBottom - sizeof( THREAD_CONTEXT )) || + ((ULONG_PTR) CurrentThreadContext <= StackTop) || + FlagOn( (ULONG_PTR) CurrentThreadContext, 0x3 ) || + (CurrentThreadContext->Cdfs != 0x53464443))) { + + ThreadContext->Cdfs = 0x53464443; + ThreadContext->SavedTopLevelIrp = (PIRP) CurrentThreadContext; + ThreadContext->TopLevelIrpContext = IrpContext; + IoSetTopLevelIrp( (PIRP) ThreadContext ); + + IrpContext->TopLevel = IrpContext; + IrpContext->ThreadContext = ThreadContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS ); + + // + // Otherwise use the IrpContext in the thread context. + // + + } else { + + IrpContext->TopLevel = CurrentThreadContext->TopLevelIrpContext; + } + + return; +} + + +BOOLEAN +CdFastIoCheckIfPossible ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN BOOLEAN CheckForReadOperation, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine checks if fast i/o is possible for a read/write operation + +Arguments: + + FileObject - Supplies the file object used in the query + + FileOffset - Supplies the starting byte offset for the read/write operation + + Length - Supplies the length, in bytes, of the read/write operation + + Wait - Indicates if we can wait + + LockKey - Supplies the lock key + + CheckForReadOperation - Indicates if this is a check for a read or write + operation + + IoStatus - Receives the status of the operation if our return value is + FastIoReturnError + +Return Value: + + BOOLEAN - TRUE if fast I/O is possible and FALSE if the caller needs + to take the long route. + +--*/ + +{ + PFCB Fcb; + TYPE_OF_OPEN TypeOfOpen; + LARGE_INTEGER LargeLength; + + PAGED_CODE(); + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if ((TypeOfOpen != UserFileOpen) || !CheckForReadOperation) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + LargeLength.QuadPart = Length; + + // + // Check whether the file locks will allow for fast io. + // + + if ((Fcb->FileLock == NULL) || + FsRtlFastCheckLockForRead( Fcb->FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + + return FALSE; +} + + +ULONG +CdSerial32 ( + IN PCHAR Buffer, + IN ULONG ByteCount + ) +/*++ + +Routine Description: + + This routine is called to generate a 32 bit serial number. This is + done by doing four separate checksums into an array of bytes and + then treating the bytes as a ULONG. + +Arguments: + + Buffer - Pointer to the buffer to generate the ID for. + + ByteCount - Number of bytes in the buffer. + +Return Value: + + ULONG - The 32 bit serial number. + +--*/ + +{ + union { + UCHAR Bytes[4]; + ULONG SerialId; + } Checksum; + + PAGED_CODE(); + + // + // Initialize the serial number. + // + + Checksum.SerialId = 0; + + // + // Continue while there are more bytes to use. + // + + while (ByteCount--) { + + // + // Increment this sub-checksum. + // + + Checksum.Bytes[ByteCount & 0x3] += *(Buffer++); + } + + // + // Return the checksums as a ULONG. + // + + return Checksum.SerialId; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/cddata.h b/reactos/drivers/filesystems/cdfs_new/cddata.h new file mode 100755 index 00000000000..0577a4ca302 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cddata.h @@ -0,0 +1,278 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdData.c + +Abstract: + + This module declares the global data used by the Cdfs file system. + + +--*/ + +#ifndef _CDDATA_ +#define _CDDATA_ + +// +// Global data structures +// + +extern CD_DATA CdData; +extern FAST_IO_DISPATCH CdFastIoDispatch; + +// +// Global constants +// + +// +// This is the number of times a mounted Vcb will be referenced on behalf +// of the system. The counts include the following references. +// +// 1 reference - shows the volume is mounted +// 1 reference - 1 for VolumeDasdFcb. +// 2 references - 1 for RootIndexFcb, 1 for internal stream. +// 2 references - 1 for PathTableFcb, 1 for internal stream. +// +// For user references we add one for the reference in each of the internal +// Fcb's. +// + +#define CDFS_RESIDUAL_REFERENCE (6) +#define CDFS_RESIDUAL_USER_REFERENCE (3) + +// +// Reserved directory strings +// + +extern WCHAR CdUnicodeSelfArray[]; +extern WCHAR CdUnicodeParentArray[]; + +extern UNICODE_STRING CdUnicodeDirectoryNames[]; + +// +// Volume descriptor identifier strings. +// + +extern CHAR CdHsgId[]; +extern CHAR CdIsoId[]; +extern CHAR CdXaId[]; + +// +// Volume label for audio disks. +// + +extern WCHAR CdAudioLabel[]; +extern USHORT CdAudioLabelLength; + +// +// Pseudo file names for audio disks. +// + +extern CHAR CdAudioFileName[]; +extern UCHAR CdAudioFileNameLength; +extern ULONG CdAudioDirentSize; +extern ULONG CdAudioDirentsPerSector; +extern ULONG CdAudioSystemUseOffset; + +#define AUDIO_NAME_ONES_OFFSET (6) +#define AUDIO_NAME_TENS_OFFSET (5) + +// +// Escape sequences for mounting Unicode volumes. +// + +extern PCHAR CdJolietEscape[]; + +// +// Hardcoded header for RIFF files. +// + +extern LONG CdXAFileHeader[]; +extern LONG CdAudioPlayHeader[]; +extern LONG CdXAAudioPhileHeader[]; + + +// +// Turn on pseudo-asserts if CD_FREE_ASSERTS is defined. +// + +#if !DBG +#ifdef CD_FREE_ASSERTS +#undef ASSERT +#undef ASSERTMSG +#define ASSERT(exp) if (!(exp)) { extern BOOLEAN KdDebuggerEnabled; DbgPrint("%s:%d %s\n",__FILE__,__LINE__,#exp); if (KdDebuggerEnabled) { DbgBreakPoint(); } } +#define ASSERTMSG(msg,exp) if (!(exp)) { extern BOOLEAN KdDebuggerEnabled; DbgPrint("%s:%d %s %s\n",__FILE__,__LINE__,msg,#exp); if (KdDebuggerEnabled) { DbgBreakPoint(); } } +#endif +#endif + + +// +// The following assertion macros ensure that the indicated structure +// is valid +// +// ASSERT_STRUCT( IN PVOID Struct, IN CSHORT NodeType ); +// ASSERT_OPTIONAL_STRUCT( IN PVOID Struct OPTIONAL, IN CSHORT NodeType ); +// +// ASSERT_VCB( IN PVCB Vcb ); +// ASSERT_OPTIONAL_VCB( IN PVCB Vcb OPTIONAL ); +// +// ASSERT_FCB( IN PFCB Fcb ); +// ASSERT_OPTIONAL_FCB( IN PFCB Fcb OPTIONAL ); +// +// ASSERT_FCB_NONPAGED( IN PFCB_NONPAGED FcbNonpaged ); +// ASSERT_OPTIONAL_FCB( IN PFCB_NONPAGED FcbNonpaged OPTIONAL ); +// +// ASSERT_CCB( IN PSCB Ccb ); +// ASSERT_OPTIONAL_CCB( IN PSCB Ccb OPTIONAL ); +// +// ASSERT_IRP_CONTEXT( IN PIRP_CONTEXT IrpContext ); +// ASSERT_OPTIONAL_IRP_CONTEXT( IN PIRP_CONTEXT IrpContext OPTIONAL ); +// +// ASSERT_IRP( IN PIRP Irp ); +// ASSERT_OPTIONAL_IRP( IN PIRP Irp OPTIONAL ); +// +// ASSERT_FILE_OBJECT( IN PFILE_OBJECT FileObject ); +// ASSERT_OPTIONAL_FILE_OBJECT( IN PFILE_OBJECT FileObject OPTIONAL ); +// +// The following macros are used to check the current thread owns +// the indicated resource +// +// ASSERT_EXCLUSIVE_RESOURCE( IN PERESOURCE Resource ); +// +// ASSERT_SHARED_RESOURCE( IN PERESOURCE Resource ); +// +// ASSERT_RESOURCE_NOT_MINE( IN PERESOURCE Resource ); +// +// The following macros are used to check whether the current thread +// owns the resoures in the given structures. +// +// ASSERT_EXCLUSIVE_CDDATA +// +// ASSERT_EXCLUSIVE_VCB( IN PVCB Vcb ); +// +// ASSERT_SHARED_VCB( IN PVCB Vcb ); +// +// ASSERT_EXCLUSIVE_FCB( IN PFCB Fcb ); +// +// ASSERT_SHARED_FCB( IN PFCB Fcb ); +// +// ASSERT_EXCLUSIVE_FILE( IN PFCB Fcb ); +// +// ASSERT_SHARED_FILE( IN PFCB Fcb ); +// +// ASSERT_LOCKED_VCB( IN PVCB Vcb ); +// +// ASSERT_NOT_LOCKED_VCB( IN PVCB Vcb ); +// +// ASSERT_LOCKED_FCB( IN PFCB Fcb ); +// +// ASSERT_NOT_LOCKED_FCB( IN PFCB Fcb ); +// + +// +// Turn on the sanity checks if this is DBG or CD_FREE_ASSERTS +// + +#if DBG +#undef CD_SANITY +#define CD_SANITY +#endif + +#ifdef CD_SANITY + +#define ASSERT_STRUCT(S,T) ASSERT( SafeNodeType( S ) == (T) ) +#define ASSERT_OPTIONAL_STRUCT(S,T) ASSERT( ((S) == NULL) || (SafeNodeType( S ) == (T)) ) + +#define ASSERT_VCB(V) ASSERT_STRUCT( (V), CDFS_NTC_VCB ) +#define ASSERT_OPTIONAL_VCB(V) ASSERT_OPTIONAL_STRUCT( (V), CDFS_NTC_VCB ) + +#define ASSERT_FCB(F) \ + ASSERT( (SafeNodeType( F ) == CDFS_NTC_FCB_DATA ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_INDEX ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_PATH_TABLE ) ) + +#define ASSERT_OPTIONAL_FCB(F) \ + ASSERT( ((F) == NULL) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_DATA ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_INDEX ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_PATH_TABLE ) ) + +#define ASSERT_FCB_NONPAGED(FN) ASSERT_STRUCT( (FN), CDFS_NTC_FCB_NONPAGED ) +#define ASSERT_OPTIONAL_FCB_NONPAGED(FN) ASSERT_OPTIONAL_STRUCT( (FN), CDFS_NTC_FCB_NONPAGED ) + +#define ASSERT_CCB(C) ASSERT_STRUCT( (C), CDFS_NTC_CCB ) +#define ASSERT_OPTIONAL_CCB(C) ASSERT_OPTIONAL_STRUCT( (C), CDFS_NTC_CCB ) + +#define ASSERT_IRP_CONTEXT(IC) ASSERT_STRUCT( (IC), CDFS_NTC_IRP_CONTEXT ) +#define ASSERT_OPTIONAL_IRP_CONTEXT(IC) ASSERT_OPTIONAL_STRUCT( (IC), CDFS_NTC_IRP_CONTEXT ) + +#define ASSERT_IRP(I) ASSERT_STRUCT( (I), IO_TYPE_IRP ) +#define ASSERT_OPTIONAL_IRP(I) ASSERT_OPTIONAL_STRUCT( (I), IO_TYPE_IRP ) + +#define ASSERT_FILE_OBJECT(FO) ASSERT_STRUCT( (FO), IO_TYPE_FILE ) +#define ASSERT_OPTIONAL_FILE_OBJECT(FO) ASSERT_OPTIONAL_STRUCT( (FO), IO_TYPE_FILE ) + +#define ASSERT_EXCLUSIVE_RESOURCE(R) ASSERT( ExIsResourceAcquiredExclusiveLite( R )) + +#define ASSERT_SHARED_RESOURCE(R) ASSERT( ExIsResourceAcquiredSharedLite( R )) + +#define ASSERT_RESOURCE_NOT_MINE(R) ASSERT( !ExIsResourceAcquiredSharedLite( R )) + +#define ASSERT_EXCLUSIVE_CDDATA ASSERT( ExIsResourceAcquiredExclusiveLite( &CdData.DataResource )) +#define ASSERT_EXCLUSIVE_VCB(V) ASSERT( ExIsResourceAcquiredExclusiveLite( &(V)->VcbResource )) +#define ASSERT_SHARED_VCB(V) ASSERT( ExIsResourceAcquiredSharedLite( &(V)->VcbResource )) + +#define ASSERT_EXCLUSIVE_FCB(F) ASSERT( ExIsResourceAcquiredExclusiveLite( &(F)->FcbNonpaged->FcbResource )) +#define ASSERT_SHARED_FCB(F) ASSERT( ExIsResourceAcquiredSharedLite( &(F)->FcbNonpaged->FcbResource )) + +#define ASSERT_EXCLUSIVE_FILE(F) ASSERT( ExIsResourceAcquiredExclusiveLite( (F)->Resource )) +#define ASSERT_SHARED_FILE(F) ASSERT( ExIsResourceAcquiredSharedLite( (F)->Resource )) + +#define ASSERT_LOCKED_VCB(V) ASSERT( (V)->VcbLockThread == PsGetCurrentThread() ) +#define ASSERT_NOT_LOCKED_VCB(V) ASSERT( (V)->VcbLockThread != PsGetCurrentThread() ) + +#define ASSERT_LOCKED_FCB(F) ASSERT( !FlagOn( (F)->FcbState, FCB_STATE_IN_FCB_TABLE) || ((F)->FcbLockThread == PsGetCurrentThread())) +#define ASSERT_NOT_LOCKED_FCB(F) ASSERT( (F)->FcbLockThread != PsGetCurrentThread() ) + +#else + +#define DebugBreakOnStatus(S) { NOTHING; } + +#define ASSERT_STRUCT(S,T) { NOTHING; } +#define ASSERT_OPTIONAL_STRUCT(S,T) { NOTHING; } +#define ASSERT_VCB(V) { NOTHING; } +#define ASSERT_OPTIONAL_VCB(V) { NOTHING; } +#define ASSERT_FCB(F) { NOTHING; } +#define ASSERT_OPTIONAL_FCB(F) { NOTHING; } +#define ASSERT_FCB_NONPAGED(FN) { NOTHING; } +#define ASSERT_OPTIONAL_FCB(FN) { NOTHING; } +#define ASSERT_CCB(C) { NOTHING; } +#define ASSERT_OPTIONAL_CCB(C) { NOTHING; } +#define ASSERT_IRP_CONTEXT(IC) { NOTHING; } +#define ASSERT_OPTIONAL_IRP_CONTEXT(IC) { NOTHING; } +#define ASSERT_IRP(I) { NOTHING; } +#define ASSERT_OPTIONAL_IRP(I) { NOTHING; } +#define ASSERT_FILE_OBJECT(FO) { NOTHING; } +#define ASSERT_OPTIONAL_FILE_OBJECT(FO) { NOTHING; } +#define ASSERT_EXCLUSIVE_RESOURCE(R) { NOTHING; } +#define ASSERT_SHARED_RESOURCE(R) { NOTHING; } +#define ASSERT_RESOURCE_NOT_MINE(R) { NOTHING; } +#define ASSERT_EXCLUSIVE_CDDATA { NOTHING; } +#define ASSERT_EXCLUSIVE_VCB(V) { NOTHING; } +#define ASSERT_SHARED_VCB(V) { NOTHING; } +#define ASSERT_EXCLUSIVE_FCB(F) { NOTHING; } +#define ASSERT_SHARED_FCB(F) { NOTHING; } +#define ASSERT_EXCLUSIVE_FILE(F) { NOTHING; } +#define ASSERT_SHARED_FILE(F) { NOTHING; } +#define ASSERT_LOCKED_VCB(V) { NOTHING; } +#define ASSERT_NOT_LOCKED_VCB(V) { NOTHING; } +#define ASSERT_LOCKED_FCB(F) { NOTHING; } +#define ASSERT_NOT_LOCKED_FCB(F) { NOTHING; } + +#endif + +#endif // _CDDATA_ + diff --git a/reactos/drivers/filesystems/cdfs_new/cdfs.rbuild b/reactos/drivers/filesystems/cdfs_new/cdfs.rbuild new file mode 100644 index 00000000000..5ef410eed57 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cdfs.rbuild @@ -0,0 +1,37 @@ + + + + + . + ntoskrnl + hal + allocsup.c + cachesup.c + cddata.c + cdinit.c + cleanup.c + close.c + create.c + devctrl.c + deviosup.c + dirctrl.c + dirsup.c + fileinfo.c + filobsup.c + fsctrl.c + fspdisp.c + lockctrl.c + namesup.c + pathsup.c + pnp.c + prefxsup.c + read.c + resrcsup.c + strucsup.c + verfysup.c + volinfo.c + workque.c + cdfs.rc + -fms-extensions + cdprocs.h + diff --git a/reactos/drivers/filesystems/cdfs_new/cdfs.rc b/reactos/drivers/filesystems/cdfs_new/cdfs.rc new file mode 100644 index 00000000000..1cf70c60840 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cdfs.rc @@ -0,0 +1,7 @@ +/* $Id: cdfs.rc 21710 2006-04-22 16:36:21Z tretiakov $ */ + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ISO9660 Driver\0" +#define REACTOS_STR_INTERNAL_NAME "cdfs\0" +#define REACTOS_STR_ORIGINAL_FILENAME "cdfs.sys\0" +#include diff --git a/reactos/drivers/filesystems/cdfs_new/cdinit.c b/reactos/drivers/filesystems/cdfs_new/cdinit.c new file mode 100755 index 00000000000..b7f33372a5e --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cdinit.c @@ -0,0 +1,366 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdInit.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for Cdfs + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CDINIT) + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +CdUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +CdInitializeGlobalData ( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT FileSystemDeviceObject + ); + +NTSTATUS +CdShutdown ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, CdUnload) +#pragma alloc_text(PAGE, CdShutdown) +#pragma alloc_text(INIT, CdInitializeGlobalData) +#endif + + +// +// Local support routine +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Cdrom file system + device driver. This routine creates the device object for the FileSystem + device and performs all other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeString; + PDEVICE_OBJECT CdfsFileSystemDeviceObject; + + // + // Create the device object. + // + + RtlInitUnicodeString( &UnicodeString, L"\\Cdfs" ); + + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + &CdfsFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + return Status; + } + DriverObject->DriverUnload = CdUnload; + // + // Note that because of the way data caching is done, we set neither + // the Direct I/O or Buffered I/O bit in DeviceObject->Flags. If + // data is not in the cache, or the request is not buffered, we may, + // set up for Direct I/O by hand. + // + + // + // Initialize the driver object with this driver's entry points. + // + // NOTE - Each entry in the dispatch table must have an entry in + // the Fsp/Fsd dispatch switch statements. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = + DriverObject->MajorFunction[IRP_MJ_CLOSE] = + DriverObject->MajorFunction[IRP_MJ_READ] = + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = + DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION]= + DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = + DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH) CdFsdDispatch; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = CdShutdown; + + DriverObject->FastIoDispatch = &CdFastIoDispatch; + + Status = IoRegisterShutdownNotification (CdfsFileSystemDeviceObject); + if (!NT_SUCCESS (Status)) { + IoDeleteDevice (CdfsFileSystemDeviceObject); + return Status; + } + + // + // Initialize the global data structures + // + + Status = CdInitializeGlobalData( DriverObject, CdfsFileSystemDeviceObject ); + if (!NT_SUCCESS (Status)) { + IoDeleteDevice (CdfsFileSystemDeviceObject); + return Status; + } + + // + // Register the file system as low priority with the I/O system. This will cause + // CDFS to receive mount requests after a) other filesystems currently registered + // and b) other normal priority filesystems that may be registered later. + // + + CdfsFileSystemDeviceObject->Flags |= DO_LOW_PRIORITY_FILESYSTEM; + + IoRegisterFileSystem( CdfsFileSystemDeviceObject ); + ObReferenceObject (CdfsFileSystemDeviceObject); + + // + // And return to our caller + // + + return( STATUS_SUCCESS ); +} + +NTSTATUS +CdShutdown ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine is the shutdown handler for CDFS. + +Arguments: + + DeviceObject - Supplies the registered device object for CDFS. + Irp - Shutdown IRP + + +Return Value: + + None. + +--*/ +{ + IoUnregisterFileSystem (DeviceObject); + IoDeleteDevice (CdData.FileSystemDeviceObject); + + CdCompleteRequest( NULL, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +VOID +CdUnload( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine unload routine for CDFS. + +Arguments: + + DriverObject - Supplies the driver object for CDFS. + +Return Value: + + None. + +--*/ +{ + PIRP_CONTEXT IrpContext; + + // + // Free any IRP contexts + // + while (1) { + IrpContext = (PIRP_CONTEXT) PopEntryList( &CdData.IrpContextList) ; + if (IrpContext == NULL) { + break; + } + CdFreePool(&IrpContext); + } + + IoFreeWorkItem (CdData.CloseItem); + ExDeleteResourceLite( &CdData.DataResource ); + ObDereferenceObject (CdData.FileSystemDeviceObject); +} + +// +// Local support routine +// + +NTSTATUS +CdInitializeGlobalData ( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT FileSystemDeviceObject + ) + +/*++ + +Routine Description: + + This routine initializes the global cdfs data structures. + +Arguments: + + DriverObject - Supplies the driver object for CDFS. + + FileSystemDeviceObject - Supplies the device object for CDFS. + +Return Value: + + None. + +--*/ + +{ + // + // Start by initializing the FastIoDispatch Table. + // + + RtlZeroMemory( &CdFastIoDispatch, sizeof( FAST_IO_DISPATCH )); + + CdFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); + CdFastIoDispatch.FastIoCheckIfPossible = CdFastIoCheckIfPossible; // CheckForFastIo + CdFastIoDispatch.FastIoRead = FsRtlCopyRead; // Read + CdFastIoDispatch.FastIoQueryBasicInfo = CdFastQueryBasicInfo; // QueryBasicInfo + CdFastIoDispatch.FastIoQueryStandardInfo = CdFastQueryStdInfo; // QueryStandardInfo + CdFastIoDispatch.FastIoLock = CdFastLock; // Lock + CdFastIoDispatch.FastIoUnlockSingle = CdFastUnlockSingle; // UnlockSingle + CdFastIoDispatch.FastIoUnlockAll = CdFastUnlockAll; // UnlockAll + CdFastIoDispatch.FastIoUnlockAllByKey = CdFastUnlockAllByKey; // UnlockAllByKey + CdFastIoDispatch.AcquireFileForNtCreateSection = CdAcquireForCreateSection; + CdFastIoDispatch.ReleaseFileForNtCreateSection = CdReleaseForCreateSection; + CdFastIoDispatch.FastIoQueryNetworkOpenInfo = CdFastQueryNetworkInfo; // QueryNetworkInfo + + CdFastIoDispatch.MdlRead = FsRtlMdlReadDev; + CdFastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev; + CdFastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev; + CdFastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev; + + // + // Initialize the CdData structure. + // + + RtlZeroMemory( &CdData, sizeof( CD_DATA )); + + CdData.NodeTypeCode = CDFS_NTC_DATA_HEADER; + CdData.NodeByteSize = sizeof( CD_DATA ); + + CdData.DriverObject = DriverObject; + CdData.FileSystemDeviceObject = FileSystemDeviceObject; + + InitializeListHead( &CdData.VcbQueue ); + + ExInitializeResourceLite( &CdData.DataResource ); + + // + // Initialize the cache manager callback routines + // + + CdData.CacheManagerCallbacks.AcquireForLazyWrite = &CdAcquireForCache; + CdData.CacheManagerCallbacks.ReleaseFromLazyWrite = &CdReleaseFromCache; + CdData.CacheManagerCallbacks.AcquireForReadAhead = &CdAcquireForCache; + CdData.CacheManagerCallbacks.ReleaseFromReadAhead = &CdReleaseFromCache; + + CdData.CacheManagerVolumeCallbacks.AcquireForLazyWrite = &CdNoopAcquire; + CdData.CacheManagerVolumeCallbacks.ReleaseFromLazyWrite = &CdNoopRelease; + CdData.CacheManagerVolumeCallbacks.AcquireForReadAhead = &CdNoopAcquire; + CdData.CacheManagerVolumeCallbacks.ReleaseFromReadAhead = &CdNoopRelease; + + // + // Initialize the lock mutex and the async and delay close queues. + // + + ExInitializeFastMutex( &CdData.CdDataMutex ); + InitializeListHead( &CdData.AsyncCloseQueue ); + InitializeListHead( &CdData.DelayedCloseQueue ); + + CdData.CloseItem = IoAllocateWorkItem (FileSystemDeviceObject); + if (CdData.CloseItem == NULL) { + + ExDeleteResourceLite( &CdData.DataResource ); + return STATUS_INSUFFICIENT_RESOURCES; + } + // + // Do the initialization based on the system size. + // + + switch (MmQuerySystemSize()) { + + case MmSmallSystem: + + CdData.IrpContextMaxDepth = 4; + CdData.MaxDelayedCloseCount = 8; + CdData.MinDelayedCloseCount = 2; + break; + + case MmMediumSystem: + + CdData.IrpContextMaxDepth = 8; + CdData.MaxDelayedCloseCount = 24; + CdData.MinDelayedCloseCount = 6; + break; + + case MmLargeSystem: + + CdData.IrpContextMaxDepth = 32; + CdData.MaxDelayedCloseCount = 72; + CdData.MinDelayedCloseCount = 18; + break; + } + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/cdprocs.h b/reactos/drivers/filesystems/cdfs_new/cdprocs.h new file mode 100755 index 00000000000..6fbe9dff676 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cdprocs.h @@ -0,0 +1,1888 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdProcs.h + +Abstract: + + This module defines all of the globally used procedures in the Cdfs + file system. + + +--*/ + +#ifndef _CDPROCS_ +#define _CDPROCS_ + +#include + +#include +#include +#include + +#ifndef INLINE +#define INLINE __inline +#endif + +#include "nodetype.h" +#include "Cd.h" +#include "CdStruc.h" +#include "CdData.h" + + +//**** x86 compiler bug **** + +#if defined(_M_IX86) +#undef Int64ShraMod32 +#define Int64ShraMod32(a, b) ((LONGLONG)(a) >> (b)) +#endif + +// +// Here are the different pool tags. +// + +#define TAG_CCB 'ccdC' // Ccb +#define TAG_CDROM_TOC 'ctdC' // TOC +#define TAG_DIRENT_NAME 'nddC' // CdName in dirent +#define TAG_ENUM_EXPRESSION 'eedC' // Search expression for enumeration +#define TAG_FCB_DATA 'dfdC' // Data Fcb +#define TAG_FCB_INDEX 'ifdC' // Index Fcb +#define TAG_FCB_NONPAGED 'nfdC' // Nonpaged Fcb +#define TAG_FCB_TABLE 'tfdC' // Fcb Table entry +#define TAG_FILE_NAME 'nFdC' // Filename buffer +#define TAG_GEN_SHORT_NAME 'sgdC' // Generated short name +#define TAG_IO_BUFFER 'fbdC' // Temporary IO buffer +#define TAG_IO_CONTEXT 'oidC' // Io context for async reads +#define TAG_IRP_CONTEXT 'cidC' // Irp Context +#define TAG_IRP_CONTEXT_LITE 'lidC' // Irp Context lite +#define TAG_MCB_ARRAY 'amdC' // Mcb array +#define TAG_PATH_ENTRY_NAME 'nPdC' // CdName in path entry +#define TAG_PREFIX_ENTRY 'epdC' // Prefix Entry +#define TAG_PREFIX_NAME 'npdC' // Prefix Entry name +#define TAG_SPANNING_PATH_TABLE 'psdC' // Buffer for spanning path table +#define TAG_UPCASE_NAME 'nudC' // Buffer for upcased name +#define TAG_VOL_DESC 'dvdC' // Buffer for volume descriptor +#define TAG_VPB 'pvdC' // Vpb allocated in filesystem + +// +// Tag all of our allocations if tagging is turned on +// + +#ifdef POOL_TAGGING + +#undef FsRtlAllocatePool +#undef FsRtlAllocatePoolWithQuota +#define FsRtlAllocatePool(a,b) FsRtlAllocatePoolWithTag(a,b,'sfdC') +#define FsRtlAllocatePoolWithQuota(a,b) FsRtlAllocatePoolWithQuotaTag(a,b,'sfdC') + +#endif // POOL_TAGGING + + +// +// File access check routine, implemented in AcChkSup.c +// + +// +// BOOLEAN +// CdIllegalFcbAccess ( +// IN PIRP_CONTEXT IrpContext, +// IN TYPE_OF_OPEN TypeOfOpen, +// IN ACCESS_MASK DesiredAccess +// ); +// + +#define CdIllegalFcbAccess(IC,T,DA) ( \ + BooleanFlagOn( (DA), \ + ((T) != UserVolumeOpen ? \ + (FILE_WRITE_ATTRIBUTES | \ + FILE_WRITE_DATA | \ + FILE_WRITE_EA | \ + FILE_ADD_FILE | \ + FILE_ADD_SUBDIRECTORY | \ + FILE_APPEND_DATA) : 0) | \ + FILE_DELETE_CHILD | \ + DELETE | \ + WRITE_DAC )) + + +// +// Allocation support routines, implemented in AllocSup.c +// +// These routines are for querying allocation on individual streams. +// + +VOID +CdLookupAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG FileOffset, + OUT PLONGLONG DiskOffset, + OUT PULONG ByteCount + ); + +VOID +CdAddAllocationFromDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG McbEntryOffset, + IN LONGLONG StartingFileOffset, + IN PDIRENT Dirent + ); + +VOID +CdAddInitialAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG StartingBlock, + IN LONGLONG DataLength + ); + +VOID +CdTruncateAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingFileOffset + ); + +VOID +CdInitializeMcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +CdUninitializeMcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + + +// +// Buffer control routines for data caching, implemented in CacheSup.c +// + +VOID +CdCreateInternalStream ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFCB Fcb + ); + +VOID +CdDeleteInternalStream ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +NTSTATUS +CdCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdPurgeVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN DismountUnderway + ); + +// +// VOID +// CdUnpinData ( +// IN PIRP_CONTEXT IrpContext, +// IN OUT PBCB *Bcb +// ); +// + +#define CdUnpinData(IC,B) \ + if (*(B) != NULL) { CcUnpinData( *(B) ); *(B) = NULL; } + + +// +// Device I/O routines, implemented in DevIoSup.c +// +// These routines perform the actual device read and writes. They only affect +// the on disk structure and do not alter any other data structures. +// + +NTSTATUS +CdNonCachedRead ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount + ); + +NTSTATUS +CdNonCachedXARead ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount + ); + +BOOLEAN +CdReadSectors ( + IN PIRP_CONTEXT IrpContext, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN BOOLEAN RaiseOnError, + IN OUT PVOID Buffer, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +NTSTATUS +CdCreateUserMdl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG BufferLength, + IN BOOLEAN RaiseOnError + ); + +NTSTATUS +CdPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ); + +// +// VOID +// CdMapUserBuffer ( +// IN PIRP_CONTEXT IrpContext +// OUT PVOID UserBuffer +// ); +// +// Returns pointer to sys address. Will raise on failure. +// +// +// VOID +// CdLockUserBuffer ( +// IN PIRP_CONTEXT IrpContext, +// IN ULONG BufferLength +// ); +// + +#define CdMapUserBuffer(IC, UB) { \ + *(UB) = (PVOID) ( ((IC)->Irp->MdlAddress == NULL) ? \ + (IC)->Irp->UserBuffer : \ + (MmGetSystemAddressForMdlSafe( (IC)->Irp->MdlAddress, NormalPagePriority))); \ + if (NULL == *(UB)) { \ + CdRaiseStatus( (IC), STATUS_INSUFFICIENT_RESOURCES); \ + } \ + } + + +#define CdLockUserBuffer(IC,BL) { \ + if ((IC)->Irp->MdlAddress == NULL) { \ + (VOID) CdCreateUserMdl( (IC), (BL), TRUE ); \ + } \ +} + + +// +// Dirent support routines, implemented in DirSup.c +// + +VOID +CdLookupDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG DirentOffset, + OUT PDIRENT_ENUM_CONTEXT DirContext + ); + +BOOLEAN +CdLookupNextDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT CurrentDirContext, + OUT PDIRENT_ENUM_CONTEXT NextDirContext + ); + +VOID +CdUpdateDirentFromRawDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT DirContext, + IN OUT PDIRENT Dirent + ); + +VOID +CdUpdateDirentName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN ULONG IgnoreCase + ); + +BOOLEAN +CdFindFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext, + OUT PCD_NAME *MatchingName + ); + +BOOLEAN +CdFindDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext + ); + +BOOLEAN +CdFindFileByShortName ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN ULONG ShortNameDirentOffset, + IN OUT PFILE_ENUM_CONTEXT FileContext + ); + +BOOLEAN +CdLookupNextInitialFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_ENUM_CONTEXT FileContext + ); + +VOID +CdLookupLastFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_ENUM_CONTEXT FileContext + ); + +VOID +CdCleanupFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_ENUM_CONTEXT FileContext + ); + +// +// VOID +// CdInitializeFileContext ( +// IN PIRP_CONTEXT IrpContext, +// IN PFILE_ENUM_CONTEXT FileContext +// ); +// +// +// VOID +// CdInitializeDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIRENT Dirent +// ); +// +// VOID +// CdInitializeDirContext ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIRENT_ENUM_CONTEXT DirContext +// ); +// +// VOID +// CdCleanupDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIRENT Dirent +// ); +// +// VOID +// CdCleanupDirContext ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIRENT_ENUM_CONTEXT DirContext +// ); +// +// VOID +// CdLookupInitialFileDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// IN PFILE_ENUM_CONTEXT FileContext, +// IN ULONG DirentOffset +// ); +// + +#define CdInitializeFileContext(IC,FC) { \ + RtlZeroMemory( FC, sizeof( FILE_ENUM_CONTEXT )); \ + (FC)->PriorDirent = &(FC)->Dirents[0]; \ + (FC)->InitialDirent = &(FC)->Dirents[1]; \ + (FC)->CurrentDirent = &(FC)->Dirents[2]; \ + (FC)->ShortName.FileName.MaximumLength = BYTE_COUNT_8_DOT_3; \ + (FC)->ShortName.FileName.Buffer = (FC)->ShortNameBuffer; \ +} + +#define CdInitializeDirent(IC,D) \ + RtlZeroMemory( D, sizeof( DIRENT )) + +#define CdInitializeDirContext(IC,DC) \ + RtlZeroMemory( DC, sizeof( DIRENT_ENUM_CONTEXT )) + +#define CdCleanupDirent(IC,D) { \ + if (FlagOn( (D)->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { \ + CdFreePool( &(D)->CdFileName.FileName.Buffer ); \ + } \ +} + +#define CdCleanupDirContext(IC,DC) \ + CdUnpinData( (IC), &(DC)->Bcb ) + +#define CdLookupInitialFileDirent(IC,F,FC,DO) \ + CdLookupDirent( IC, \ + F, \ + DO, \ + &(FC)->InitialDirent->DirContext ); \ + CdUpdateDirentFromRawDirent( IC, \ + F, \ + &(FC)->InitialDirent->DirContext, \ + &(FC)->InitialDirent->Dirent ) + + +// +// The following routines are used to manipulate the fscontext fields +// of the file object, implemented in FilObSup.c +// + +// +// Type of opens. FilObSup.c depends on this order. +// + +typedef enum _TYPE_OF_OPEN { + + UnopenedFileObject = 0, + StreamFileOpen, + UserVolumeOpen, + UserDirectoryOpen, + UserFileOpen, + BeyondValidType + +} TYPE_OF_OPEN; +typedef TYPE_OF_OPEN *PTYPE_OF_OPEN; + +VOID +CdSetFileObject ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN TYPE_OF_OPEN TypeOfOpen, + IN PFCB Fcb OPTIONAL, + IN PCCB Ccb OPTIONAL + ); + +TYPE_OF_OPEN +CdDecodeFileObject ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb, + OUT PCCB *Ccb + ); + +TYPE_OF_OPEN +CdFastDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb + ); + + +// +// Name support routines, implemented in NameSup.c +// + +VOID +CdConvertNameToCdName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PCD_NAME CdName + ); + +VOID +CdConvertBigToLittleEndian ( + IN PIRP_CONTEXT IrpContext, + IN PCHAR BigEndian, + IN ULONG ByteCount, + OUT PCHAR LittleEndian + ); + +VOID +CdUpcaseName ( + IN PIRP_CONTEXT IrpContext, + IN PCD_NAME Name, + IN OUT PCD_NAME UpcaseName + ); + +VOID +CdDissectName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PUNICODE_STRING RemainingName, + OUT PUNICODE_STRING FinalName + ); + +BOOLEAN +CdIs8dot3Name ( + IN PIRP_CONTEXT IrpContext, + IN UNICODE_STRING FileName + ); + +VOID +CdGenerate8dot3Name ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING FileName, + IN ULONG DirentOffset, + OUT PWCHAR ShortFileName, + OUT PUSHORT ShortByteCount + ); + +BOOLEAN +CdIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN PCD_NAME CurrentName, + IN PCD_NAME SearchExpression, + IN ULONG WildcardFlags, + IN BOOLEAN CheckVersion + ); + +ULONG +CdShortNameDirentOffset ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING Name + ); + +FSRTL_COMPARISON_RESULT +CdFullCompareNames ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING NameA, + IN PUNICODE_STRING NameB + ); + + +// +// Filesystem control operations. Implemented in Fsctrl.c +// + +NTSTATUS +CdLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + +NTSTATUS +CdUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + + +// +// Path table enumeration routines. Implemented in PathSup.c +// + +VOID +CdLookupPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN ULONG PathEntryOffset, + IN ULONG Ordinal, + IN BOOLEAN VerifyBounds, + IN OUT PCOMPOUND_PATH_ENTRY CompoundPathEntry + ); + +BOOLEAN +CdLookupNextPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN OUT PPATH_ENUM_CONTEXT PathContext, + IN OUT PPATH_ENTRY PathEntry + ); + +BOOLEAN +CdFindPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN PFCB ParentFcb, + IN PCD_NAME DirName, + IN BOOLEAN IgnoreCase, + IN OUT PCOMPOUND_PATH_ENTRY CompoundPathEntry + ); + +VOID +CdUpdatePathEntryName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PPATH_ENTRY PathEntry, + IN BOOLEAN IgnoreCase + ); + +// +// VOID +// CdInitializeCompoundPathEntry ( +// IN PIRP_CONTEXT IrpContext, +// IN PCOMPOUND_PATH_ENTRY CompoundPathEntry +// ); +// +// VOID +// CdCleanupCompoundPathEntry ( +// IN PIRP_CONTEXT IrpContext, +// IN PCOMPOUND_PATH_ENTRY CompoundPathEntry +// ); +// + +#define CdInitializeCompoundPathEntry(IC,CP) \ + RtlZeroMemory( CP, sizeof( COMPOUND_PATH_ENTRY )) + +#define CdCleanupCompoundPathEntry(IC,CP) { \ + CdUnpinData( (IC), &(CP)->PathContext.Bcb ); \ + if ((CP)->PathContext.AllocatedData) { \ + CdFreePool( &(CP)->PathContext.Data ); \ + } \ + if (FlagOn( (CP)->PathEntry.Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) { \ + CdFreePool( &(CP)->PathEntry.CdDirName.FileName.Buffer ); \ + } \ +} + + +// +// Largest matching prefix searching routines, implemented in PrefxSup.c +// + +VOID +CdInsertPrefix ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PFCB ParentFcb + ); + +VOID +CdRemovePrefix ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +CdFindPrefix ( + IN PIRP_CONTEXT IrpContext, + IN OUT PFCB *CurrentFcb, + IN OUT PUNICODE_STRING RemainingName, + IN BOOLEAN IgnoreCase + ); + + +// +// Synchronization routines. Implemented in Resrcsup.c +// +// The following routines/macros are used to synchronize the in-memory structures. +// +// Routine/Macro Synchronizes Subsequent +// +// CdAcquireCdData Volume Mounts/Dismounts,Vcb Queue CdReleaseCdData +// CdAcquireVcbExclusive Vcb for open/close CdReleaseVcb +// CdAcquireVcbShared Vcb for open/close CdReleaseVcb +// CdAcquireAllFiles Locks out operations to all files CdReleaseAllFiles +// CdAcquireFileExclusive Locks out file operations CdReleaseFile +// CdAcquireFileShared Files for file operations CdReleaseFile +// CdAcquireFcbExclusive Fcb for open/close CdReleaseFcb +// CdAcquireFcbShared Fcb for open/close CdReleaseFcb +// CdLockCdData Fields in CdData CdUnlockCdData +// CdLockVcb Vcb fields, FcbReference, FcbTable CdUnlockVcb +// CdLockFcb Fcb fields, prefix table, Mcb CdUnlockFcb +// + +typedef enum _TYPE_OF_ACQUIRE { + + AcquireExclusive, + AcquireShared, + AcquireSharedStarveExclusive + +} TYPE_OF_ACQUIRE, *PTYPE_OF_ACQUIRE; + +BOOLEAN +CdAcquireResource ( + IN PIRP_CONTEXT IrpContext, + IN PERESOURCE Resource, + IN BOOLEAN IgnoreWait, + IN TYPE_OF_ACQUIRE Type + ); + +// +// BOOLEAN +// CdAcquireCdData ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdReleaseCdData ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// BOOLEAN +// CdAcquireVcbExclusive ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdAcquireVcbShared ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN BOOLEAN IgnoreWait +// ); +// +// VOID +// CdReleaseVcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// +// VOID +// CdAcquireAllFiles ( +// IN PIRP_CONTEXT, +// IN PVCB Vcb +// ); +// +// VOID +// CdReleaseAllFiles ( +// IN PIRP_CONTEXT, +// IN PVCB Vcb +// ); +// +// VOID +// CdAcquireFileExclusive ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// ); +// +// VOID +// CdAcquireFileShared ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdReleaseFile ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// BOOLEAN +// CdAcquireFcbExclusive ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// IN BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdAcquireFcbShared ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// IN BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdReleaseFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdLockCdData ( +// ); +// +// VOID +// CdUnlockCdData ( +// ); +// +// VOID +// CdLockVcb ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdUnlockVcb ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdLockFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdUnlockFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define CdAcquireCdData(IC) \ + ExAcquireResourceExclusiveLite( &CdData.DataResource, TRUE ) + +#define CdReleaseCdData(IC) \ + ExReleaseResourceLite( &CdData.DataResource ) + +#define CdAcquireVcbExclusive(IC,V,I) \ + CdAcquireResource( (IC), &(V)->VcbResource, (I), AcquireExclusive ) + +#define CdAcquireVcbShared(IC,V,I) \ + CdAcquireResource( (IC), &(V)->VcbResource, (I), AcquireShared ) + +#define CdReleaseVcb(IC,V) \ + ExReleaseResourceLite( &(V)->VcbResource ) + +#define CdAcquireAllFiles(IC,V) \ + CdAcquireResource( (IC), &(V)->FileResource, FALSE, AcquireExclusive ) + +#define CdReleaseAllFiles(IC,V) \ + ExReleaseResourceLite( &(V)->FileResource ) + +#define CdAcquireFileExclusive(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireExclusive ) + +#define CdAcquireFileShared(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireShared ) + +#define CdAcquireFileSharedStarveExclusive(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireSharedStarveExclusive ) + +#define CdReleaseFile(IC,F) \ + ExReleaseResourceLite( (F)->Resource ) + +#define CdAcquireFcbExclusive(IC,F,I) \ + CdAcquireResource( (IC), &(F)->FcbNonpaged->FcbResource, (I), AcquireExclusive ) + +#define CdAcquireFcbShared(IC,F,I) \ + CdAcquireResource( (IC), &(F)->FcbNonpaged->FcbResource, (I), AcquireShared ) + +#define CdReleaseFcb(IC,F) \ + ExReleaseResourceLite( &(F)->FcbNonpaged->FcbResource ) + +#define CdLockCdData() \ + ExAcquireFastMutex( &CdData.CdDataMutex ); \ + CdData.CdDataLockThread = PsGetCurrentThread() + +#define CdUnlockCdData() \ + CdData.CdDataLockThread = NULL; \ + ExReleaseFastMutex( &CdData.CdDataMutex ) + +#define CdLockVcb(IC,V) \ + ExAcquireFastMutex( &(V)->VcbMutex ); \ + ASSERT( NULL == (V)->VcbLockThread); \ + (V)->VcbLockThread = PsGetCurrentThread() + +#define CdUnlockVcb(IC,V) \ + ASSERT( NULL != (V)->VcbLockThread); \ + (V)->VcbLockThread = NULL; \ + ExReleaseFastMutex( &(V)->VcbMutex ) + +#define CdLockFcb(IC,F) { \ + PVOID _CurrentThread = PsGetCurrentThread(); \ + if (_CurrentThread != (F)->FcbLockThread) { \ + ExAcquireFastMutex( &(F)->FcbNonpaged->FcbMutex ); \ + ASSERT( (F)->FcbLockCount == 0 ); \ + (F)->FcbLockThread = _CurrentThread; \ + } \ + (F)->FcbLockCount += 1; \ +} + +#define CdUnlockFcb(IC,F) { \ + (F)->FcbLockCount -= 1; \ + if ((F)->FcbLockCount == 0) { \ + (F)->FcbLockThread = NULL; \ + ExReleaseFastMutex( &(F)->FcbNonpaged->FcbMutex ); \ + } \ +} + +BOOLEAN +CdNoopAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ); + +VOID +CdNoopRelease ( + IN PVOID Fcb + ); + +BOOLEAN +CdAcquireForCache ( + IN PFCB Fcb, + IN BOOLEAN Wait + ); + +VOID +CdReleaseFromCache ( + IN PFCB Fcb + ); + +VOID +CdAcquireForCreateSection ( + IN PFILE_OBJECT FileObject + ); + +VOID +CdReleaseForCreateSection ( + IN PFILE_OBJECT FileObject + ); + + +// +// In-memory structure support routines. Implemented in StrucSup.c +// + +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 + ); + +VOID +CdUpdateVcbFromVolDescriptor ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + IN PCHAR RawIsoVd OPTIONAL + ); + +VOID +CdDeleteVcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb + ); + +PFCB +CdCreateFcb ( + IN PIRP_CONTEXT IrpContext, + IN FILE_ID FileId, + IN NODE_TYPE_CODE NodeTypeCode, + OUT PBOOLEAN FcbExisted OPTIONAL + ); + +VOID +CdInitializeFcbFromPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB ParentFcb OPTIONAL, + IN PPATH_ENTRY PathEntry + ); + +VOID +CdInitializeFcbFromFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB ParentFcb OPTIONAL, + IN PFILE_ENUM_CONTEXT FileContext + ); + +PCCB +CdCreateCcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG Flags + ); + +VOID +CdDeleteCcb ( + IN PIRP_CONTEXT IrpContext, + IN PCCB Ccb + ); + +BOOLEAN +CdCreateFileLock ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PFCB Fcb, + IN BOOLEAN RaiseOnError + ); + +VOID +CdDeleteFileLock ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_LOCK FileLock + ); + +PIRP_CONTEXT +CdCreateIrpContext ( + IN PIRP Irp, + IN BOOLEAN Wait + ); + +VOID +CdCleanupIrpContext ( + IN PIRP_CONTEXT IrpContext, + IN BOOLEAN Post + ); + +VOID +CdInitializeStackIrpContext ( + OUT PIRP_CONTEXT IrpContext, + IN PIRP_CONTEXT_LITE IrpContextLite + ); + +// +// PIRP_CONTEXT_LITE +// CdCreateIrpContextLite ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdFreeIrpContextLite ( +// IN PIRP_CONTEXT_LITE IrpContextLite +// ); +// + +#define CdCreateIrpContextLite(IC) \ + ExAllocatePoolWithTag( CdNonPagedPool, sizeof( IRP_CONTEXT_LITE ), TAG_IRP_CONTEXT_LITE ) + +#define CdFreeIrpContextLite(ICL) \ + CdFreePool( &(ICL) ) + +VOID +CdTeardownStructures ( + IN PIRP_CONTEXT IrpContext, + IN PFCB StartingFcb, + OUT PBOOLEAN RemovedStartingFcb + ); + +// +// VOID +// CdIncrementCleanupCounts ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdDecrementCleanupCounts ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdIncrementReferenceCounts ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// IN ULONG ReferenceCount +// IN ULONG UserReferenceCount +// ); +// +// VOID +// CdDecrementReferenceCounts ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb, +// IN ULONG ReferenceCount +// IN ULONG UserReferenceCount +// ); +// +// VOID +// CdIncrementFcbReference ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// +// VOID +// CdDecrementFcbReference ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define CdIncrementCleanupCounts(IC,F) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbCleanup += 1; \ + (F)->Vcb->VcbCleanup += 1; \ +} + +#define CdDecrementCleanupCounts(IC,F) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbCleanup -= 1; \ + (F)->Vcb->VcbCleanup -= 1; \ +} + +#define CdIncrementReferenceCounts(IC,F,C,UC) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbReference += (C); \ + (F)->FcbUserReference += (UC); \ + (F)->Vcb->VcbReference += (C); \ + (F)->Vcb->VcbUserReference += (UC); \ +} + +#define CdDecrementReferenceCounts(IC,F,C,UC) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbReference -= (C); \ + (F)->FcbUserReference -= (UC); \ + (F)->Vcb->VcbReference -= (C); \ + (F)->Vcb->VcbUserReference -= (UC); \ +} + +// +// PCD_IO_CONTEXT +// CdAllocateIoContext ( +// ); +// +// VOID +// CdFreeIoContext ( +// PCD_IO_CONTEXT IoContext +// ); +// + +#define CdAllocateIoContext() \ + FsRtlAllocatePoolWithTag( CdNonPagedPool, \ + sizeof( CD_IO_CONTEXT ), \ + TAG_IO_CONTEXT ) + +#define CdFreeIoContext(IO) CdFreePool( &(IO) ) + +PFCB +CdLookupFcbTable ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FILE_ID FileId + ); + +PFCB +CdGetNextFcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PVOID *RestartKey + ); + +NTSTATUS +CdProcessToc ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PCDROM_TOC CdromToc, + IN OUT PULONG Length, + OUT PULONG TrackCount, + OUT PULONG DiskFlags + ); + +// +// For debugging purposes we sometimes want to allocate our structures from nonpaged +// pool so that in the kernel debugger we can walk all the structures. +// + +#define CdPagedPool PagedPool +#define CdNonPagedPool NonPagedPool +#define CdNonPagedPoolCacheAligned NonPagedPoolCacheAligned + + +// +// Verification support routines. Contained in verfysup.c +// + + +INLINE +BOOLEAN +CdOperationIsDasdOpen( + IN PIRP_CONTEXT IrpContext + ) +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp); + + return ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->FileName.Length == 0) && + (IrpSp->FileObject->RelatedFileObject == NULL)); +} + + +NTSTATUS +CdPerformVerify ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT DeviceToVerify + ); + +BOOLEAN +CdCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB, + IN BOOLEAN Force + ); + +VOID +CdVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +BOOLEAN +CdVerifyFcbOperation ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PFCB Fcb + ); + +BOOLEAN +CdDismountVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + + +// +// Macros to abstract device verify flag changes. +// + +#define CdUpdateMediaChangeCount( V, C) (V)->MediaChangeCount = (C) +#define CdUpdateVcbCondition( V, C) (V)->VcbCondition = (C) + +#define CdMarkRealDevForVerify( DO) SetFlag( (DO)->Flags, DO_VERIFY_VOLUME) + +#define CdMarkRealDevVerifyOk( DO) ClearFlag( (DO)->Flags, DO_VERIFY_VOLUME) + + +#define CdRealDevNeedsVerify( DO) BooleanFlagOn( (DO)->Flags, DO_VERIFY_VOLUME) + +// +// BOOLEAN +// CdIsRawDevice ( +// IN PIRP_CONTEXT IrpContext, +// IN NTSTATUS Status +// ); +// + +#define CdIsRawDevice(IC,S) ( \ + ((S) == STATUS_DEVICE_NOT_READY) || \ + ((S) == STATUS_NO_MEDIA_IN_DEVICE) \ +) + + +// +// Work queue routines for posting and retrieving an Irp, implemented in +// workque.c +// + +NTSTATUS +CdFsdPostRequest( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +CdPrePostIrp ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +CdOplockComplete ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + + +// +// Miscellaneous support routines +// + +// +// This macro returns TRUE if a flag in a set of flags is on and FALSE +// otherwise +// + +//#ifndef BooleanFlagOn +//#define BooleanFlagOn(F,SF) ( \ +// (BOOLEAN)(((F) & (SF)) != 0) \ +//) +//#endif + +//#ifndef SetFlag +//#define SetFlag(Flags,SingleFlag) { \ +// (Flags) |= (SingleFlag); \ +//} +//#endif + +//#ifndef ClearFlag +//#define ClearFlag(Flags,SingleFlag) { \ +// (Flags) &= ~(SingleFlag); \ +//} +//#endif + +// +// CAST +// Add2Ptr ( +// IN PVOID Pointer, +// IN ULONG Increment +// IN (CAST) +// ); +// +// ULONG +// PtrOffset ( +// IN PVOID BasePtr, +// IN PVOID OffsetPtr +// ); +// + +#define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC))) + +#define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG_PTR)(OFFSET) - (ULONG_PTR)(BASE))) + +// +// This macro takes a pointer (or ulong) and returns its rounded up word +// value +// + +#define WordAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up longword +// value +// + +#define LongAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up quadword +// value +// + +#define QuadAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \ + ) + +// +// The following macros round up and down to sector boundaries. +// + +#define SectorAlign(L) ( \ + ((((ULONG)(L)) + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1)) \ +) + +#define LlSectorAlign(L) ( \ + ((((LONGLONG)(L)) + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1)) \ +) + +#define SectorTruncate(L) ( \ + ((ULONG)(L)) & ~(SECTOR_SIZE - 1) \ +) + +#define LlSectorTruncate(L) ( \ + ((LONGLONG)(L)) & ~(SECTOR_SIZE - 1) \ +) + +#define BytesFromSectors(L) ( \ + ((ULONG) (L)) << SECTOR_SHIFT \ +) + +#define SectorsFromBytes(L) ( \ + ((ULONG) (L)) >> SECTOR_SHIFT \ +) + +#define LlBytesFromSectors(L) ( \ + Int64ShllMod32( (LONGLONG)(L), SECTOR_SHIFT ) \ +) + +#define LlSectorsFromBytes(L) ( \ + Int64ShraMod32( (LONGLONG)(L), SECTOR_SHIFT ) \ +) + +#define SectorOffset(L) ( \ + ((ULONG)(ULONG_PTR) (L)) & SECTOR_MASK \ +) + +#define SectorBlockOffset(V,LB) ( \ + ((ULONG) (LB)) & ((V)->BlocksPerSector - 1) \ +) + +#define BytesFromBlocks(V,B) ( \ + (ULONG) (B) << (V)->BlockToByteShift \ +) + +#define LlBytesFromBlocks(V,B) ( \ + Int64ShllMod32( (LONGLONG) (B), (V)->BlockToByteShift ) \ +) + +#define BlockAlign(V,L) ( \ + ((ULONG)(L) + (V)->BlockMask) & (V)->BlockInverseMask \ +) + +// +// Carefully make sure the mask is sign extended to 64bits +// + +#define LlBlockAlign(V,L) ( \ + ((LONGLONG)(L) + (V)->BlockMask) & (LONGLONG)((LONG)(V)->BlockInverseMask) \ +) + +#define BlockOffset(V,L) ( \ + ((ULONG) (L)) & (V)->BlockMask \ +) + +#define RawSectorAlign( B) ((((B)+(RAW_SECTOR_SIZE - 1)) / RAW_SECTOR_SIZE) * RAW_SECTOR_SIZE) + +// +// The following types and macros are used to help unpack the packed and +// misaligned fields found in the Bios parameter block +// + +typedef union _UCHAR1 { + UCHAR Uchar[1]; + UCHAR ForceAlignment; +} UCHAR1, *PUCHAR1; + +typedef union _UCHAR2 { + UCHAR Uchar[2]; + USHORT ForceAlignment; +} UCHAR2, *PUCHAR2; + +typedef union _UCHAR4 { + UCHAR Uchar[4]; + ULONG ForceAlignment; +} UCHAR4, *PUCHAR4; + +typedef union _USHORT2 { + USHORT Ushort[2]; + ULONG ForceAlignment; +} USHORT2, *PUSHORT2; + +// +// This macro copies an unaligned src byte to an aligned dst byte +// + +#define CopyUchar1(Dst,Src) { \ + *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \ + } + +// +// This macro copies an unaligned src word to an aligned dst word +// + +#define CopyUchar2(Dst,Src) { \ + *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// + +#define CopyUchar4(Dst,Src) { \ + *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// accessing the source on a word boundary. +// + +#define CopyUshort2(Dst,Src) { \ + *((USHORT2 *)(Dst)) = *((UNALIGNED USHORT2 *)(Src));\ + } + + +// +// Following routines handle entry in and out of the filesystem. They are +// contained in CdData.c +// + +NTSTATUS +CdFsdDispatch ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +LONG +CdExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ); + +NTSTATUS +CdProcessException ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ); + +VOID +CdCompleteRequest ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp OPTIONAL, + IN NTSTATUS Status + ); + +// +// VOID +// CdRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// +// VOID +// CdNormalizeAndRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// + +#if 0 +#define AssertVerifyDevice(C, S) \ + ASSERT( (C) == NULL || \ + FlagOn( (C)->Flags, IRP_CONTEXT_FLAG_IN_FSP ) || \ + !((S) == STATUS_VERIFY_REQUIRED && \ + IoGetDeviceToVerify( PsGetCurrentThread() ) == NULL )); + +#define AssertVerifyDeviceIrp(I) \ + ASSERT( (I) == NULL || \ + !(((I)->IoStatus.Status) == STATUS_VERIFY_REQUIRED && \ + ((I)->Tail.Overlay.Thread == NULL || \ + IoGetDeviceToVerify( (I)->Tail.Overlay.Thread ) == NULL ))); +#else +#define AssertVerifyDevice(C, S) +#define AssertVerifyDeviceIrp(I) +#endif + + +#ifdef CD_SANITY + +DECLSPEC_NORETURN +VOID +CdRaiseStatusEx( + IN PIRP_CONTEXT IrpContext, + IN NTSTATUS Status, + IN BOOLEAN NormalizeStatus, + IN OPTIONAL ULONG FileId, + IN OPTIONAL ULONG Line + ); + +#else + +INLINE +DECLSPEC_NORETURN +VOID +CdRaiseStatusEx( + IN PIRP_CONTEXT IrpContext, + IN NTSTATUS Status, + IN BOOLEAN NormalizeStatus, + IN ULONG Fileid, + IN ULONG Line + ) +{ + if (NormalizeStatus) { + + IrpContext->ExceptionStatus = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR); + } + else { + + IrpContext->ExceptionStatus = Status; + } + + IrpContext->RaisedAtLineFile = (Fileid << 16) | Line; + + ExRaiseStatus( IrpContext->ExceptionStatus ); +} + +#endif + +#define CdRaiseStatus( IC, S) CdRaiseStatusEx( (IC), (S), FALSE, BugCheckFileId, __LINE__); +#define CdNormalizeAndRaiseStatus( IC, S) CdRaiseStatusEx( (IC), (S), TRUE, BugCheckFileId, __LINE__); + +// +// Following are the fast entry points. +// + +BOOLEAN +CdFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_BASIC_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + BOOLEAN ExclusiveLock, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastUnlockAll ( + IN PFILE_OBJECT FileObject, + PEPROCESS ProcessId, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastIoCheckIfPossible ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN BOOLEAN CheckForReadOperation, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdFastQueryNetworkInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +// +// Following are the routines to handle the top level thread logic. +// + +VOID +CdSetThreadContext ( + IN PIRP_CONTEXT IrpContext, + IN PTHREAD_CONTEXT ThreadContext + ); + + +// +// VOID +// CdRestoreThreadContext ( +// IN PIRP_CONTEXT IrpContext +// ); +// + +#define CdRestoreThreadContext(IC) \ + (IC)->ThreadContext->Cdfs = 0; \ + IoSetTopLevelIrp( (IC)->ThreadContext->SavedTopLevelIrp ); \ + (IC)->ThreadContext = NULL + +ULONG +CdSerial32 ( + IN PCHAR Buffer, + IN ULONG ByteCount + ); + +// +// The following macro is used to determine if an FSD thread can block +// for I/O or wait for a resource. It returns TRUE if the thread can +// block and FALSE otherwise. This attribute can then be used to call +// the FSD & FSP common work routine with the proper wait value. +// + +#define CanFsdWait(I) IoIsOperationSynchronous(I) + +// +// The following macro is used to set the fast i/o possible bits in the +// FsRtl header. +// +// FastIoIsNotPossible - If the Fcb is bad or there are oplocks on the file. +// +// FastIoIsQuestionable - If there are file locks. +// +// FastIoIsPossible - In all other cases. +// +// + +#define CdIsFastIoPossible(F) ((BOOLEAN) \ + ((((F)->Vcb->VcbCondition != VcbMounted ) || \ + !FsRtlOplockIsFastIoPossible( &(F)->Oplock )) ? \ + \ + FastIoIsNotPossible : \ + \ + ((((F)->FileLock != NULL) && FsRtlAreThereCurrentFileLocks( (F)->FileLock )) ? \ + \ + FastIoIsQuestionable : \ + \ + FastIoIsPossible)) \ +) + + +// +// The FSP level dispatch/main routine. This is the routine that takes +// IRP's off of the work queue and calls the appropriate FSP level +// work routine. +// + +VOID +CdFspDispatch ( // implemented in FspDisp.c + IN PIRP_CONTEXT IrpContext + ); + +VOID +CdFspClose ( // implemented in Close.c + IN PVCB Vcb OPTIONAL + ); + +// +// The following routines are the entry points for the different operations +// based on the IrpSp major functions. +// + +NTSTATUS +CdCommonCreate ( // Implemented in Create.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonClose ( // Implemented in Close.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonRead ( // Implemented in Read.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonQueryInfo ( // Implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonSetInfo ( // Implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonQueryVolInfo ( // Implemented in VolInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonDirControl ( // Implemented in DirCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonFsControl ( // Implemented in FsCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonDevControl ( // Implemented in DevCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonLockControl ( // Implemented in LockCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonCleanup ( // Implemented in Cleanup.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdCommonPnp ( // Implemented in Pnp.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + + +// +// The following macros are used to establish the semantics needed +// to do a return from within a try-finally clause. As a rule every +// try clause must end with a label call try_exit. For example, +// +// try { +// : +// : +// +// try_exit: NOTHING; +// } finally { +// +// : +// : +// } +// +// Every return statement executed inside of a try clause should use the +// try_return macro. If the compiler fully supports the try-finally construct +// then the macro should be +// +// #define try_return(S) { return(S); } +// +// If the compiler does not support the try-finally construct then the macro +// should be +// +// #define try_return(S) { S; goto try_exit; } +// + +#define try_return(S) { S; goto try_exit; } +#define try_leave(S) { S; leave; } + +// +// Encapsulate safe pool freeing +// + +INLINE +VOID +CdFreePool( + IN PVOID *Pool + ) +{ + if (*Pool != NULL) { + + ExFreePool(*Pool); + *Pool = NULL; + } +} + +#endif // _CDPROCS_ + + diff --git a/reactos/drivers/filesystems/cdfs_new/cdstruc.h b/reactos/drivers/filesystems/cdfs_new/cdstruc.h new file mode 100755 index 00000000000..6705d9014af --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cdstruc.h @@ -0,0 +1,1763 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdStruc.h + +Abstract: + + This module defines the data structures that make up the major internal + part of the Cdfs file system. + + In-Memory structures: + + The global data structures with the CdDataRecord. It contains a pointer + to a File System Device object and a queue of Vcb's. There is a Vcb for + every currently or previously mounted volumes. We may be in the process + of tearing down the Vcb's which have been dismounted. The Vcb's are + allocated as an extension to a volume device object. + + +--------+ + | CdData | +--------+ + | | --> |FilSysDo| + | | | | + | | <+ +--------+ + +--------+ | + | + | +--------+ +--------+ + | |VolDo | |VolDo | + | | | | | + | +--------+ +--------+ + +> |Vcb | <-> |Vcb | <-> ... + | | | | + +--------+ +--------+ + + + Each Vcb contains a table of all the Fcbs for the volume indexed by + their FileId. Each Vcb contains a pointer to the root directory of + the volume. Each directory Fcb contains a queue of child Fcb's for + its children. There can also be detached subtrees due to open operations + by Id where the Fcb's are not connected to the root. + + The following diagram shows the root structure. + + +--------+ +--------+ + | Vcb |---->| Fcb |-----------------------------------------------+ + | | | Table |--------------------------------------------+ | | + | |--+ | |-----------------------------------------+ | | | + +--------+ | +--------+ | | | + | | | | | | | + | | | +--------------------+ | | | + | V +---------+ | | | | + | +--------+ | | | | | + | |RootFcb | V V | | | + +->| | +--------+ +--------+ | | | + | |-->|Child | |Child | | | | + +--------+ | Fcb |<-->| Fcb |<--> ... | | | + | | | | | | | + +--------+ +--------+ | | | + | | | + (Freestanding sub-tree) | | | + +--------+ | | | + |OpenById|<-----------------------------------------+ | | + | Dir | +--------+ | | + | |--->|OpenById|<------------------------------+ | + +--------+ | Child | +--------+ | + | Dir |--->|OpenById|<-------------------+ + +--------+ | Child | + | File | + +--------+ + + Attached to each Directory Fcb is a prefix table containing the names + of children of this directory for which there is an Fcb. Not all Fcb's + will necessarily have an entry in this table. + + +--------+ +--------+ + | Dir | | Prefix | + | Fcb |----->| Table |--------------------+ + | | | |-------+ | + +--------+ +--------+ | | + | | | | + | | | | + | V V V + | +--------+ +--------+ +--------+ +--------+ + | | Fcb | | Fcb | | Fcb | | Fcb | + +---------->| |<-->| |<-->| |<-->| | + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + + Each file object open on a CDROM volume contains two context pointers. The + first will point back to the Fcb for the file object. The second, if present, + points to a Ccb (ContextControlBlock) which contains the per-handle information. + This includes the state of any directory enumeration. + + +--------+ +--------+ +--------+ + | Fcb |<------| File | | Ccb | + | | | Object|--->| | + | | | | | | + +--------+ +--------+ +--------+ + ^ ^ + | | +--------+ +--------+ + | | | File | | Ccb | + | +---------| Object|--->| | + | | | | | + | +--------+ +--------+ + | + | +--------+ + | |Stream | + +--------------| File | + | Object| + +--------+ + + + Synchronization: + + 1. A resource in the CdData synchronizes access to the Vcb queue. This + is used during mount/verify/dismount operations. + + 2. A resource in the Vcb is used to synchronize access to Vcb for + open/close operations. Typically acquired shared, it + is acquired exclusively to lock out these operations. + + 3. A second resource in the Vcb is used to synchronize all file operations. + Typically acquired shared, it is acquired exclusively to lock + out all file operations. Acquiring both Vcb resources will lock + the entire volume. + + 4. A resource in the nonpaged Fcb will synchronize open/close operations + on an Fcb. + + 5. A fast mutex in the Vcb will protect access to the Fcb table and + the open counts in the Vcb. It is also used to modify the reference + counts in all Fcbs. This mutex cannot be acquired + exclusely and is an end resource. + + 6. A fast mutex in the Fcb will synchronize access to all Fcb fields + which aren't synchronized in some other way. A thread may acquire + mutexes for multiple Fcb's as long as it works it way toward the + root of the tree. This mutex can also be acquired recursively. + + 7. Normal locking order is CdData/Vcb/Fcb starting at any point in this + chain. The Vcb is required prior to acquiring resources for multiple + files. Shared ownership of the Vcb is sufficient in this case. + + 8. Normal locking order when acquiring multiple Fcb's is from some + starting Fcb and walking towards the root of tree. Create typically + walks down the tree. In this case we will attempt to acquire the + next node optimistically and if that fails we will reference + the current node in the tree, release it and acquire the next node. + At that point it will be safe to reacquire the parent node. + + 9. Locking order for the Fcb (via the fast mutex) will be from leaf of + tree back towards the root. No other resource may be acquired + after locking the Vcb (other than in-page reads). + + 10. Cleanup operations only lock the Vcb and Fcb long enough to change the + critical counts and share access fields. No reason to synchronize + otherwise. None of the structures can go away from beneath us + in this case. + + +--*/ + +#ifndef _CDSTRUC_ +#define _CDSTRUC_ + +typedef PVOID PBCB; //**** Bcb's are now part of the cache module + +#define BYTE_COUNT_EMBEDDED_NAME (32) + + +// +// The CD_MCB is used to store the mapping of logical file offset to +// logical disk offset. NOTE - This package only deals with the +// logical 2048 sectors. Translating to 'raw' sectors happens in +// software. We will embed a single MCB_ENTRY in the Fcb since this +// will be the typical case. +// + +typedef struct _CD_MCB { + + // + // Size and current count of the Mcb entries. + // + + ULONG MaximumEntryCount; + ULONG CurrentEntryCount; + + // + // Pointer to the start of the Mcb entries. + // + + struct _CD_MCB_ENTRY *McbArray; + +} CD_MCB; +typedef CD_MCB *PCD_MCB; + +typedef struct _CD_MCB_ENTRY { + + // + // Starting offset and number of bytes described by this entry. + // The Byte count is rounded to a logical block boundary if this is + // the last block. + // + + LONGLONG DiskOffset; + LONGLONG ByteCount; + + // + // Starting offset in the file of mapping described by this dirent. + // + + LONGLONG FileOffset; + + // + // Data length and block length. Data length is the length of each + // data block. Total length is the length of each data block and + // the skip size. + // + + LONGLONG DataBlockByteCount; + LONGLONG TotalBlockByteCount; + +} CD_MCB_ENTRY; +typedef CD_MCB_ENTRY *PCD_MCB_ENTRY; + + +// +// Cd name structure. The following structure is used to represent the +// full Cdrom name. This name can be stored in either Unicode or ANSI +// format. +// + +typedef struct _CD_NAME { + + // + // String containing name without the version number. + // The maximum length field for filename indicates the + // size of the buffer allocated for the two parts of the name. + // + + UNICODE_STRING FileName; + + // + // String containging the version number. + // + + UNICODE_STRING VersionString; + +} CD_NAME; +typedef CD_NAME *PCD_NAME; + +// +// Following is the splay link structure for the prefix lookup. +// The names can be in either Unicode string or Ansi string format. +// + +typedef struct _NAME_LINK { + + RTL_SPLAY_LINKS Links; + UNICODE_STRING FileName; + +} NAME_LINK; +typedef NAME_LINK *PNAME_LINK; + + +// +// Prefix entry. There is one of these for each name in the prefix table. +// An Fcb will have one of these embedded for the long name and an optional +// pointer to the short name entry. +// + +typedef struct _PREFIX_ENTRY { + + // + // Pointer to the Fcb for this entry. + // + + struct _FCB *Fcb; + + // + // Flags field. Used to indicate if the name is in the prefix table. + // + + ULONG PrefixFlags; + + // + // Exact case name match. + // + + NAME_LINK ExactCaseName; + + // + // Case-insensitive name link. + // + + NAME_LINK IgnoreCaseName; + + WCHAR FileNameBuffer[ BYTE_COUNT_EMBEDDED_NAME ]; + +} PREFIX_ENTRY; +typedef PREFIX_ENTRY *PPREFIX_ENTRY; + +#define PREFIX_FLAG_EXACT_CASE_IN_TREE (0x00000001) +#define PREFIX_FLAG_IGNORE_CASE_IN_TREE (0x00000002) + + +// +// The CD_DATA record is the top record in the CDROM file system in-memory +// data structure. This structure must be allocated from non-paged pool. +// + +typedef struct _CD_DATA { + + // + // The type and size of this record (must be CDFS_NTC_DATA_HEADER) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // A pointer to the Driver object we were initialized with + // + + PDRIVER_OBJECT DriverObject; + + // + // Vcb queue. + // + + LIST_ENTRY VcbQueue; + + // + // The following fields are used to allocate IRP context structures + // using a lookaside list, and other fixed sized structures from a + // small cache. We use the CdData mutex to protext these structures. + // + + ULONG IrpContextDepth; + ULONG IrpContextMaxDepth; + SINGLE_LIST_ENTRY IrpContextList; + + // + // Filesystem device object for CDFS. + // + + PDEVICE_OBJECT FileSystemDeviceObject; + + // + // Following are used to manage the async and delayed close queue. + // + // FspCloseActive - Indicates whether there is a thread processing the + // two close queues. + // ReduceDelayedClose - Indicates that we have hit the upper threshold + // for the delayed close queue and need to reduce it to lower threshold. + // + // AsyncCloseQueue - Queue of IrpContext waiting for async close operation. + // AsyncCloseCount - Number of entries on the async close queue. + // + // DelayedCloseQueue - Queue of IrpContextLite waiting for delayed close + // operation. + // MaxDelayedCloseCount - Trigger delay close work at this threshold. + // MinDelayedCloseCount - Turn off delay close work at this threshold. + // DelayedCloseCount - Number of entries on the delayted close queue. + // + // CloseItem - Workqueue item used to start FspClose thread. + // + + LIST_ENTRY AsyncCloseQueue; + ULONG AsyncCloseCount; + BOOLEAN FspCloseActive; + BOOLEAN ReduceDelayedClose; + USHORT PadUshort; + + // + // The following fields describe the deferred close file objects. + // + + LIST_ENTRY DelayedCloseQueue; + ULONG DelayedCloseCount; + ULONG MaxDelayedCloseCount; + ULONG MinDelayedCloseCount; + + // + // Fast mutex used to lock the fields of this structure. + // + + PVOID CdDataLockThread; + FAST_MUTEX CdDataMutex; + + // + // A resource variable to control access to the global CDFS data record + // + + ERESOURCE DataResource; + + // + // Cache manager call back structure, which must be passed on each call + // to CcInitializeCacheMap. + // + + CACHE_MANAGER_CALLBACKS CacheManagerCallbacks; + CACHE_MANAGER_CALLBACKS CacheManagerVolumeCallbacks; + + // + // This is the ExWorkerItem that does both kinds of deferred closes. + // + + PIO_WORKITEM CloseItem; + +} CD_DATA; +typedef CD_DATA *PCD_DATA; + + +// +// The Vcb (Volume control block) record corresponds to every +// volume mounted by the file system. They are ordered in a queue off +// of CdData.VcbQueue. +// +// The Vcb will be in several conditions during its lifespan. +// +// NotMounted - Disk is not currently mounted (i.e. removed +// from system) but cleanup and close operations are +// supported. +// +// MountInProgress - State of the Vcb from the time it is +// created until it is successfully mounted or the mount +// fails. +// +// Mounted - Volume is currently in the mounted state. +// +// Invalid - User has invalidated the volume. Only legal operations +// are cleanup and close. +// +// DismountInProgress - We have begun the process of tearing down the +// Vcb. It can be deleted when all the references to it +// have gone away. +// + +typedef enum _VCB_CONDITION { + + VcbNotMounted = 0, + VcbMountInProgress, + VcbMounted, + VcbInvalid, + VcbDismountInProgress + +} VCB_CONDITION; + +typedef struct _VCB { + + // + // The type and size of this record (must be CDFS_NTC_VCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Vpb for this volume. + // + + PVPB Vpb; + + // + // Device object for the driver below us. + // + + PDEVICE_OBJECT TargetDeviceObject; + + // + // File object used to lock the volume. + // + + PFILE_OBJECT VolumeLockFileObject; + + // + // Link into queue of Vcb's in the CdData structure. We will create a union with + // a LONGLONG to force the Vcb to be quad-aligned. + // + + union { + + LIST_ENTRY VcbLinks; + LONGLONG Alignment; + }; + + // + // State flags and condition for the Vcb. + // + + ULONG VcbState; + VCB_CONDITION VcbCondition; + + // + // Various counts for this Vcb. + // + // VcbCleanup - Open handles left on this system. + // VcbReference - Number of reasons this Vcb is still present. + // VcbUserReference - Number of user file objects still present. + // + + ULONG VcbCleanup; + ULONG VcbReference; + ULONG VcbUserReference; + + // + // Fcb for the Volume Dasd file, root directory and the Path Table. + // + + struct _FCB *VolumeDasdFcb; + struct _FCB *RootIndexFcb; + struct _FCB *PathTableFcb; + + // + // Location of current session and offset of volume descriptors. + // + + ULONG BaseSector; + ULONG VdSectorOffset; + ULONG PrimaryVdSectorOffset; + + // + // Following is a sector from the last non-cached read of an XA file. + // Also the cooked offset on the disk. + // + + PVOID XASector; + LONGLONG XADiskOffset; + + // + // Vcb resource. This is used to synchronize open/cleanup/close operations. + // + + ERESOURCE VcbResource; + + // + // File resource. This is used to synchronize all file operations except + // open/cleanup/close. + // + + ERESOURCE FileResource; + + // + // Vcb fast mutex. This is used to synchronize the fields in the Vcb + // when modified when the Vcb is not held exclusively. Included here + // are the count fields and Fcb table. + // + // We also use this to synchronize changes to the Fcb reference field. + // + + FAST_MUTEX VcbMutex; + PVOID VcbLockThread; + + // + // The following is used to synchronize the dir notify package. + // + + PNOTIFY_SYNC NotifySync; + + // + // The following is the head of a list of notify Irps. + // + + LIST_ENTRY DirNotifyList; + + // + // Logical block size for this volume as well constant values + // associated with the block size. + // + + ULONG BlockSize; + ULONG BlockToSectorShift; + ULONG BlockToByteShift; + ULONG BlocksPerSector; + ULONG BlockMask; + ULONG BlockInverseMask; + + // + // Fcb table. Synchronized with the Vcb fast mutex. + // + + RTL_GENERIC_TABLE FcbTable; + + // + // Volume TOC. Cache this information for quick lookup. + // + + PCDROM_TOC CdromToc; + ULONG TocLength; + ULONG TrackCount; + ULONG DiskFlags; + + // + // Block factor to determine last session information. + // + + ULONG BlockFactor; + + // + // Media change count from device driver for bulletproof detection + // of media movement + // + + ULONG MediaChangeCount; + + // + // For raw reads, CDFS must obey the port maximum transfer restrictions. + // + + ULONG MaximumTransferRawSectors; + ULONG MaximumPhysicalPages; + + // + // Preallocated VPB for swapout, so we are not forced to consider + // must succeed pool. + // + + PVPB SwapVpb; + +} VCB; +typedef VCB *PVCB; + +#define VCB_STATE_HSG (0x00000001) +#define VCB_STATE_ISO (0x00000002) +#define VCB_STATE_JOLIET (0x00000004) +#define VCB_STATE_LOCKED (0x00000010) +#define VCB_STATE_REMOVABLE_MEDIA (0x00000020) +#define VCB_STATE_CDXA (0x00000040) +#define VCB_STATE_AUDIO_DISK (0x00000080) +#define VCB_STATE_NOTIFY_REMOUNT (0x00000100) +#define VCB_STATE_VPB_NOT_ON_DEVICE (0x00000200) + + +// +// The Volume Device Object is an I/O system device object with a +// workqueue and an VCB record appended to the end. There are multiple +// of these records, one for every mounted volume, and are created during +// a volume mount operation. The work queue is for handling an overload +// of work requests to the volume. +// + +typedef struct _VOLUME_DEVICE_OBJECT { + + DEVICE_OBJECT DeviceObject; + + // + // The following field tells how many requests for this volume have + // either been enqueued to ExWorker threads or are currently being + // serviced by ExWorker threads. If the number goes above + // a certain threshold, put the request on the overflow queue to be + // executed later. + // + + ULONG PostedRequestCount; + + // + // The following field indicates the number of IRP's waiting + // to be serviced in the overflow queue. + // + + ULONG OverflowQueueCount; + + // + // The following field contains the queue header of the overflow queue. + // The Overflow queue is a list of IRP's linked via the IRP's ListEntry + // field. + // + + LIST_ENTRY OverflowQueue; + + // + // The following spinlock protects access to all the above fields. + // + + KSPIN_LOCK OverflowQueueSpinLock; + + // + // This is the file system specific volume control block. + // + + VCB Vcb; + +} VOLUME_DEVICE_OBJECT; +typedef VOLUME_DEVICE_OBJECT *PVOLUME_DEVICE_OBJECT; + + +// +// The following two structures are the separate union structures for +// data and index Fcb's. The path table is actually the same structure +// as the index Fcb since it uses the first few fields. +// + +typedef enum _FCB_CONDITION { + FcbGood = 1, + FcbBad, + FcbNeedsToBeVerified +} FCB_CONDITION; + +typedef struct _FCB_DATA { + + // + // The following field is used by the oplock module + // to maintain current oplock information. + // + + OPLOCK Oplock; + + // + // The following field is used by the filelock module + // to maintain current byte range locking information. + // A file lock is allocated as needed. + // + + PFILE_LOCK FileLock; + +} FCB_DATA; +typedef FCB_DATA *PFCB_DATA; + +typedef struct _FCB_INDEX { + + // + // Internal stream file. + // + + PFILE_OBJECT FileObject; + + // + // Offset of first entry in stream. This is for case where directory + // or path table does not begin on a sector boundary. This value is + // added to all offset values to determine the real offset. + // + + ULONG StreamOffset; + + // + // List of child fcbs. + // + + LIST_ENTRY FcbQueue; + + // + // Ordinal number for this directory. Combine this with the path table offset + // in the FileId and you have a starting point in the path table. + // + + ULONG Ordinal; + + // + // Children path table start. This is the offset in the path table + // for the first child of the directory. A value of zero indicates + // that we haven't found the first child yet. If there are no child + // directories we will position at a point in the path table so that + // subsequent searches will fail quickly. + // + + ULONG ChildPathTableOffset; + ULONG ChildOrdinal; + + // + // Root of splay trees for exact and ignore case prefix trees. + // + + PRTL_SPLAY_LINKS ExactCaseRoot; + PRTL_SPLAY_LINKS IgnoreCaseRoot; + +} FCB_INDEX; +typedef FCB_INDEX *PFCB_INDEX; + +typedef struct _FCB_NONPAGED { + + // + // Type and size of this record must be CDFS_NTC_FCB_NONPAGED + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The following field contains a record of special pointers used by + // MM and Cache to manipluate section objects. Note that the values + // are set outside of the file system. However the file system on an + // open/create will set the file object's SectionObject field to + // point to this field + // + + SECTION_OBJECT_POINTERS SegmentObject; + + // + // This is the resource structure for this Fcb. + // + + ERESOURCE FcbResource; + + // + // This is the FastMutex for this Fcb. + // + + FAST_MUTEX FcbMutex; + + // + // This is the mutex that is inserted into the FCB_ADVANCED_HEADER + // FastMutex field + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + +} FCB_NONPAGED; +typedef FCB_NONPAGED *PFCB_NONPAGED; + +// +// The Fcb/Dcb record corresponds to every open file and directory, and to +// every directory on an opened path. +// + +typedef struct _FCB { + + // + // The following field is used for fast I/O. It contains the node + // type code and size, indicates if fast I/O is possible, contains + // allocation, file, and valid data size, a resource, and call back + // pointers for FastIoRead and FastMdlRead. + // + // + // Node type codes for the Fcb must be one of the following. + // + // CDFS_NTC_FCB_PATH_TABLE + // CDFS_NTC_FCB_INDEX + // CDFS_NTC_FCB_DATA + // + + // + // Common Fsrtl Header. The named header is for the fieldoff.c output. We + // use the unnamed header internally. + // + + union{ + + FSRTL_ADVANCED_FCB_HEADER Header; + FSRTL_ADVANCED_FCB_HEADER; + }; + + // + // Vcb for this Fcb. + // + + PVCB Vcb; + + // + // Parent Fcb for this Fcb. This may be NULL if this file was opened + // by ID, also for the root Fcb. + // + + struct _FCB *ParentFcb; + + // + // Links to the queue of Fcb's in the parent. + // + + LIST_ENTRY FcbLinks; + + // + // FileId for this file. + // + + FILE_ID FileId; + + // + // Counts on this Fcb. Cleanup count represents the number of open handles + // on this Fcb. Reference count represents the number of reasons this Fcb + // is still present. It includes file objects, children Fcb and anyone + // who wants to prevent this Fcb from going away. Cleanup count is synchronized + // with the FcbResource. The reference count is synchronized with the + // VcbMutex. + // + + ULONG FcbCleanup; + ULONG FcbReference; + ULONG FcbUserReference; + + // + // State flags for this Fcb. + // + + ULONG FcbState; + + // + // NT style attributes for the Fcb. + // + + ULONG FileAttributes; + + // + // CDXA attributes for this file. + // + + USHORT XAAttributes; + + // + // File number from the system use area. + // + + UCHAR XAFileNumber; + + // + // This is the thread and count for the thread which has locked this + // Fcb. + // + + PVOID FcbLockThread; + ULONG FcbLockCount; + + // + // Pointer to the Fcb non-paged structures. + // + + PFCB_NONPAGED FcbNonpaged; + + // + // Share access structure. + // + + SHARE_ACCESS ShareAccess; + + // + // Mcb for the on disk mapping and a single map entry. + // + + CD_MCB_ENTRY McbEntry; + CD_MCB Mcb; + + // + // Embed the prefix entry for the longname. Store an optional pointer + // to a prefix structure for the short name. + // + + PPREFIX_ENTRY ShortNamePrefix; + PREFIX_ENTRY FileNamePrefix; + + // + // Time stamp for this file. + // + + LONGLONG CreationTime; + + union{ + + ULONG FcbType; + FCB_DATA; + FCB_INDEX; + }; + +} FCB; +typedef FCB *PFCB; + +#define FCB_STATE_INITIALIZED (0x00000001) +#define FCB_STATE_IN_FCB_TABLE (0x00000002) +#define FCB_STATE_MODE2FORM2_FILE (0x00000004) +#define FCB_STATE_MODE2_FILE (0x00000008) +#define FCB_STATE_DA_FILE (0x00000010) + +// +// These file types are read as raw 2352 byte sectors +// + +#define FCB_STATE_RAWSECTOR_MASK ( FCB_STATE_MODE2FORM2_FILE | \ + FCB_STATE_MODE2_FILE | \ + FCB_STATE_DA_FILE ) + +#define SIZEOF_FCB_DATA \ + (FIELD_OFFSET( FCB, FcbType ) + sizeof( FCB_DATA )) + +#define SIZEOF_FCB_INDEX \ + (FIELD_OFFSET( FCB, FcbType ) + sizeof( FCB_INDEX )) + + +// +// The Ccb record is allocated for every file object +// + +typedef struct _CCB { + + // + // Type and size of this record (must be CDFS_NTC_CCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Flags. Indicates flags to apply for the current open. + // + + ULONG Flags; + + // + // Fcb for the file being opened. + // + + PFCB Fcb; + + // + // We store state information in the Ccb for a directory + // enumeration on this handle. + // + + // + // Offset in the directory stream to base the next enumeration. + // + + ULONG CurrentDirentOffset; + CD_NAME SearchExpression; + +} CCB; +typedef CCB *PCCB; + +#define CCB_FLAG_OPEN_BY_ID (0x00000001) +#define CCB_FLAG_OPEN_RELATIVE_BY_ID (0x00000002) +#define CCB_FLAG_IGNORE_CASE (0x00000004) +#define CCB_FLAG_OPEN_WITH_VERSION (0x00000008) +#define CCB_FLAG_DISMOUNT_ON_CLOSE (0x00000010) + +// +// Following flags refer to index enumeration. +// + +#define CCB_FLAG_ENUM_NAME_EXP_HAS_WILD (0x00010000) +#define CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD (0x00020000) +#define CCB_FLAG_ENUM_MATCH_ALL (0x00040000) +#define CCB_FLAG_ENUM_VERSION_MATCH_ALL (0x00080000) +#define CCB_FLAG_ENUM_RETURN_NEXT (0x00100000) +#define CCB_FLAG_ENUM_INITIALIZED (0x00200000) +#define CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY (0x00400000) + + +// +// The Irp Context record is allocated for every orginating Irp. It is +// created by the Fsd dispatch routines, and deallocated by the CdComplete +// request routine +// + +typedef struct _IRP_CONTEXT { + + // + // Type and size of this record (must be CDFS_NTC_IRP_CONTEXT) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Originating Irp for the request. + // + + PIRP Irp; + + // + // Vcb for this operation. When this is NULL it means we were called + // with our filesystem device object instead of a volume device object. + // (Mount will fill this in once the Vcb is created) + // + + PVCB Vcb; + + // + // Exception encountered during the request. Any error raised explicitly by + // the file system will be stored here. Any other error raised by the system + // is stored here after normalizing it. + // + + NTSTATUS ExceptionStatus; + ULONG RaisedAtLineFile; + + // + // Flags for this request. + // + + ULONG Flags; + + // + // Real device object. This represents the physical device closest to the media. + // + + PDEVICE_OBJECT RealDevice; + + // + // Io context for a read request. + // Address of Fcb for teardown oplock in create case. + // + + union { + + struct _CD_IO_CONTEXT *IoContext; + PFCB *TeardownFcb; + }; + + // + // Top level irp context for this thread. + // + + struct _IRP_CONTEXT *TopLevel; + + // + // Major and minor function codes. + // + + UCHAR MajorFunction; + UCHAR MinorFunction; + + // + // Pointer to the top-level context if this IrpContext is responsible + // for cleaning it up. + // + + struct _THREAD_CONTEXT *ThreadContext; + + // + // This structure is used for posting to the Ex worker threads. + // + + WORK_QUEUE_ITEM WorkQueueItem; + +} IRP_CONTEXT; +typedef IRP_CONTEXT *PIRP_CONTEXT; + +#define IRP_CONTEXT_FLAG_ON_STACK (0x00000001) +#define IRP_CONTEXT_FLAG_MORE_PROCESSING (0x00000002) +#define IRP_CONTEXT_FLAG_WAIT (0x00000004) +#define IRP_CONTEXT_FLAG_FORCE_POST (0x00000008) +#define IRP_CONTEXT_FLAG_TOP_LEVEL (0x00000010) +#define IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS (0x00000020) +#define IRP_CONTEXT_FLAG_IN_FSP (0x00000040) +#define IRP_CONTEXT_FLAG_IN_TEARDOWN (0x00000080) +#define IRP_CONTEXT_FLAG_ALLOC_IO (0x00000100) +#define IRP_CONTEXT_FLAG_DISABLE_POPUPS (0x00000200) +#define IRP_CONTEXT_FLAG_FORCE_VERIFY (0x00000400) + +// +// Flags used for create. +// + +#define IRP_CONTEXT_FLAG_FULL_NAME (0x10000000) +#define IRP_CONTEXT_FLAG_TRAIL_BACKSLASH (0x20000000) + +// +// The following flags need to be cleared when a request is posted. +// + +#define IRP_CONTEXT_FLAGS_CLEAR_ON_POST ( \ + IRP_CONTEXT_FLAG_MORE_PROCESSING | \ + IRP_CONTEXT_FLAG_WAIT | \ + IRP_CONTEXT_FLAG_FORCE_POST | \ + IRP_CONTEXT_FLAG_TOP_LEVEL | \ + IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS | \ + IRP_CONTEXT_FLAG_IN_FSP | \ + IRP_CONTEXT_FLAG_IN_TEARDOWN | \ + IRP_CONTEXT_FLAG_DISABLE_POPUPS \ +) + +// +// The following flags need to be cleared when a request is retried. +// + +#define IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY ( \ + IRP_CONTEXT_FLAG_MORE_PROCESSING | \ + IRP_CONTEXT_FLAG_IN_TEARDOWN | \ + IRP_CONTEXT_FLAG_DISABLE_POPUPS \ +) + +// +// The following flags are set each time through the Fsp loop. +// + +#define IRP_CONTEXT_FSP_FLAGS ( \ + IRP_CONTEXT_FLAG_WAIT | \ + IRP_CONTEXT_FLAG_TOP_LEVEL | \ + IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS | \ + IRP_CONTEXT_FLAG_IN_FSP \ +) + + +// +// Following structure is used to queue a request to the delayed close queue. +// This structure should be the minimum block allocation size. +// + +typedef struct _IRP_CONTEXT_LITE { + + // + // Type and size of this record (must be CDFS_NTC_IRP_CONTEXT_LITE) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Fcb for the file object being closed. + // + + PFCB Fcb; + + // + // List entry to attach to delayed close queue. + // + + LIST_ENTRY DelayedCloseLinks; + + // + // User reference count for the file object being closed. + // + + ULONG UserReference; + + // + // Real device object. This represents the physical device closest to the media. + // + + PDEVICE_OBJECT RealDevice; + +} IRP_CONTEXT_LITE; +typedef IRP_CONTEXT_LITE *PIRP_CONTEXT_LITE; + + +// +// Context structure for asynchronous I/O calls. Most of these fields +// are actually only required for the ReadMultiple routines, but +// the caller must allocate one as a local variable anyway before knowing +// whether there are multiple requests are not. Therefore, a single +// structure is used for simplicity. +// + +typedef struct _CD_IO_CONTEXT { + + // + // These two fields are used for multiple run Io + // + + LONG IrpCount; + PIRP MasterIrp; + NTSTATUS Status; + BOOLEAN AllocatedContext; + + union { + + // + // This element handles the asynchronous non-cached Io + // + + struct { + + PERESOURCE Resource; + ERESOURCE_THREAD ResourceThreadId; + ULONG RequestedByteCount; + }; + + // + // and this element handles the synchronous non-cached Io. + // + + KEVENT SyncEvent; + }; + +} CD_IO_CONTEXT; +typedef CD_IO_CONTEXT *PCD_IO_CONTEXT; + + +// +// Following structure is used to track the top level request. Each Cdfs +// Fsd and Fsp entry point will examine the top level irp location in the +// thread local storage to determine if this request is top level and/or +// top level Cdfs. The top level Cdfs request will remember the previous +// value and update that location with a stack location. This location +// can be accessed by recursive Cdfs entry points. +// + +typedef struct _THREAD_CONTEXT { + + // + // CDFS signature. Used to confirm structure on stack is valid. + // + + ULONG Cdfs; + + // + // Previous value in top-level thread location. We restore this + // when done. + // + + PIRP SavedTopLevelIrp; + + // + // Top level Cdfs IrpContext. Initial Cdfs entry point on stack + // will store the IrpContext for the request in this stack location. + // + + PIRP_CONTEXT TopLevelIrpContext; + +} THREAD_CONTEXT; +typedef THREAD_CONTEXT *PTHREAD_CONTEXT; + + +// +// The following structure is used for enumerating the entries in the +// path table. We will always map this two sectors at a time so we don't +// have to worry about entries which span sectors. We move through +// one sector at a time though. We will unpin and remap after +// crossing a sector boundary. +// +// The only special case is where we span a cache view. In that case +// we will allocate a buffer and read both pieces into it. +// +// This strategy takes advantage of the CC enhancement which allows +// overlapping ranges. +// + +typedef struct _PATH_ENUM_CONTEXT { + + // + // Pointer to the current sector and the offset of this sector to + // the beginning of the path table. The Data pointer may be + // a pool block in the case where we cross a cache view + // boundary. Also the length of the data for this block. + // + + PVOID Data; + ULONG BaseOffset; + ULONG DataLength; + + // + // Bcb for the sector. (We may actually have pinned two sectors) + // This will be NULL for the case where we needed to allocate a + // buffer in the case where we span a cache view. + // + + PBCB Bcb; + + // + // Offset to current entry within the current data block. + // + + ULONG DataOffset; + + // + // Did we allocate the buffer for the entry. + // + + BOOLEAN AllocatedData; + + // + // End of Path Table. This tells us whether the current data + // block includes the end of the path table. This is the + // only block where we need to do a careful check about whether + // the path table entry fits into the buffer. + // + // Also once we have reached the end of the path table we don't + // need to remap the data as we move into the final sector. + // We always look at the last two sectors together. + // + + BOOLEAN LastDataBlock; + +} PATH_ENUM_CONTEXT; +typedef PATH_ENUM_CONTEXT *PPATH_ENUM_CONTEXT; + +#define VACB_MAPPING_MASK (VACB_MAPPING_GRANULARITY - 1) +#define LAST_VACB_SECTOR_OFFSET (VACB_MAPPING_GRANULARITY - SECTOR_SIZE) + + +// +// Path Entry. This is our representation of the on disk data. +// + +typedef struct _PATH_ENTRY { + + // + // Directory number and offset. This is the ordinal and the offset from + // the beginning of the path table stream for this entry. + // + // + + ULONG Ordinal; + ULONG PathTableOffset; + + // + // Logical block Offset on the disk for this entry. We already bias + // this by any Xar blocks. + // + + ULONG DiskOffset; + + // + // Length of on-disk path table entry. + // + + ULONG PathEntryLength; + + // + // Parent number. + // + + ULONG ParentOrdinal; + + // + // DirName length and Id. Typically the pointer here points to the raw on-disk + // bytes. We will point to a fixed self entry if this is the root directory. + // + + ULONG DirNameLen; + PCHAR DirName; + + // + // Following are the flags used to cleanup this structure. + // + + ULONG Flags; + + // + // The following is the filename string and version number strings. We embed a buffer + // large enough to hold two 8.3 names. One for exact case and one for case insensitive. + // + + CD_NAME CdDirName; + CD_NAME CdCaseDirName; + + WCHAR NameBuffer[BYTE_COUNT_EMBEDDED_NAME / sizeof( WCHAR ) * 2]; + +} PATH_ENTRY; +typedef PATH_ENTRY *PPATH_ENTRY; + +#define PATH_ENTRY_FLAG_ALLOC_BUFFER (0x00000001) + + +// +// Compound path entry. This structure combines the on-disk entries +// with the in-memory structures. +// + +typedef struct _COMPOUND_PATH_ENTRY { + + PATH_ENUM_CONTEXT PathContext; + PATH_ENTRY PathEntry; + +} COMPOUND_PATH_ENTRY; +typedef COMPOUND_PATH_ENTRY *PCOMPOUND_PATH_ENTRY; + + +// +// The following is used for enumerating through a directory via the +// dirents. +// + +typedef struct _DIRENT_ENUM_CONTEXT { + + // + // Pointer the current sector and the offset of this sector within + // the directory file. Also the data length of this pinned block. + // + + PVOID Sector; + ULONG BaseOffset; + ULONG DataLength; + + // + // Bcb for the sector. + // + + PBCB Bcb; + + // + // Offset to the current dirent within this sector. + // + + ULONG SectorOffset; + + // + // Length to next dirent. A zero indicates to move to the next sector. + // + + ULONG NextDirentOffset; + +} DIRENT_ENUM_CONTEXT; +typedef DIRENT_ENUM_CONTEXT *PDIRENT_ENUM_CONTEXT; + + +// +// Following structure is used to smooth out the differences in the HSG, ISO +// and Joliett directory entries. +// + +typedef struct _DIRENT { + + // + // Offset in the Directory of this entry. Note this includes + // any bytes added to the beginning of the directory to pad + // down to a sector boundary. + // + + ULONG DirentOffset; + + ULONG DirentLength; + + // + // Starting offset on the disk including any Xar blocks. + // + + ULONG StartingOffset; + + // + // DataLength of the data. If not the last block then this should + // be an integral number of logical blocks. + // + + ULONG DataLength; + + // + // The following field is the time stamp out of the directory entry. + // Use a pointer into the dirent for this. + // + + PCHAR CdTime; + + // + // The following field is the dirent file flags field. + // + + UCHAR DirentFlags; + + // + // Following field is a Cdfs flag field used to clean up this structure. + // + + UCHAR Flags; + + // + // The following fields indicate the file unit size and interleave gap + // for interleaved files. Each of these are in logical blocks. + // + + ULONG FileUnitSize; + ULONG InterleaveGapSize; + + // + // System use offset. Zero value indicates no system use area. + // + + ULONG SystemUseOffset; + + // + // CDXA attributes and file number for this file. + // + + USHORT XAAttributes; + UCHAR XAFileNumber; + + // + // Filename length and ID. We copy the length (in bytes) and keep + // a pointer to the start of the name. + // + + ULONG FileNameLen; + PCHAR FileName; + + // + // The following are the filenames stored by name and version numbers. + // The fixed buffer here can hold two Unicode 8.3 names. This allows + // us to upcase the name into a fixed buffer. + // + + CD_NAME CdFileName; + CD_NAME CdCaseFileName; + + // + // Data stream type. Indicates if this is audio, XA mode2 form2 or cooked sectors. + // + + XA_EXTENT_TYPE ExtentType; + + WCHAR NameBuffer[BYTE_COUNT_EMBEDDED_NAME / sizeof( WCHAR ) * 2]; + +} DIRENT; +typedef DIRENT *PDIRENT; + +#define DIRENT_FLAG_ALLOC_BUFFER (0x01) +#define DIRENT_FLAG_CONSTANT_ENTRY (0x02) + +#define DIRENT_FLAG_NOT_PERSISTENT (0) + + +// +// Following structure combines the on-disk information with the normalized +// structure. +// + +typedef struct _COMPOUND_DIRENT { + + DIRENT_ENUM_CONTEXT DirContext; + DIRENT Dirent; + +} COMPOUND_DIRENT; +typedef COMPOUND_DIRENT *PCOMPOUND_DIRENT; + + +// +// The following structure is used to enumerate the files in a directory. +// It contains three DirContext/Dirent pairs and then self pointers to +// know which of these is begin used how. +// + +typedef struct _FILE_ENUM_CONTEXT { + + // + // Pointers to the current compound dirents below. + // + // PriorDirent - Initial dirent for the last file encountered. + // InitialDirent - Initial dirent for the current file. + // CurrentDirent - Second or later dirent for the current file. + // + + PCOMPOUND_DIRENT PriorDirent; + PCOMPOUND_DIRENT InitialDirent; + PCOMPOUND_DIRENT CurrentDirent; + + // + // Flags indicating the state of the search. + // + + ULONG Flags; + + // + // This is an accumulation of the file sizes of the different extents + // of a single file. + // + + LONGLONG FileSize; + + // + // Short name for this file. + // + + CD_NAME ShortName; + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; + + // + // Array of compound dirents. + // + + COMPOUND_DIRENT Dirents[3]; + +} FILE_ENUM_CONTEXT; +typedef FILE_ENUM_CONTEXT *PFILE_ENUM_CONTEXT; + +#define FILE_CONTEXT_MULTIPLE_DIRENTS (0x00000001) + + +// +// RIFF header. Prepended to the data of a file containing XA sectors. +// This is a hard-coded structure except that we bias the 'ChunkSize' and +// 'RawSectors' fields with the file size. We also copy the attributes flag +// from the system use area in the dirent. We always initialize this +// structure by copying the XAFileHeader. +// + +typedef struct _RIFF_HEADER { + + ULONG ChunkId; + LONG ChunkSize; + ULONG SignatureCDXA; + ULONG SignatureFMT; + ULONG XAChunkSize; + ULONG OwnerId; + USHORT Attributes; + USHORT SignatureXA; + UCHAR FileNumber; + UCHAR Reserved[7]; + ULONG SignatureData; + ULONG RawSectors; + +} RIFF_HEADER; +typedef RIFF_HEADER *PRIFF_HEADER; + +// +// Audio play header for CDDA tracks. +// + +typedef struct _AUDIO_PLAY_HEADER { + + ULONG Chunk; + ULONG ChunkSize; + ULONG SignatureCDDA; + ULONG SignatureFMT; + ULONG FMTChunkSize; + USHORT FormatTag; + USHORT TrackNumber; + ULONG DiskID; + ULONG StartingSector; + ULONG SectorCount; + UCHAR TrackAddress[4]; + UCHAR TrackLength[4]; + +} AUDIO_PLAY_HEADER; +typedef AUDIO_PLAY_HEADER *PAUDIO_PLAY_HEADER; + + +// +// Some macros for supporting the use of a Generic Table +// containing all the FCB/DCBs and indexed by their FileId. +// +// For directories: +// +// The HighPart contains the path table offset of this directory in the +// path table. +// +// The LowPart contains zero except for the upper bit which is +// set to indicate that this is a directory. +// +// For files: +// +// The HighPart contains the path table offset of the parent directory +// in the path table. +// +// The LowPart contains the byte offset of the dirent in the parent +// directory file. +// +// A directory is always entered into the Fcb Table as if it's +// dirent offset was zero. This enables any child to look in the FcbTable +// for it's parent by searching with the same HighPart but with zero +// as the value for LowPart. +// +// The Id field is a LARGE_INTEGER where the High and Low parts can be +// accessed separately. +// +// The following macros are used to access the Fid fields. +// +// CdQueryFidDirentOffset - Accesses the Dirent offset field +// CdQueryFidPathTableNumber - Accesses the PathTable offset field +// CdSetFidDirentOffset - Sets the Dirent offset field +// CdSetFidPathTableNumber - Sets the PathTable ordinal field +// CdFidIsDirectory - Queries if directory bit is set +// CdFidSetDirectory - Sets directory bit +// + +#define FID_DIR_MASK 0x80000000 // high order bit means directory. + +#define CdQueryFidDirentOffset(I) ((I).LowPart & ~FID_DIR_MASK) +#define CdQueryFidPathTableOffset(I) ((I).HighPart) +#define CdSetFidDirentOffset(I,D) ((I).LowPart = D) +#define CdSetFidPathTableOffset(I,P) ((I).HighPart = P) +#define CdFidIsDirectory(I) FlagOn( (I).LowPart, FID_DIR_MASK ) +#define CdFidSetDirectory(I) SetFlag( (I).LowPart, FID_DIR_MASK ) + +#define CdSetFidFromParentAndDirent(I,F,D) { \ + CdSetFidPathTableOffset( (I), CdQueryFidPathTableOffset( (F)->FileId )); \ + CdSetFidDirentOffset( (I), (D)->DirentOffset ); \ + if (FlagOn( (D)->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { \ + CdFidSetDirectory((I)); \ + } \ +} + +#endif // _CDSTRUC_ + diff --git a/reactos/drivers/filesystems/cdfs_new/cleanup.c b/reactos/drivers/filesystems/cdfs_new/cleanup.c new file mode 100755 index 00000000000..49a18e7d09f --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/cleanup.c @@ -0,0 +1,332 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cleanup.c + +Abstract: + + This module implements the File Cleanup routine for Cdfs called by the + dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CLEANUP) + + +NTSTATUS +CdCommonCleanup ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for cleanup of a file/directory called by both + the fsd and fsp threads. + + Cleanup is invoked whenever the last handle to a file object is closed. + This is different than the Close operation which is invoked when the last + reference to a file object is deleted. + + The function of cleanup is to essentially "cleanup" the file/directory + after a user is done with it. The Fcb/Dcb remains around (because MM + still has the file object referenced) but is now available for another + user to open (i.e., as far as the user is concerned the is now closed). + + See close for a more complete description of what close does. + + We do no synchronization in this routine until we get to the point + where we modify the counts, share access and volume lock field. + + We need to update the Fcb and Vcb to show that a user handle has been closed. + The following structures and fields are affected. + + Vcb: + + VolumeLockFileObject - Did the user lock the volume with this file object. + VcbState - Check if we are unlocking the volume here. + VcbCleanup - Count of outstanding handles on the volume. + DirNotifyQueue - If this file object has pending DirNotify Irps. + + Fcb: + + ShareAccess - If this is a user handle. + FcbCleanup - Count of outstanding handles on this Fcb. + Oplock - Any outstanding oplocks on this file object. + FileLock - Any outstanding filelocks on this file object. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN SendUnlockNotification = FALSE; + BOOLEAN AttemptTeardown; + BOOLEAN VcbAcquired = FALSE; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + KIRQL SavedIrql; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get the file object out of the Irp and decode the type of open. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + TypeOfOpen = CdDecodeFileObject( IrpContext, + FileObject, + &Fcb, + &Ccb ); + + // + // No work here for either an UnopenedFile object or a StreamFileObject. + // + + if (TypeOfOpen <= StreamFileOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; + } + + // + // Keep a local pointer to the Vcb. + // + + Vcb = Fcb->Vcb; + + // + // Synchronise with reads while we set the cleanup complete + // flag on this fileobject. Once this flag is set, any further + // reads will be rejected (CdVerifyFcbOperation) + // + + CdAcquireFileExclusive( IrpContext, Fcb); + + // + // Set the flag in the FileObject to indicate that cleanup is complete. + // + + SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE ); + + CdReleaseFile( IrpContext, Fcb); + + // + // Acquire the current file. + // + + CdAcquireFcbExclusive( IrpContext, Fcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Case on the type of open that we are trying to cleanup. + // + + switch (TypeOfOpen) { + + case UserDirectoryOpen: + + // + // Check if we need to complete any dir notify Irps on this file object. + // + + FsRtlNotifyCleanup( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb ); + + break; + + case UserFileOpen: + + // + // Coordinate the cleanup operation with the oplock state. + // Oplock cleanup operations can always cleanup immediately so no + // need to check for STATUS_PENDING. + // + + FsRtlCheckOplock( &Fcb->Oplock, + Irp, + IrpContext, + NULL, + NULL ); + + // + // Unlock all outstanding file locks. + // + + if (Fcb->FileLock != NULL) { + + FsRtlFastUnlockAll( Fcb->FileLock, + FileObject, + IoGetRequestorProcess( Irp ), + NULL ); + } + + // + // Cleanup the cache map. + // + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + + // + // Check the fast io state. + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + break; + + case UserVolumeOpen : + + break; + + default : + + CdBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // Now lock the Vcb in order to modify the fields in the in-memory + // structures. + // + + CdLockVcb( IrpContext, Vcb ); + + // + // Decrement the cleanup counts in the Vcb and Fcb. + // + + CdDecrementCleanupCounts( IrpContext, Fcb ); + + // + // If the cleanup count hit zero and the volume is not mounted, we + // will want to try to spark teardown. + // + + AttemptTeardown = (Vcb->VcbCleanup == 0 && Vcb->VcbCondition == VcbNotMounted); + + // + // If this file object has locked the volume then perform the unlock operation. + // We do this regardless of explicit or implicit (no share DASD open) lock. + // + + if (FileObject == Vcb->VolumeLockFileObject) { + + ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_LOCKED)); + + IoAcquireVpbSpinLock( &SavedIrql ); + + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED); + ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + Vcb->VolumeLockFileObject = NULL; + SendUnlockNotification = TRUE; + + IoReleaseVpbSpinLock( SavedIrql ); + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We must clean up the share access at this time, since we may not + // get a Close call for awhile if the file was mapped through this + // File Object. + // + + IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); + + } finally { + + CdReleaseFcb( IrpContext, Fcb ); + + if (SendUnlockNotification) { + + FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); + } + } + + // + // If appropriate, try to spark teardown by purging the volume. Should + // this very fileobject we were cleaning up be the last reason for the + // volume to remain, teardown will commence on completion of this Irp. + // + + if (AttemptTeardown) { + + // + // Preacquire CdData here, since the purges will generate closes which + // may acquire CdData if there is a possibility of tearing the volume + // down. + // + + CdAcquireCdData( IrpContext); + + try { + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + VcbAcquired = TRUE; + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + } finally { + + if (VcbAcquired) { CdReleaseVcb( IrpContext, Vcb ); } + + CdReleaseCdData( IrpContext); + } + } + + // + // If this is a normal termination then complete the request + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/close.c b/reactos/drivers/filesystems/cdfs_new/close.c new file mode 100755 index 00000000000..5433f289ed0 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/close.c @@ -0,0 +1,1026 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Close.c + +Abstract: + + This module implements the File Close routine for Cdfs called by the + Fsd/Fsp dispatch routines. + + The close operation interacts with both the async and delayed close queues + in the CdData structure. Since close may be called recursively we may + violate the locking order in acquiring the Vcb or Fcb. In this case + we may move the request to the async close queue. If this is the last + reference on the Fcb and there is a chance the user may reopen this + file again soon we would like to defer the close. In this case we + may move the request to the async close queue. + + Once we are past the decode file operation there is no need for the + file object. If we are moving the request to either of the work + queues then we remember all of the information from the file object and + complete the request with STATUS_SUCCESS. The Io system can then + reuse the file object and we can complete the request when convenient. + + The async close queue consists of requests which we would like to + complete as soon as possible. They are queued using the original + IrpContext where some of the fields have been overwritten with + information from the file object. We will extract this information, + cleanup the IrpContext and then call the close worker routine. + + The delayed close queue consists of requests which we would like to + defer the close for. We keep size of this list within a range + determined by the size of the system. We let it grow to some maximum + value and then shrink to some minimum value. We allocate a small + structure which contains the key information from the file object + and use this information along with an IrpContext on the stack + to complete the request. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CLOSE) + +// +// Local support routines +// + +BOOLEAN +CdCommonClosePrivate ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFCB Fcb, + IN ULONG UserReference, + IN BOOLEAN FromFsd + ); + +VOID +CdQueueClose ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG UserReference, + IN BOOLEAN DelayedClose + ); + +PIRP_CONTEXT +CdRemoveClose ( + IN PVCB Vcb OPTIONAL + ); + +VOID +CdCloseWorker ( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonClose) +#pragma alloc_text(PAGE, CdCommonClosePrivate) +#pragma alloc_text(PAGE, CdQueueClose) +#pragma alloc_text(PAGE, CdRemoveClose) +#pragma alloc_text(PAGE, CdCloseWorker) +#endif + + +VOID +CdFspClose ( + IN PVCB Vcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to process the close queues in the CdData. If the + Vcb is passed then we want to remove all of the closes for this Vcb. + Otherwise we will do as many of the delayed closes as we need to do. + +Arguments: + + Vcb - If specified then we are looking for all of the closes for the + given Vcb. + +Return Value: + + None + +--*/ + +{ + PIRP_CONTEXT IrpContext; + IRP_CONTEXT StackIrpContext; + + THREAD_CONTEXT ThreadContext; + + PFCB Fcb; + ULONG UserReference; + + ULONG VcbHoldCount = 0; + PVCB CurrentVcb = NULL; + + BOOLEAN PotentialVcbTeardown = FALSE; + + PAGED_CODE(); + + FsRtlEnterFileSystem(); + + // + // Continue processing until there are no more closes to process. + // + + while (IrpContext = CdRemoveClose( Vcb )) { + + // + // If we don't have an IrpContext then use the one on the stack. + // Initialize it for this request. + // + + if (SafeNodeType( IrpContext ) != CDFS_NTC_IRP_CONTEXT ) { + + // + // Update the local values from the IrpContextLite. + // + + Fcb = ((PIRP_CONTEXT_LITE) IrpContext)->Fcb; + UserReference = ((PIRP_CONTEXT_LITE) IrpContext)->UserReference; + + // + // Update the stack irp context with the values from the + // IrpContextLite. + // + + CdInitializeStackIrpContext( &StackIrpContext, + (PIRP_CONTEXT_LITE) IrpContext ); + + // + // Free the IrpContextLite. + // + + CdFreeIrpContextLite( (PIRP_CONTEXT_LITE) IrpContext ); + + // + // Remember we have the IrpContext from the stack. + // + + IrpContext = &StackIrpContext; + + // + // Otherwise cleanup the existing IrpContext. + // + + } else { + + // + // Remember the Fcb and user reference count. + // + + Fcb = (PFCB) IrpContext->Irp; + IrpContext->Irp = NULL; + + UserReference = (ULONG) IrpContext->ExceptionStatus; + IrpContext->ExceptionStatus = STATUS_SUCCESS; + } + + // + // We have an IrpContext. Now we need to set the top level thread + // context. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FSP_FLAGS ); + + // + // If we were given a Vcb then there is a request on top of this. + // + + if (ARGUMENT_PRESENT( Vcb )) { + + ClearFlag( IrpContext->Flags, + IRP_CONTEXT_FLAG_TOP_LEVEL | IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS ); + } + + CdSetThreadContext( IrpContext, &ThreadContext ); + + // + // If we have hit the maximum number of requests to process without + // releasing the Vcb then release the Vcb now. If we are holding + // a different Vcb to this one then release the previous Vcb. + // + // In either case acquire the current Vcb. + // + // We use the MinDelayedCloseCount from the CdData since it is + // a convenient value based on the system size. Only thing we are trying + // to do here is prevent this routine starving other threads which + // may need this Vcb exclusively. + // + // Note that the check for potential teardown below is unsafe. We'll + // repeat later within the cddata lock. + // + + PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) && + (Fcb->Vcb->VcbCondition != VcbMounted) && + (Fcb->Vcb->VcbCondition != VcbMountInProgress) && + (Fcb->Vcb->VcbCleanup == 0); + + if (PotentialVcbTeardown || + (VcbHoldCount > CdData.MinDelayedCloseCount) || + (Fcb->Vcb != CurrentVcb)) { + + if (CurrentVcb != NULL) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + } + + if (PotentialVcbTeardown) { + + CdAcquireCdData( IrpContext ); + + // + // Repeat the checks with global lock held. The volume could have + // been remounted while we didn't hold the lock. + // + + PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) && + (Fcb->Vcb->VcbCondition != VcbMounted) && + (Fcb->Vcb->VcbCondition != VcbMountInProgress) && + (Fcb->Vcb->VcbCleanup == 0); + + if (!PotentialVcbTeardown) { + + CdReleaseCdData( IrpContext); + } + } + + CurrentVcb = Fcb->Vcb; + CdAcquireVcbShared( IrpContext, CurrentVcb, FALSE ); + + VcbHoldCount = 0; + + } else { + + VcbHoldCount += 1; + } + + // + // Call our worker routine to perform the close operation. + // + + CdCommonClosePrivate( IrpContext, CurrentVcb, Fcb, UserReference, FALSE ); + + // + // If the reference count on this Vcb is below our residual reference + // then check if we should dismount the volume. + // + + if (PotentialVcbTeardown) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + CdCheckForDismount( IrpContext, CurrentVcb, FALSE ); + + CurrentVcb = NULL; + + CdReleaseCdData( IrpContext ); + PotentialVcbTeardown = FALSE; + } + + // + // Complete the current request to cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + } + + // + // Release any Vcb we may still hold. + // + + if (CurrentVcb != NULL) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + + } + + FsRtlExitFileSystem(); +} + + +NTSTATUS +CdCommonClose ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the Fsd entry for the close operation. We decode the file + object to find the CDFS structures and type of open. We call our internal + worker routine to perform the actual work. If the work wasn't completed + then we post to one of our worker queues. The Ccb isn't needed after this + point so we delete the Ccb and return STATUS_SUCCESS to our caller in all + cases. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + ULONG UserReference = 0; + + BOOLEAN PotentialVcbTeardown = FALSE; + BOOLEAN ForceDismount = FALSE; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Decode the file object to get the type of open and Fcb/Ccb. + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, + IoGetCurrentIrpStackLocation( Irp )->FileObject, + &Fcb, + &Ccb ); + + // + // No work to do for unopened file objects. + // + + if (TypeOfOpen == UnopenedFileObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; + } + + Vcb = Fcb->Vcb; + + // + // Clean up any CCB associated with this open. + // + + if (Ccb != NULL) { + + UserReference = 1; + + // + // Was a FSCTL_DISMOUNT issued on this handle? If so, we need to + // force a dismount of the volume now. + // + + ForceDismount = BooleanFlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE); + + // + // We can always deallocate the Ccb if present. + // + + CdDeleteCcb( IrpContext, Ccb ); + } + + // + // If this is the last reference to a user file or directory on a + // currently mounted volume, then post it to the delayed close queue. Note + // that the VcbCondition check is unsafe, but it doesn't really matter - + // we just might delay the volume teardown a little by posting this close. + // + + if ((Vcb->VcbCondition == VcbMounted) && + (Fcb->FcbReference == 1) && + ((TypeOfOpen == UserFileOpen) || + (TypeOfOpen == UserDirectoryOpen))) { + + CdQueueClose( IrpContext, Fcb, UserReference, TRUE ); + IrpContext = NULL; + + // + // Otherwise try to process this close. Post to the async close queue + // if we can't acquire all of the resources. + // + + } else { + + // + // If we may be dismounting this volume then acquire the CdData + // resource. + // + // Since we now must make volumes go away as soon as reasonable after + // the last user handles closes, key off of the cleanup count. It is + // OK to do this more than neccesary. Since this Fcb could be holding + // a number of other Fcbs (and thus their references), a simple check + // on reference count is not appropriate. + // + // Do an unsafe check first to avoid taking the (global) cddata lock in the + // common case. + // + + if (((Vcb->VcbCleanup == 0) || ForceDismount) && + (Vcb->VcbCondition != VcbMounted)) { + + // + // Possible. Acquire CdData to synchronise with the remount path, and + // then repeat the tests. + // + // Note that we must send the notification outside of any locks, since + // the worker that processes the notify could also be calling into our + // pnp path which wants both CdData and VcbResource. For a force dismount + // the volume will be marked invalid (no going back), so we will definitely + // go ahead and dismount below. + // + + if (ForceDismount) { + + // + // Send notification. + // + + FsRtlNotifyVolumeEvent( IoGetCurrentIrpStackLocation( Irp )->FileObject, + FSRTL_VOLUME_DISMOUNT ); + } + + CdAcquireCdData( IrpContext ); + + if (((Vcb->VcbCleanup == 0) || ForceDismount) && + (Vcb->VcbCondition != VcbMounted) && + (Vcb->VcbCondition != VcbMountInProgress) && + FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS )) { + + PotentialVcbTeardown = TRUE; + } + else { + + // + // We can't dismount this volume now, there are other references or + // it's just been remounted. + // + + CdReleaseCdData( IrpContext); + } + } + + if (ForceDismount) { + + // + // Physically disconnect this Vcb from the device so a new mount can + // occur. Vcb deletion cannot happen at this time since there is + // a handle on it associated with this very request, but we'll call + // check for dismount again later anyway. + // + + CdCheckForDismount( IrpContext, Vcb, TRUE ); + } + + // + // Call the worker routine to perform the actual work. This routine + // should never raise except for a fatal error. + // + + if (!CdCommonClosePrivate( IrpContext, Vcb, Fcb, UserReference, TRUE )) { + + // + // If we didn't complete the request then post the request as needed. + // + + CdQueueClose( IrpContext, Fcb, UserReference, FALSE ); + IrpContext = NULL; + + // + // Check whether we should be dismounting the volume and then complete + // the request. + // + + } else if (PotentialVcbTeardown) { + + CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + + // + // Always complete this request with STATUS_SUCCESS. + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + if (PotentialVcbTeardown) { + + CdReleaseCdData( IrpContext ); + } + + // + // Always return STATUS_SUCCESS for closes. + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +BOOLEAN +CdCommonClosePrivate ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFCB Fcb, + IN ULONG UserReference, + IN BOOLEAN FromFsd + ) + +/*++ + +Routine Description: + + This is the worker routine for the close operation. We can be called in + an Fsd thread or from a worker Fsp thread. If called from the Fsd thread + then we acquire the resources without waiting. Otherwise we know it is + safe to wait. + + We check to see whether we should post this request to the delayed close + queue. If we are to process the close here then we acquire the Vcb and + Fcb. We will adjust the counts and call our teardown routine to see + if any of the structures should go away. + +Arguments: + + Vcb - Vcb for this volume. + + Fcb - Fcb for this request. + + UserReference - Number of user references for this file object. This is + zero for an internal stream. + + FromFsd - This request was called from an Fsd thread. Indicates whether + we should wait to acquire resources. + + DelayedClose - Address to store whether we should try to put this on + the delayed close queue. Ignored if this routine can process this + close. + +Return Value: + + BOOLEAN - TRUE if this thread processed the close, FALSE otherwise. + +--*/ + +{ + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Try to acquire the Vcb and Fcb. If we can't acquire them then return + // and let our caller know he should post the request to the async + // queue. + // + + if (CdAcquireVcbShared( IrpContext, Vcb, FromFsd )) { + + if (!CdAcquireFcbExclusive( IrpContext, Fcb, FromFsd )) { + + // + // We couldn't get the Fcb. Release the Vcb and let our caller + // know to post this request. + // + + CdReleaseVcb( IrpContext, Vcb ); + return FALSE; + } + + // + // We didn't get the Vcb. Let our caller know to post this request. + // + + } else { + + return FALSE; + } + + // + // Lock the Vcb and decrement the reference counts. + // + + CdLockVcb( IrpContext, Vcb ); + CdDecrementReferenceCounts( IrpContext, Fcb, 1, UserReference ); + CdUnlockVcb( IrpContext, Vcb ); + + // + // Call our teardown routine to see if this object can go away. + // If we don't remove the Fcb then release it. + // + + CdTeardownStructures( IrpContext, Fcb, &RemovedFcb ); + + if (!RemovedFcb) { + + CdReleaseFcb( IrpContext, Fcb ); + } + + // + // Release the Vcb and return to our caller. Let him know we completed + // this request. + // + + CdReleaseVcb( IrpContext, Vcb ); + + return TRUE; +} + +VOID +CdCloseWorker ( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ) +/*++ + +Routine Description: + + Worker routine to call CsFspClose. + +Arguments: + + DeviceObject - Filesystem registration device object + + Context - Callers context + +Return Value: + + None + +--*/ + +{ + CdFspClose (NULL); +} + + +VOID +CdQueueClose ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG UserReference, + IN BOOLEAN DelayedClose + ) + +/*++ + +Routine Description: + + This routine is called to queue a request to either the async or delayed + close queue. For the delayed queue we need to allocate a smaller + structure to contain the information about the file object. We do + that so we don't put the larger IrpContext structures into this long + lived queue. If we can allocate this structure then we put this + on the async queue instead. + +Arguments: + + Fcb - Fcb for this file object. + + UserReference - Number of user references for this file object. This is + zero for an internal stream. + + DelayedClose - Indicates whether this should go on the async or delayed + close queue. + +Return Value: + + None + +--*/ + +{ + PIRP_CONTEXT_LITE IrpContextLite = NULL; + BOOLEAN StartWorker = FALSE; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Start with the delayed queue request. We can move this to the async + // queue if there is an allocation failure. + // + + if (DelayedClose) { + + // + // Try to allocate non-paged pool for the IRP_CONTEXT_LITE. + // + + IrpContextLite = CdCreateIrpContextLite( IrpContext ); + } + + // + // We want to clear the top level context in this thread if + // necessary. Call our cleanup routine to do the work. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, TRUE ); + + // + // Synchronize with the CdData lock. + // + + CdLockCdData(); + + // + // If we have an IrpContext then put the request on the delayed close queue. + // + + if (IrpContextLite != NULL) { + + // + // Initialize the IrpContextLite. + // + + IrpContextLite->NodeTypeCode = CDFS_NTC_IRP_CONTEXT_LITE; + IrpContextLite->NodeByteSize = sizeof( IRP_CONTEXT_LITE ); + IrpContextLite->Fcb = Fcb; + IrpContextLite->UserReference = UserReference; + IrpContextLite->RealDevice = IrpContext->RealDevice; + + // + // Add this to the delayed close list and increment + // the count. + // + + InsertTailList( &CdData.DelayedCloseQueue, + &IrpContextLite->DelayedCloseLinks ); + + CdData.DelayedCloseCount += 1; + + // + // If we are above our threshold then start the delayed + // close operation. + // + + if (CdData.DelayedCloseCount > CdData.MaxDelayedCloseCount) { + + CdData.ReduceDelayedClose = TRUE; + + if (!CdData.FspCloseActive) { + + CdData.FspCloseActive = TRUE; + StartWorker = TRUE; + } + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + + // + // Cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + // + // Otherwise drop into the async case below. + // + + } else { + + // + // Store the information about the file object into the IrpContext. + // + + IrpContext->Irp = (PIRP) Fcb; + IrpContext->ExceptionStatus = (NTSTATUS) UserReference; + + // + // Add this to the async close list and increment the count. + // + + InsertTailList( &CdData.AsyncCloseQueue, + &IrpContext->WorkQueueItem.List ); + + CdData.AsyncCloseCount += 1; + + // + // Remember to start the Fsp close thread if not currently started. + // + + if (!CdData.FspCloseActive) { + + CdData.FspCloseActive = TRUE; + + StartWorker = TRUE; + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + } + + // + // Start the FspClose thread if we need to. + // + + if (StartWorker) { + + IoQueueWorkItem( CdData.CloseItem, CdCloseWorker, CriticalWorkQueue, NULL ); + } + + // + // Return to our caller. + // + + return; +} + + +// +// Local support routine +// + +PIRP_CONTEXT +CdRemoveClose ( + IN PVCB Vcb OPTIONAL + ) + +/*++ + +Routine Description: + +Arguments: + + This routine is called to scan the async and delayed close queues looking + for a suitable entry. If the Vcb is specified then we scan both queues + looking for an entry with the same Vcb. Otherwise we will look in the + async queue first for any close item. If none found there then we look + in the delayed close queue provided that we have triggered the delayed + close operation. + +Return Value: + + PIRP_CONTEXT - NULL if no work item found. Otherwise it is the pointer to + either the IrpContext or IrpContextLite for this request. + +--*/ + +{ + PIRP_CONTEXT IrpContext = NULL; + PIRP_CONTEXT NextIrpContext; + PIRP_CONTEXT_LITE NextIrpContextLite; + + PLIST_ENTRY Entry; + + PAGED_CODE(); + + ASSERT_OPTIONAL_VCB( Vcb ); + + // + // Lock the CdData to perform the scan. + // + + CdLockCdData(); + + // + // First check the list of async closes. + // + + Entry = CdData.AsyncCloseQueue.Flink; + + while (Entry != &CdData.AsyncCloseQueue) { + + // + // Extract the IrpContext. + // + + NextIrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + // + // If no Vcb was specified or this Vcb is for our volume + // then perform the close. + // + + if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContext->Vcb == Vcb)) { + + RemoveEntryList( Entry ); + CdData.AsyncCloseCount -= 1; + + IrpContext = NextIrpContext; + break; + } + + // + // Move to the next entry. + // + + Entry = Entry->Flink; + } + + // + // If we didn't find anything look through the delayed close + // queue. + // + // We will only check the delayed close queue if we were given + // a Vcb or the delayed close operation is active. + // + + if ((IrpContext == NULL) && + (ARGUMENT_PRESENT( Vcb ) || + (CdData.ReduceDelayedClose && + (CdData.DelayedCloseCount > CdData.MinDelayedCloseCount)))) { + + Entry = CdData.DelayedCloseQueue.Flink; + + while (Entry != &CdData.DelayedCloseQueue) { + + // + // Extract the IrpContext. + // + + NextIrpContextLite = CONTAINING_RECORD( Entry, + IRP_CONTEXT_LITE, + DelayedCloseLinks ); + + // + // If no Vcb was specified or this Vcb is for our volume + // then perform the close. + // + + if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContextLite->Fcb->Vcb == Vcb)) { + + RemoveEntryList( Entry ); + CdData.DelayedCloseCount -= 1; + + IrpContext = (PIRP_CONTEXT) NextIrpContextLite; + break; + } + + // + // Move to the next entry. + // + + Entry = Entry->Flink; + } + } + + // + // If the Vcb wasn't specified and we couldn't find an entry + // then turn off the Fsp thread. + // + + if (!ARGUMENT_PRESENT( Vcb ) && (IrpContext == NULL)) { + + CdData.FspCloseActive = FALSE; + CdData.ReduceDelayedClose = FALSE; + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + + return IrpContext; +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/create.c b/reactos/drivers/filesystems/cdfs_new/create.c new file mode 100755 index 00000000000..c9e0fcd2799 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/create.c @@ -0,0 +1,2916 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for Cdfs called by the + Fsd/Fsp dispatch routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CREATE) + +// +// Local support routines +// + +NTSTATUS +CdNormalizeFileNames ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN OpenByFileId, + IN BOOLEAN IgnoreCase, + IN TYPE_OF_OPEN RelatedTypeOfOpen, + IN PCCB RelatedCcb OPTIONAL, + IN PUNICODE_STRING RelatedFileName OPTIONAL, + IN OUT PUNICODE_STRING FileName, + IN OUT PCD_NAME RemainingName + ); + +NTSTATUS +CdOpenByFileId ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb + ); + +NTSTATUS +CdOpenExistingFcb ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN OUT PFCB *CurrentFcb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN IgnoreCase, + IN PCCB RelatedCcb OPTIONAL + ); + +NTSTATUS +CdOpenDirectoryFromPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN PCD_NAME DirName, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PPATH_ENTRY PathEntry, + IN BOOLEAN PerformUserOpen, + IN PCCB RelatedCcb OPTIONAL + ); + +NTSTATUS +CdOpenFileFromFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN PCD_NAME FileName, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PFILE_ENUM_CONTEXT FileContext, + IN PCCB RelatedCcb OPTIONAL + ); + +NTSTATUS +CdCompleteFcbOpen ( + IN PIRP_CONTEXT IrpContext, + PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN TYPE_OF_OPEN TypeOfOpen, + IN ULONG UserCcbFlags, + IN ACCESS_MASK DesiredAccess + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonCreate) +#pragma alloc_text(PAGE, CdCompleteFcbOpen) +#pragma alloc_text(PAGE, CdNormalizeFileNames) +#pragma alloc_text(PAGE, CdOpenByFileId) +#pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry) +#pragma alloc_text(PAGE, CdOpenExistingFcb) +#pragma alloc_text(PAGE, CdOpenFileFromFileContext) +#endif + + +NTSTATUS +CdCommonCreate ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for opening a file called by both the + Fsp and Fsd threads. + + The file can be opened either by name or by file Id either with or without + a relative name. The file name field in the file object passed to this routine + contains either a unicode string or a 64 bit value which is the file Id. + If this is not a Joliet disk then we will convert the unicode name to + an Oem string in this routine. If there is a related file object with + a name then we will already have converted that name to Oem. + + We will store the full name for the file in the file object on a successful + open. We will allocate a larger buffer if necessary and combine the + related and file object names. The only exception is the relative open + when the related file object is for an OpenByFileId file. If we need to + allocate a buffer for a case insensitive name then we allocate it at + the tail of the buffer we will store into the file object. The upcased + portion will begin immediately after the name defined by the FileName + in the file object. + + Once we have the full name in the file object we don't want to split the + name in the event of a retry. We use a flag in the IrpContext to indicate + that the name has been split. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - This is the status from this open operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFILE_OBJECT FileObject; + + COMPOUND_PATH_ENTRY CompoundPathEntry; + BOOLEAN CleanupCompoundPathEntry = FALSE; + + FILE_ENUM_CONTEXT FileContext; + BOOLEAN CleanupFileContext = FALSE; + BOOLEAN FoundEntry; + + PVCB Vcb; + + BOOLEAN OpenByFileId; + BOOLEAN IgnoreCase; + ULONG CreateDisposition; + + BOOLEAN ShortNameMatch; + ULONG ShortNameDirentOffset; + + BOOLEAN VolumeOpen = FALSE; + + // + // We will be acquiring and releasing file Fcb's as we move down the + // directory tree during opens. At any time we need to know the deepest + // point we have traversed down in the tree in case we need to cleanup + // any structures created here. + // + // CurrentFcb - represents this point. If non-null it means we have + // acquired it and need to release it in finally clause. + // + // NextFcb - represents the NextFcb to walk to but haven't acquired yet. + // + + TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject; + PFILE_OBJECT RelatedFileObject; + PCCB RelatedCcb = NULL; + + PFCB NextFcb; + PFCB CurrentFcb = NULL; + + // + // During the open we need to combine the related file object name + // with the remaining name. We also may need to upcase the file name + // in order to do a case-insensitive name comparison. We also need + // to restore the name in the file object in the event that we retry + // the request. We use the following string variables to manage the + // name. We will can put these strings into either Unicode or Ansi + // form. + // + // FileName - Pointer to name as currently stored in the file + // object. We store the full name into the file object early in + // the open operation. + // + // RelatedFileName - Pointer to the name in the related file object. + // + // RemainingName - String containing remaining name to parse. + // + // MatchingName - Address of name structure in FileContext which matched. + // We need this to know whether we matched the long or short name. + // + + PUNICODE_STRING FileName; + PUNICODE_STRING RelatedFileName = NULL; + + CD_NAME RemainingName; + CD_NAME FinalName; + PCD_NAME MatchingName; + + PAGED_CODE(); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get create parameters from the Irp. + // + + OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID ); + IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE ); + CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff; + + // + // Do some preliminary checks to make sure the operation is supported. + // We fail in the following cases immediately. + // + // - Open a paging file. + // - Open a target directory. + // - Open a file with Eas. + // - Create a file. + // + + if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) || + (IrpSp->Parameters.Create.EaLength != 0) || + (CreateDisposition == FILE_CREATE)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + return STATUS_ACCESS_DENIED; + } + + // + // Copy the Vcb to a local. Assume the starting directory is the root. + // + + Vcb = IrpContext->Vcb; + NextFcb = Vcb->RootIndexFcb; + + // + // Reference our input parameters to make things easier + // + + FileObject = IrpSp->FileObject; + RelatedFileObject = NULL; + + FileName = &FileObject->FileName; + + // + // Set up the file object's Vpb pointer in case anything happens. + // This will allow us to get a reasonable pop-up. + // + + if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) { + + RelatedFileObject = FileObject->RelatedFileObject; + FileObject->Vpb = RelatedFileObject->Vpb; + + RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb ); + + // + // Fail the request if this is not a user file object. + // + + if (RelatedTypeOfOpen < UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Remember the name in the related file object. + // + + RelatedFileName = &RelatedFileObject->FileName; + } + + // + // If we haven't initialized the names then make sure the strings are valid. + // If this an OpenByFileId then verify the file id buffer. + // + // After this routine returns we know that the full name is in the + // FileName buffer and the buffer will hold the upcased portion + // of the name yet to parse immediately after the full name in the + // buffer. Any trailing backslash has been removed and the flag + // in the IrpContext will indicate whether we removed the + // backslash. + // + + Status = CdNormalizeFileNames( IrpContext, + Vcb, + OpenByFileId, + IgnoreCase, + RelatedTypeOfOpen, + RelatedCcb, + RelatedFileName, + FileName, + &RemainingName ); + + // + // Return the error code if not successful. + // + + if (!NT_SUCCESS( Status )) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // We want to acquire the Vcb. Exclusively for a volume open, shared otherwise. + // The file name is empty for a volume open. + // + + if ((FileName->Length == 0) && + (RelatedTypeOfOpen <= UserVolumeOpen) && + !OpenByFileId) { + + VolumeOpen = TRUE; + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + } else { + + CdAcquireVcbShared( IrpContext, Vcb, FALSE ); + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify that the Vcb is not in an unusable condition. This routine + // will raise if not usable. + // + + CdVerifyVcb( IrpContext, Vcb ); + + // + // If the Vcb is locked then we cannot open another file + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If we are opening this file by FileId then process this immediately + // and exit. + // + + if (OpenByFileId) { + + // + // We only allow Dasd opens of audio disks. Fail this request at + // this point. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // Make sure we can wait for this request. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + try_return( Status = CdOpenByFileId( IrpContext, + IrpSp, + Vcb, + &CurrentFcb )); + } + + // + // If we are opening this volume Dasd then process this immediately + // and exit. + // + + if (VolumeOpen) { + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If they wanted to open a directory, surprise. + // + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Acquire the Fcb first. + // + + CurrentFcb = Vcb->VolumeDasdFcb; + CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE ); + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserVolumeOpen, + FALSE, + NULL )); + } + + // + // At this point CurrentFcb points to the deepest Fcb for this open + // in the tree. Let's acquire this Fcb to keep it from being deleted + // beneath us. + // + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CurrentFcb = NextFcb; + + // + // Do a prefix search if there is more of the name to parse. + // + + if (RemainingName.FileName.Length != 0) { + + // + // Do the prefix search to find the longest matching name. + // + + CdFindPrefix( IrpContext, + &CurrentFcb, + &RemainingName.FileName, + IgnoreCase ); + } + + // + // If the remaining name length is zero then we have found our + // target. + // + + if (RemainingName.FileName.Length == 0) { + + // + // If this is a file so verify the user didn't want to open + // a directory. + // + + if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || + FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserFileOpen, + IgnoreCase, + RelatedCcb )); + + // + // This is a directory. Verify the user didn't want to open + // as a file. + // + + } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + + // + // Open the file as a directory. + // + + } else { + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserDirectoryOpen, + IgnoreCase, + RelatedCcb )); + } + } + + // + // We have more work to do. We have a starting Fcb which we own shared. + // We also have the remaining name to parse. Walk through the name + // component by component looking for the full name. + // + + // + // Our starting Fcb better be a directory. + // + + if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // If we can't wait then post this request. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make sure the final name has no version string. + // + + FinalName.VersionString.Length = 0; + + while (TRUE) { + + ShortNameMatch = FALSE; + + // + // Split off the next component from the name. + // + + CdDissectName( IrpContext, + &RemainingName.FileName, + &FinalName.FileName ); + + // + // Go ahead and look this entry up in the path table. + // + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = TRUE; + + FoundEntry = CdFindPathEntry( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + &CompoundPathEntry ); + + // + // If we didn't find the entry then check if the current name + // is a possible short name. + // + + if (!FoundEntry) { + + ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName ); + + // + // If there is an embedded short name offset then look for the + // matching long name in the directory. + // + + if (ShortNameDirentOffset != MAXULONG) { + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + CdInitializeFileContext( IrpContext, &FileContext ); + CleanupFileContext = TRUE; + + FoundEntry = CdFindFileByShortName( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameDirentOffset, + &FileContext ); + + // + // If we found an entry and it is a directory then look + // this up in the path table. + // + + if (FoundEntry) { + + ShortNameMatch = TRUE; + + if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, + CD_ATTRIBUTE_DIRECTORY )) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + + FoundEntry = CdFindPathEntry( IrpContext, + CurrentFcb, + &FileContext.InitialDirent->Dirent.CdCaseFileName, + IgnoreCase, + &CompoundPathEntry ); + + // + // We better find this entry. + // + + if (!FoundEntry) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Upcase the name with the short name if case + // insensitive. + // + + if (IgnoreCase) { + + CdUpcaseName( IrpContext, &FinalName, &FinalName ); + } + + // + // We found a matching file. If we are at the last + // entry then break out of the loop and open the + // file below. Otherwise we return an error. + // + + } else if (RemainingName.FileName.Length == 0) { + + // + // Break out of the loop. We will process the dirent + // below. + // + + MatchingName = &FileContext.ShortName; + break; + + } else { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + } + } + + // + // We didn't find the name in either the path table or as + // a short name in a directory. If the remaining name + // length is zero then break out of the loop to search + // the directory. + // + + if (!FoundEntry) { + + if (RemainingName.FileName.Length == 0) { + + break; + + // + // Otherwise this path could not be cracked. + // + + } else { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + } + } + + // + // If this is an ignore case open then copy the exact case + // in the file object name. If it was a short name match then + // the name must be upcase already. + // + + if (IgnoreCase && !ShortNameMatch) { + + RtlCopyMemory( FinalName.FileName.Buffer, + CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer, + CompoundPathEntry.PathEntry.CdDirName.FileName.Length ); + } + + // + // If we have found the last component then open this as a directory + // and return to our caller. + // + + if (RemainingName.FileName.Length == 0) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameMatch, + &CompoundPathEntry.PathEntry, + TRUE, + RelatedCcb )); + } + + // + // Otherwise open an Fcb for this intermediate index Fcb. + // + + CdOpenDirectoryFromPathEntry( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameMatch, + &CompoundPathEntry.PathEntry, + FALSE, + NULL ); + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = FALSE; + } + + // + // We need to scan the current directory for a matching file name + // if we don't already have one. + // + + if (!FoundEntry) { + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + CdInitializeFileContext( IrpContext, &FileContext ); + CleanupFileContext = TRUE; + + // + // Split our search name into separate components. + // + + CdConvertNameToCdName( IrpContext, &FinalName ); + + FoundEntry = CdFindFile( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + &FileContext, + &MatchingName ); + } + + // + // If we didn't find a match then check if the name is invalid to + // determine which error code to return. + // + + if (!FoundEntry) { + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OVERWRITE)) { + + try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Any other operation return STATUS_ACCESS_DENIED. + // + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If this is a directory then the disk is corrupt because it wasn't + // in the Path Table. + // + + if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Make sure our opener didn't want a directory. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || + FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If this is an ignore case open then copy the exact case + // in the file object name. Any version portion should + // already be upcased. + // + + if (IgnoreCase) { + + RtlCopyMemory( FinalName.FileName.Buffer, + MatchingName->FileName.Buffer, + MatchingName->FileName.Length ); + } + + // + // Open the file using the file context. We already have the + // first and last dirents. + // + + try_return( Status = CdOpenFileFromFileContext( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + (BOOLEAN) (MatchingName == &FileContext.ShortName), + &FileContext, + RelatedCcb )); + + try_exit: NOTHING; + } finally { + + // + // Cleanup the PathEntry if initialized. + // + + if (CleanupCompoundPathEntry) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + } + + // + // Cleanup the FileContext if initialized. + // + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + // + // The result of this open could be success, pending or some error + // condition. + // + + if (AbnormalTermination()) { + + + // + // In the error path we start by calling our teardown routine if we + // have a CurrentFcb. + // + + if (CurrentFcb != NULL) { + + BOOLEAN RemovedFcb; + + CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb ); + + if (RemovedFcb) { + + CurrentFcb = NULL; + } + } + + // + // No need to complete the request. + // + + IrpContext = NULL; + Irp = NULL; + + // + // If we posted this request through the oplock package we need + // to show that there is no reason to complete the request. + // + + } else if (Status == STATUS_PENDING) { + + IrpContext = NULL; + Irp = NULL; + } + + // + // Release the Current Fcb if still acquired. + // + + if (CurrentFcb != NULL) { + + CdReleaseFcb( IrpContext, CurrentFcb ); + } + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Call our completion routine. It will handle the case where either + // the Irp and/or IrpContext are gone. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdNormalizeFileNames ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN OpenByFileId, + IN BOOLEAN IgnoreCase, + IN TYPE_OF_OPEN RelatedTypeOfOpen, + IN PCCB RelatedCcb OPTIONAL, + IN PUNICODE_STRING RelatedFileName OPTIONAL, + IN OUT PUNICODE_STRING FileName, + IN OUT PCD_NAME RemainingName + ) + +/*++ + +Routine Description: + + This routine is called to store the full name and upcased name into the + filename buffer. We only upcase the portion yet to parse. We also + check for a trailing backslash and lead-in double backslashes. This + routine also verifies the mode of the related open against the name + currently in the filename. + +Arguments: + + Vcb - Vcb for this volume. + + OpenByFileId - Indicates if the filename should be a 64 bit FileId. + + IgnoreCase - Indicates if this open is a case-insensitive operation. + + RelatedTypeOfOpen - Indicates the type of the related file object. + + RelatedCcb - Ccb for the related open. Ignored if no relative open. + + RelatedFileName - FileName buffer for related open. Ignored if no + relative open. + + FileName - FileName to update in this routine. The name should + either be a 64-bit FileId or a Unicode string. + + RemainingName - Name with the remaining portion of the name. This + will begin after the related name and any separator. For a + non-relative open we also step over the initial separator. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code + otherwise. + +--*/ + +{ + ULONG RemainingNameLength; + ULONG RelatedNameLength = 0; + ULONG SeparatorLength = 0; + + ULONG BufferLength; + + UNICODE_STRING NewFileName; + + PAGED_CODE(); + + // + // If this is the first pass then we need to build the full name and + // check for name compatibility. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) { + + // + // Deal with the regular file name case first. + // + + if (!OpenByFileId) { + + // + // This is here because the Win32 layer can't avoid sending me double + // beginning backslashes. + // + + if ((FileName->Length > sizeof( WCHAR )) && + (FileName->Buffer[1] == L'\\') && + (FileName->Buffer[0] == L'\\')) { + + // + // If there are still two beginning backslashes, the name is bogus. + // + + if ((FileName->Length > 2 * sizeof( WCHAR )) && + (FileName->Buffer[2] == L'\\')) { + + return STATUS_OBJECT_NAME_INVALID; + } + + // + // Slide the name down in the buffer. + // + + FileName->Length -= sizeof( WCHAR ); + + RtlMoveMemory( FileName->Buffer, + FileName->Buffer + 1, + FileName->Length ); + } + + // + // Check for a trailing backslash. Don't strip off if only character + // in the full name or for relative opens where this is illegal. + // + + if (((FileName->Length > sizeof( WCHAR)) || + ((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) && + (FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ); + FileName->Length -= sizeof( WCHAR ); + } + + // + // Remember the length we need for this portion of the name. + // + + RemainingNameLength = FileName->Length; + + // + // If this is a related file object then we verify the compatibility + // of the name in the file object with the relative file object. + // + + if (RelatedTypeOfOpen != UnopenedFileObject) { + + // + // If the filename length was zero then it must be legal. + // If there are characters then check with the related + // type of open. + // + + if (FileName->Length != 0) { + + // + // The name length must always be zero for a volume open. + // + + if (RelatedTypeOfOpen <= UserVolumeOpen) { + + return STATUS_INVALID_PARAMETER; + + // + // The remaining name cannot begin with a backslash. + // + + } else if (FileName->Buffer[0] == L'\\' ) { + + return STATUS_INVALID_PARAMETER; + + // + // If the related file is a user file then there + // is no file with this path. + // + + } else if (RelatedTypeOfOpen == UserFileOpen) { + + return STATUS_OBJECT_PATH_NOT_FOUND; + } + } + + // + // Remember the length of the related name when building + // the full name. We leave the RelatedNameLength and + // SeparatorLength at zero if the relative file is opened + // by Id. + // + + if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + // + // Add a separator if the name length is non-zero + // unless the relative Fcb is at the root. + // + + if ((FileName->Length != 0) && + (RelatedCcb->Fcb != Vcb->RootIndexFcb)) { + + SeparatorLength = sizeof( WCHAR ); + } + + RelatedNameLength = RelatedFileName->Length; + } + + // + // The full name is already in the filename. It must either + // be length 0 or begin with a backslash. + // + + } else if (FileName->Length != 0) { + + if (FileName->Buffer[0] != L'\\') { + + return STATUS_INVALID_PARAMETER; + } + + // + // We will want to trim the leading backslash from the + // remaining name we return. + // + + RemainingNameLength -= sizeof( WCHAR ); + SeparatorLength = sizeof( WCHAR ); + } + + // + // Now see if the buffer is large enough to hold the full name. + // + + BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength; + + // + // Check for an overflow of the maximum filename size. + // + + if (BufferLength > MAXUSHORT) { + + return STATUS_INVALID_PARAMETER; + } + + // + // Now see if we need to allocate a new buffer. + // + + if (FileName->MaximumLength < BufferLength) { + + NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + BufferLength, + TAG_FILE_NAME ); + + NewFileName.MaximumLength = (USHORT) BufferLength; + + } else { + + NewFileName.Buffer = FileName->Buffer; + NewFileName.MaximumLength = FileName->MaximumLength; + } + + // + // If there is a related name then we need to slide the remaining bytes up and + // insert the related name. Otherwise the name is in the correct position + // already. + // + + if (RelatedNameLength != 0) { + + // + // Store the remaining name in its correct position. + // + + if (RemainingNameLength != 0) { + + RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ), + FileName->Buffer, + RemainingNameLength ); + } + + RtlCopyMemory( NewFileName.Buffer, + RelatedFileName->Buffer, + RelatedNameLength ); + + // + // Add the separator if needed. + // + + if (SeparatorLength != 0) { + + *(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\'; + } + + // + // Update the filename value we got from the user. + // + + if (NewFileName.Buffer != FileName->Buffer) { + + if (FileName->Buffer != NULL) { + + CdFreePool( &FileName->Buffer ); + } + + FileName->Buffer = NewFileName.Buffer; + FileName->MaximumLength = NewFileName.MaximumLength; + } + + // + // Copy the name length to the user's filename. + // + + FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength); + } + + // + // Now update the remaining name to parse. + // + + RemainingName->FileName.MaximumLength = + RemainingName->FileName.Length = (USHORT) RemainingNameLength; + RemainingName->VersionString.Length = 0; + + RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer, + RelatedNameLength + SeparatorLength, + PWCHAR ); + + // + // Upcase the name if necessary. + // + + if (IgnoreCase && (RemainingNameLength != 0)) { + + CdUpcaseName( IrpContext, + RemainingName, + RemainingName ); + } + + // + // Do a quick check to make sure there are no wildcards. + // + + if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) { + + return STATUS_OBJECT_NAME_INVALID; + } + + // + // For the open by file Id case we verify the name really contains + // a 64 bit value. + // + + } else { + + // + // Check for validity of the buffer. + // + + if (FileName->Length != sizeof( FILE_ID )) { + + return STATUS_INVALID_PARAMETER; + } + } + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME ); + + // + // If we are in the retry path then the full name is already in the + // file object name. If this is a case-sensitive operation then + // we need to upcase the name from the end of any related file name already stored + // there. + // + + } else { + + // + // Assume there is no relative name. + // + + RemainingName->FileName = *FileName; + RemainingName->VersionString.Length = 0; + + // + // Nothing to do if the name length is zero. + // + + if (RemainingName->FileName.Length != 0) { + + // + // If there is a relative name then we need to walk past it. + // + + if (RelatedTypeOfOpen != UnopenedFileObject) { + + // + // Nothing to walk past if the RelatedCcb is opened by FileId. + // + + + if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + // + // Related file name is a proper prefix of the full name. + // We step over the related name and if we are then + // pointing at a separator character we step over that. + // + + RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, + RelatedFileName->Length, + PWCHAR ); + + RemainingName->FileName.Length -= RelatedFileName->Length; + } + } + + // + // If we are pointing at a separator character then step past that. + // + + if (RemainingName->FileName.Length != 0) { + + if (*(RemainingName->FileName.Buffer) == L'\\') { + + RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, + sizeof( WCHAR ), + PWCHAR ); + + RemainingName->FileName.Length -= sizeof( WCHAR ); + } + } + } + + // + // Upcase the name if necessary. + // + + if (IgnoreCase && (RemainingName->FileName.Length != 0)) { + + CdUpcaseName( IrpContext, + RemainingName, + RemainingName ); + } + } + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdOpenByFileId ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb + ) + +/*++ + +Routine Description: + + This routine is called to open a file by the FileId. The file Id is in + the FileObject name buffer and has been verified to be 64 bits. + + We extract the Id number and then check to see whether we are opening a + file or directory and compare that with the create options. If this + generates no error then optimistically look up the Fcb in the Fcb Table. + + If we don't find the Fcb then we need to carefully verify there is a file + at this offset. First check whether the Parent Fcb is in the table. If + not then lookup the parent at the path table offset given by file ID. + + If found then build the Fcb from this entry and store the new Fcb in the + tree. + + We know have the parent Fcb. Do a directory scan to find the dirent at + the given offset in this stream. This must point to the first entry + of a valid file. + + Finally we call our worker routine to complete the open on this Fcb. + +Arguments: + + IrpSp - Stack location within the create Irp. + + Vcb - Vcb for this volume. + + CurrentFcb - Address to store the Fcb for this open. We only store the + CurrentFcb here when we have acquired it so our caller knows to + free or deallocate it. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_ACCESS_DENIED; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN Found; + + ULONG StreamOffset; + + NODE_TYPE_CODE NodeTypeCode; + TYPE_OF_OPEN TypeOfOpen; + + FILE_ENUM_CONTEXT FileContext; + BOOLEAN CleanupFileContext = FALSE; + + COMPOUND_PATH_ENTRY CompoundPathEntry; + BOOLEAN CleanupCompoundPathEntry = FALSE; + + FILE_ID FileId; + FILE_ID ParentFileId; + + PFCB NextFcb; + + PAGED_CODE(); + + // + // Extract the FileId from the FileObject. + // + + RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID )); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Go ahead and figure out the TypeOfOpen and NodeType. We can + // get these from the input FileId. + // + + if (CdFidIsDirectory( FileId )) { + + TypeOfOpen = UserDirectoryOpen; + NodeTypeCode = CDFS_NTC_FCB_INDEX; + + // + // If the offset isn't zero then the file Id is bad. + // + + if (CdQueryFidDirentOffset( FileId ) != 0) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + } else { + + TypeOfOpen = UserFileOpen; + NodeTypeCode = CDFS_NTC_FCB_DATA; + } + + // + // Acquire the Vcb and check if there is already an Fcb. + // If not we will need to carefully verify the Fcb. + // We will post the request if we don't find the Fcb and this + // request can't wait. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId ); + + if (NextFcb == NULL) { + + // + // Get the path table offset from the file id. + // + + StreamOffset = CdQueryFidPathTableOffset( FileId ); + + // + // Build the parent FileId for this and try looking it + // up in the PathTable. + // + + CdSetFidDirentOffset( ParentFileId, 0 ); + CdSetFidPathTableOffset( ParentFileId, StreamOffset ); + CdFidSetDirectory( ParentFileId ); + + NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId ); + + // + // If not present then walk through the PathTable to this point. + // + + if (NextFcb == NULL) { + + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // Check that the path table offset lies within the path + // table. + // + + if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = TRUE; + + // + // Start at the first entry in the PathTable. + // + + CdLookupPathEntry( IrpContext, + Vcb->PathTableFcb->StreamOffset, + 1, + TRUE, + &CompoundPathEntry ); + + // + // Continue looking until we have passed our target offset. + // + + while (TRUE) { + + // + // Move to the next entry. + // + + Found = CdLookupNextPathEntry( IrpContext, + &CompoundPathEntry.PathContext, + &CompoundPathEntry.PathEntry ); + + // + // If we didn't find the entry or are beyond it then the + // input Id is invalid. + // + + if (!Found || + (CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + // + // If the FileId specified a directory then we have found + // the entry. Make sure our caller wanted to open a directory. + // + + if ((TypeOfOpen == UserDirectoryOpen) && + FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // Lock the Vcb and create the Fcb if necessary. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found ); + + // + // It's possible that someone got in here ahead of us. + // + + if (!Found) { + + CdInitializeFcbFromPathEntry( IrpContext, + NextFcb, + NULL, + &CompoundPathEntry.PathEntry ); + } + + // + // If the user wanted to open a directory then we have found + // it. Store this Fcb into the CurrentFcb and skip the + // directory scan. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + *CurrentFcb = NextFcb; + NextFcb = NULL; + } + } + + // + // Perform the directory scan if we don't already have our target. + // + + if (NextFcb != NULL) { + + // + // Acquire the parent. We currently own the Vcb lock so + // do this without waiting first. + // + + if (!CdAcquireFcbExclusive( IrpContext, + NextFcb, + TRUE )) { + + NextFcb->FcbReference += 1; + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + // + // Set up the CurrentFcb pointers. We know there was + // no previous parent in this case. + // + + *CurrentFcb = NextFcb; + + // + // Calculate the offset in the stream. + // + + StreamOffset = CdQueryFidDirentOffset( FileId ); + + // + // Create the stream file if it doesn't exist. This will update + // the Fcb with the size from the self entry. + // + + if (NextFcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Vcb, NextFcb ); + } + + // + // If our offset is beyond the end of the directory then the + // FileId is invalid. + // + + if (StreamOffset > NextFcb->FileSize.LowPart) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Otherwise position ourselves at the self entry and walk + // through dirent by dirent until this location is found. + // + + CdInitializeFileContext( IrpContext, &FileContext ); + CdLookupInitialFileDirent( IrpContext, + NextFcb, + &FileContext, + NextFcb->StreamOffset ); + + CleanupFileContext = TRUE; + + while (TRUE) { + + // + // Move to the first entry of the next file. + // + + Found = CdLookupNextInitialFileDirent( IrpContext, + NextFcb, + &FileContext ); + + // + // If we didn't find the entry or are beyond it then the + // input Id is invalid. + // + + if (!Found || + (FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + // + // This better not be a directory. Directory FileIds must + // refer to the self entry for directories. + // + + if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, + CD_ATTRIBUTE_DIRECTORY )) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Check that our caller wanted to open a file. + // + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Otherwise we want to collect all of the dirents for this file + // and create an Fcb with this. + // + + CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext ); + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found ); + + // + // It's possible that someone has since created this Fcb since we + // first checked. If so then can simply use this. Otherwise + // we need to initialize a new Fcb and attach it to our parent + // and insert it into the Fcb Table. + // + + if (!Found) { + + CdInitializeFcbFromFileContext( IrpContext, + NextFcb, + *CurrentFcb, + &FileContext ); + } + } + + // + // We have the Fcb. Check that the type of the file is compatible with + // the desired type of file to open. + // + + } else { + + if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + } + + // + // If we have a the previous Fcb and have inserted the next Fcb into + // the Fcb Table. It is safe to release the current Fcb if present + // since it is referenced through the child Fcb. + // + + if (*CurrentFcb != NULL) { + + CdReleaseFcb( IrpContext, *CurrentFcb ); + } + + // + // We now know the Fcb and currently hold the Vcb lock. + // Try to acquire this Fcb without waiting. Otherwise we + // need to reference it, drop the Vcb, acquire the Fcb and + // then dereference the Fcb. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + // + // Move to this Fcb. + // + + *CurrentFcb = NextFcb; + + // + // Check the requested access on this Fcb. + // + + if (!CdIllegalFcbAccess( IrpContext, + TypeOfOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + TypeOfOpen, + CCB_FLAG_OPEN_BY_ID, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + } + + try_exit: NOTHING; + } finally { + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + if (CleanupCompoundPathEntry) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + } + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdOpenExistingFcb ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN OUT PFCB *CurrentFcb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN IgnoreCase, + IN PCCB RelatedCcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to open an Fcb which is already in the Fcb table. + We will verify the access to the file and then call our worker routine + to perform the final operations. + +Arguments: + + IrpSp - Pointer to the stack location for this open. + + CurrentFcb - Address of Fcb to open. We will clear this if the Fcb + is released here. + + TypeOfOpen - Indicates whether we are opening a file, directory or volume. + + IgnoreCase - Indicates if this open is case-insensitive. + + RelatedCcb - Ccb for related file object if relative open. We use + this when setting the Ccb flags for this open. It will tell + us whether the name currently in the file object is relative or + absolute. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + + NTSTATUS Status = STATUS_ACCESS_DENIED; + + PAGED_CODE(); + + // + // Check that the desired access is legal. + // + + if (!CdIllegalFcbAccess( IrpContext, + TypeOfOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + // + // Set the Ignore case. + // + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Check the related Ccb to see if this was an OpenByFileId and + // whether there was a version. + // + + if (ARGUMENT_PRESENT( RelatedCcb )) { + + SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION )); + + + if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); + } + } + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + (*CurrentFcb)->Vcb, + CurrentFcb, + TypeOfOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdOpenDirectoryFromPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN PCD_NAME DirName, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PPATH_ENTRY PathEntry, + IN BOOLEAN PerformUserOpen, + IN PCCB RelatedCcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to open a directory where the directory was found + in the path table. This routine is called in the case where this is the + file to open for the user and where this is an intermediate node in the + full path to open. + + We first check that the desired access is legal for a directory. Then we + construct the FileId for this and do a check to see if it is the Fcb + Table. It is always possible that either it was created since or simply + wasn't in the prefix table at the time of the prefix table search. + Initialize the Fcb and store into the FcbTable if not present. + + Next we will add this to the prefix table of our parent if needed. + + Once we know that the new Fcb has been initialized then we move our pointer + in the tree down to this position. + + This routine does not own the Vcb lock on entry. We must be sure to release + it on exit. + +Arguments: + + IrpSp - Stack location for this request. + + Vcb - Vcb for this volume. + + CurrentFcb - On input this is the parent of the Fcb to open. On output we + store the Fcb for the file being opened. + + DirName - This is always the exact name used to reach this file. + + IgnoreCase - Indicates the type of case match for the open. + + ShortNameMatch - Indicates if we are opening via the short name. + + PathEntry - Path entry for the entry found. + + PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise. + + RelatedCcb - RelatedCcb for relative file object used to make this open. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + FILE_ID FileId; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN FcbExisted; + + PFCB NextFcb; + PFCB ParentFcb = NULL; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check for illegal access to this file. + // + + if (PerformUserOpen && + CdIllegalFcbAccess( IrpContext, + UserDirectoryOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Check the related Ccb to see if this was an OpenByFileId. + // + + if (ARGUMENT_PRESENT( RelatedCcb ) && + FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID; + } + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Build the file Id for this file. + // + + FileId.QuadPart = 0; + CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset ); + CdFidSetDirectory( FileId ); + + // + // Lock the Vcb so we can examine the Fcb Table. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + // + // Get the Fcb for this directory. + // + + NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted ); + + // + // If the Fcb was created here then initialize from the values in the + // path table entry. + // + + if (!FcbExisted) { + + CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry ); + } + + // + // Now try to acquire the new Fcb without waiting. We will reference + // the Fcb and retry with wait if unsuccessful. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + // + // Unlock the Vcb and move down to this new Fcb. Remember that we still + // own the parent however. + // + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + ParentFcb = *CurrentFcb; + *CurrentFcb = NextFcb; + + // + // Store this name into the prefix table for the parent. + // + + if (ShortNameMatch) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + DirName, + FALSE, + TRUE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + DirName, + TRUE, + TRUE, + ParentFcb ); + } + + } else { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + &PathEntry->CdDirName, + FALSE, + FALSE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + &PathEntry->CdCaseDirName, + TRUE, + FALSE, + ParentFcb ); + } + } + + // + // Release the parent Fcb at this point. + // + + CdReleaseFcb( IrpContext, ParentFcb ); + ParentFcb = NULL; + + // + // Call our worker routine to complete the open. + // + + if (PerformUserOpen) { + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + UserDirectoryOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + } + + } finally { + + // + // Unlock the Vcb if held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release the parent if held. + // + + if (ParentFcb != NULL) { + + CdReleaseFcb( IrpContext, ParentFcb ); + } + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdOpenFileFromFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN PCD_NAME FileName, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PFILE_ENUM_CONTEXT FileContext, + IN PCCB RelatedCcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to open a file where the file was found in a directory scan. + This should only be for a file in the case since we will find the directories in the + path table. + + We first check that the desired access is legal for this file. Then we + construct the FileId for this and do a check to see if it is the Fcb + Table. It is always possible that either it was created since or simply + wasn't in the prefix table at the time of the prefix table search. + Initialize the Fcb and store into the FcbTable if not present. + + Next we will add this to the prefix table of our parent if needed. + + Once we know that the new Fcb has been initialized then we move our pointer + in the tree down to this position. + + This routine does not own the Vcb lock on entry. We must be sure to release + it on exit. + +Arguments: + + IrpSp - Stack location for this request. + + Vcb - Vcb for the current volume. + + CurrentFcb - On input this is the parent of the Fcb to open. On output we + store the Fcb for the file being opened. + + FileName - This is always the exact name used to reach this file. + + IgnoreCase - Indicates the type of case of CaseName above. + + ShortNameMatch - Indicates if we are opening via the short name. + + FileContext - This is the context used to find the file. + + RelatedCcb - RelatedCcb for relative file object used to make this open. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + FILE_ID FileId; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN FcbExisted; + + PFCB NextFcb; + PFCB ParentFcb = NULL; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check for illegal access to this file. + // + + if (CdIllegalFcbAccess( IrpContext, + UserFileOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Check if a version number was used to open this file. + // + + if (FileName->VersionString.Length != 0) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION ); + } + + // + // Check the related Ccb to see if this was an OpenByFileId. + // + + if (ARGUMENT_PRESENT( RelatedCcb ) && + FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); + } + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Build the file Id for this file. We can use the path table offset from the + // parent and the directory offset from the dirent. + // + + CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId )); + CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset ); + + // + // Lock the Vcb so we can examine the Fcb Table. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + // + // Get the Fcb for this file. + // + + NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted ); + + // + // If the Fcb was created here then initialize from the values in the + // dirent. + // + + if (!FcbExisted) { + + CdInitializeFcbFromFileContext( IrpContext, + NextFcb, + *CurrentFcb, + FileContext ); + } + + // + // Now try to acquire the new Fcb without waiting. We will reference + // the Fcb and retry with wait if unsuccessful. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + // + // Unlock the Vcb and move down to this new Fcb. Remember that we still + // own the parent however. + // + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + ParentFcb = *CurrentFcb; + *CurrentFcb = NextFcb; + + // + // Store this name into the prefix table for the parent. + // + + + if (ShortNameMatch) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + FileName, + FALSE, + TRUE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + FileName, + TRUE, + TRUE, + ParentFcb ); + } + + // + // Insert this into the prefix table if we found this without + // using a version string. + // + + } else if (FileName->VersionString.Length == 0) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + &FileContext->InitialDirent->Dirent.CdFileName, + FALSE, + FALSE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + &FileContext->InitialDirent->Dirent.CdCaseFileName, + TRUE, + FALSE, + ParentFcb ); + } + } + + // + // Release the parent Fcb at this point. + // + + CdReleaseFcb( IrpContext, ParentFcb ); + ParentFcb = NULL; + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + UserFileOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + + } finally { + + // + // Unlock the Vcb if held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release the parent if held. + // + + if (ParentFcb != NULL) { + + CdReleaseFcb( IrpContext, ParentFcb ); + } + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdCompleteFcbOpen ( + IN PIRP_CONTEXT IrpContext, + PIO_STACK_LOCATION IrpSp, + IN PVCB Vcb, + IN OUT PFCB *CurrentFcb, + IN TYPE_OF_OPEN TypeOfOpen, + IN ULONG UserCcbFlags, + IN ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This is the worker routine which takes an existing Fcb and completes + the open. We will do any necessary oplock checks and sharing checks. + Finally we will create the Ccb and update the file object and any + file object flags. + +Arguments: + + IrpSp - Stack location for the current request. + + Vcb - Vcb for the current volume. + + CurrentFcb - Address of pointer to Fcb to open. We clear this field if + we release the resource for this file. + + TypeOfOpen - Type of open for this request. + + UserCcbFlags - Flags to OR into the Ccb flags. + + DesiredAccess - Desired access for this open. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if + the oplock package takes the Irp or SHARING_VIOLATION if there is a + sharing check conflict. + +--*/ + +{ + NTSTATUS Status; + NTSTATUS OplockStatus = STATUS_SUCCESS; + ULONG Information = FILE_OPENED; + + BOOLEAN LockVolume = FALSE; + + PFCB Fcb = *CurrentFcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Expand maximum allowed to something sensible for share access checking + // + + if (MAXIMUM_ALLOWED == DesiredAccess) { + + DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ? + (FILE_WRITE_ATTRIBUTES | + FILE_WRITE_DATA | + FILE_WRITE_EA | + FILE_ADD_FILE | + FILE_ADD_SUBDIRECTORY | + FILE_APPEND_DATA) : 0) | + FILE_DELETE_CHILD | + DELETE | + WRITE_DAC ); + } + + // + // If this a volume open and the user wants to lock the volume then + // purge and lock the volume. + // + + if ((TypeOfOpen <= UserVolumeOpen) && + !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) { + + // + // If there are open handles then fail this immediately. + // + + if (Vcb->VcbCleanup != 0) { + + return STATUS_SHARING_VIOLATION; + } + + // + // If we can't wait then force this to be posted. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + LockVolume = TRUE; + + // + // Purge the volume and make sure all of the user references + // are gone. + // + + Status = CdPurgeVolume( IrpContext, Vcb, FALSE ); + + if (Status != STATUS_SUCCESS) { + + return Status; + } + + // + // Now force all of the delayed close operations to go away. + // + + CdFspClose( Vcb ); + + if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) { + + return STATUS_SHARING_VIOLATION; + } + } + + // + // If the Fcb already existed then we need to check the oplocks and + // the share access. + // + + if (Fcb->FcbCleanup != 0) { + + // + // If this is a user file open then check whether there are any + // batch oplock. + // + + if (TypeOfOpen == UserFileOpen) { + + // + // Store the address of the Fcb for a possible teardown into + // the IrpContext. We will release this in the call to + // prepost the Irp. + // + + IrpContext->TeardownFcb = CurrentFcb; + + if (FsRtlCurrentBatchOplock( &Fcb->Oplock )) { + + // + // We remember if a batch oplock break is underway for the + // case where the sharing check fails. + // + + Information = FILE_OPBATCH_BREAK_UNDERWAY; + + OplockStatus = FsRtlCheckOplock( &Fcb->Oplock, + IrpContext->Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + if (OplockStatus == STATUS_PENDING) { + + return STATUS_PENDING; + } + } + + // + // Check the share access before breaking any exclusive oplocks. + // + + Status = IoCheckShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // Now check that we can continue based on the oplock state of the + // file. + // + + OplockStatus = FsRtlCheckOplock( &Fcb->Oplock, + IrpContext->Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + if (OplockStatus == STATUS_PENDING) { + + return STATUS_PENDING; + } + + IrpContext->TeardownFcb = NULL; + + // + // Otherwise just do the sharing check. + // + + } else { + + Status = IoCheckShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + } + } + + // + // Create the Ccb now. + // + + Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags ); + + // + // Update the share access. + // + + if (Fcb->FcbCleanup == 0) { + + IoSetShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess ); + + } else { + + IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess ); + } + + // + // Set the file object type. + // + + CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb ); + + // + // Set the appropriate cache flags for a user file object. + // + + if (TypeOfOpen == UserFileOpen) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) { + + SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); + + } else { + + SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED ); + } + } + else if (TypeOfOpen == UserVolumeOpen) { + + SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); + } + + // + // Update the open and cleanup counts. Check the fast io state here. + // + + CdLockVcb( IrpContext, Vcb ); + + CdIncrementCleanupCounts( IrpContext, Fcb ); + CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 ); + + if (LockVolume) { + + Vcb->VolumeLockFileObject = IrpSp->FileObject; + SetFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + } + + CdUnlockVcb( IrpContext, Vcb ); + + CdLockFcb( IrpContext, Fcb ); + + if (TypeOfOpen == UserFileOpen) { + + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + + } else { + + Fcb->IsFastIoPossible = FastIoIsNotPossible; + } + + CdUnlockFcb( IrpContext, Fcb ); + + // + // Show that we opened the file. + // + + IrpContext->Irp->IoStatus.Information = Information; + + // + // Point to the section object pointer in the non-paged Fcb. + // + + IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; + return OplockStatus; +} + + + + + diff --git a/reactos/drivers/filesystems/cdfs_new/devctrl.c b/reactos/drivers/filesystems/cdfs_new/devctrl.c new file mode 100755 index 00000000000..bfc9e450123 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/devctrl.c @@ -0,0 +1,196 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevCtrl.c + +Abstract: + + This module implements the File System Device Control routines for Cdfs + called by the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DEVCTRL) + +// +// Local support routines +// + +NTSTATUS +CdDevCtrlCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonDevControl) +#endif + + +NTSTATUS +CdCommonDevControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ + +{ + NTSTATUS Status; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PIO_STACK_LOCATION IrpSp; + PIO_STACK_LOCATION NextIrpSp; + + PVOID TargetBuffer = NULL; + + PAGED_CODE(); + + // + // Extract and decode the file object. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TypeOfOpen = CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ); + + // + // The only type of opens we accept are user volume opens. + // + + if (TypeOfOpen != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) { + + // + // Verify the Vcb in this case to detect if the volume has changed. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Handle the case of the disk type ourselves. + // + + } else if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_DISK_TYPE) { + + // + // Verify the Vcb in this case to detect if the volume has changed. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Check the size of the output buffer. + // + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof( CDROM_DISK_DATA )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy the data from the Vcb. + // + + ((PCDROM_DISK_DATA) Irp->AssociatedIrp.SystemBuffer)->DiskData = Fcb->Vcb->DiskFlags; + + Irp->IoStatus.Information = sizeof( CDROM_DISK_DATA ); + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get the next stack location, and copy over the stack parameter + // information. + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + *NextIrpSp = *IrpSp; + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + CdDevCtrlCompletionRoutine, + NULL, + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + Status = IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + + // + // Cleanup our Irp Context. The driver has completed the Irp. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdDevCtrlCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + // + // Add the hack-o-ramma to fix formats. + // + + if (Irp->PendingReturned) { + + IoMarkIrpPending( Irp ); + } + + return STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/deviosup.c b/reactos/drivers/filesystems/cdfs_new/deviosup.c new file mode 100755 index 00000000000..8f505c80b6b --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/deviosup.c @@ -0,0 +1,3603 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevIoSup.c + +Abstract: + + This module implements the low lever disk read/write support for Cdfs. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DEVIOSUP) + +// +// Local structure definitions +// + +// +// An array of these structures is passed to CdMultipleAsync describing +// a set of runs to execute in parallel. +// + +typedef struct _IO_RUN { + + // + // Disk offset to read from and number of bytes to read. These + // must be a multiple of 2048 and the disk offset is also a + // multiple of 2048. + // + + LONGLONG DiskOffset; + ULONG DiskByteCount; + + // + // Current position in user buffer. This is the final destination for + // this portion of the Io transfer. + // + + PVOID UserBuffer; + + // + // Buffer to perform the transfer to. If this is the same as the + // user buffer above then we are using the user's buffer. Otherwise + // we either allocated a temporary buffer or are using a different portion + // of the user's buffer. + // + // TransferBuffer - Read full sectors into this location. This can + // be a pointer into the user's buffer at the exact location the + // data should go. It can also be an earlier point in the user's + // buffer if the complete I/O doesn't start on a sector boundary. + // It may also be a pointer into an allocated buffer. + // + // TransferByteCount - Count of bytes to transfer to user's buffer. A + // value of zero indicates that we did do the transfer into the + // user's buffer directly. + // + // TransferBufferOffset - Offset in this buffer to begin the transfer + // to the user's buffer. + // + + PVOID TransferBuffer; + ULONG TransferByteCount; + ULONG TransferBufferOffset; + + // + // This is the Mdl describing the locked pages in memory. It may + // be allocated to describe the allocated buffer. Or it may be + // the Mdl in the originating Irp. The MdlOffset is the offset of + // the current buffer from the beginning of the buffer described by + // the Mdl below. If the TransferMdl is not the same as the Mdl + // in the user's Irp then we know we have allocated it. + // + + PMDL TransferMdl; + PVOID TransferVirtualAddress; + + // + // Associated Irp used to perform the Io. + // + + PIRP SavedIrp; + +} IO_RUN; +typedef IO_RUN *PIO_RUN; + +#define MAX_PARALLEL_IOS 5 + +// +// Local support routines +// + +BOOLEAN +CdPrepareBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PVOID UserBuffer, + IN ULONG UserBufferOffset, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PIO_RUN IoRuns, + IN PULONG RunCount, + IN PULONG ThisByteCount + ); + +VOID +CdPrepareXABuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PVOID UserBuffer, + IN ULONG UserBufferOffset, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PIO_RUN IoRuns, + IN PULONG RunCount, + IN PULONG ThisByteCount + ); + +BOOLEAN +CdFinishBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIO_RUN IoRuns, + IN ULONG RunCount, + IN BOOLEAN FinalCleanup, + IN BOOLEAN SaveXABuffer + ); + +VOID +CdMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN ULONG RunCount, + IN PIO_RUN IoRuns + ); + +VOID +CdMultipleXAAsync ( + IN PIRP_CONTEXT IrpContext, + IN ULONG RunCount, + IN PIO_RUN IoRuns, + IN PRAW_READ_INFO RawReads, + IN TRACK_MODE_TYPE TrackMode + ); + +VOID +CdSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN LONGLONG ByteOffset, + IN ULONG ByteCount + ); + +VOID +CdWaitSync ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +CdMultiSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdMultiAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdSingleSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdSingleAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +CdReadAudioSystemFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PVOID SystemBuffer + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCreateUserMdl) +#pragma alloc_text(PAGE, CdMultipleAsync) +#pragma alloc_text(PAGE, CdMultipleXAAsync) +#pragma alloc_text(PAGE, CdNonCachedRead) +#pragma alloc_text(PAGE, CdNonCachedXARead) +#pragma alloc_text(PAGE, CdFinishBuffers) +#pragma alloc_text(PAGE, CdPerformDevIoCtrl) +#pragma alloc_text(PAGE, CdPrepareBuffers) +#pragma alloc_text(PAGE, CdReadAudioSystemFile) +#pragma alloc_text(PAGE, CdReadSectors) +#pragma alloc_text(PAGE, CdSingleAsync) +#pragma alloc_text(PAGE, CdWaitSync) +#endif + + +__inline +TRACK_MODE_TYPE +CdFileTrackMode ( + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine converts FCB XA file type flags to the track mode + used by the device drivers. + +Arguments: + + Fcb - Fcb representing the file to read. + +Return Value: + + TrackMode of the file represented by the Fcb. + +--*/ +{ + ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE | + FCB_STATE_MODE2_FILE | + FCB_STATE_DA_FILE )); + + if (FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE )) { + + return XAForm2; + + } else if (FlagOn( Fcb->FcbState, FCB_STATE_DA_FILE )) { + + return CDDA; + + } + + // + // FCB_STATE_MODE2_FILE + // + + return YellowMode2; +} + + +NTSTATUS +CdNonCachedRead ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached reads to 'cooked' sectors (2048 bytes + per sector). This is done by performing the following in a loop. + + Fill in the IoRuns array for the next block of Io. + Send the Io to the device. + Perform any cleanup on the Io runs array. + + We will not do async Io to any request that generates non-aligned Io. + Also we will not perform async Io if it will exceed the size of our + IoRuns array. These should be the unusual cases but we will raise + or return CANT_WAIT in this routine if we detect this case. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + IO_RUN IoRuns[MAX_PARALLEL_IOS]; + ULONG RunCount = 0; + ULONG CleanupRunCount = 0; + + PVOID UserBuffer; + ULONG UserBufferOffset = 0; + LONGLONG CurrentOffset = StartingOffset; + ULONG RemainingByteCount = ByteCount; + ULONG ThisByteCount; + + BOOLEAN Unaligned; + BOOLEAN FlushIoBuffers = FALSE; + BOOLEAN FirstPass = TRUE; + + PAGED_CODE(); + + // + // We want to make sure the user's buffer is locked in all cases. + // + + if (IrpContext->Irp->MdlAddress == NULL) { + + CdCreateUserMdl( IrpContext, ByteCount, TRUE ); + } + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Special case the root directory and path table for a music volume. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_AUDIO_DISK ) && + ((SafeNodeType( Fcb ) == CDFS_NTC_FCB_INDEX) || + (SafeNodeType( Fcb ) == CDFS_NTC_FCB_PATH_TABLE))) { + + CdReadAudioSystemFile( IrpContext, + Fcb, + StartingOffset, + ByteCount, + UserBuffer ); + + return STATUS_SUCCESS; + } + + // + // Use a try-finally to perform the final cleanup. + // + + try { + + // + // Loop while there are more bytes to transfer. + // + + do { + + // + // Call prepare buffers to set up the next entries + // in the IoRuns array. Remember if there are any + // unaligned entries. This routine will raise CANT_WAIT + // if there are unaligned entries for an async request. + // + + RtlZeroMemory( IoRuns, sizeof( IoRuns )); + + Unaligned = CdPrepareBuffers( IrpContext, + IrpContext->Irp, + Fcb, + UserBuffer, + UserBufferOffset, + CurrentOffset, + RemainingByteCount, + IoRuns, + &CleanupRunCount, + &ThisByteCount ); + + + RunCount = CleanupRunCount; + + // + // If this is an async request and there aren't enough entries + // in the Io array then post the request. + // + + if ((ThisByteCount < RemainingByteCount) && + !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // If the entire Io is contained in a single run then + // we can pass the Io down to the driver. Send the driver down + // and wait on the result if this is synchronous. + // + + if ((RunCount == 1) && !Unaligned && FirstPass) { + + CdSingleAsync( IrpContext, + IoRuns[0].DiskOffset, + IoRuns[0].DiskByteCount ); + + // + // No cleanup needed for the IoRuns array here. + // + + CleanupRunCount = 0; + + // + // Wait if we are synchronous, otherwise return + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Our completion routine will free the Io context but + // we do want to return STATUS_PENDING. + // + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + Status = STATUS_PENDING; + } + + try_return( NOTHING ); + } + + // + // Otherwise we will perform multiple Io to read in the data. + // + + CdMultipleAsync( IrpContext, RunCount, IoRuns ); + + // + // If this is a synchronous request then perform any necessary + // post-processing. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + // + // Wait for the request to complete. + // + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Exit this loop if there is an error. + // + + if (!NT_SUCCESS( Status )) { + + try_return( NOTHING ); + } + + // + // Perform post read operations on the IoRuns if + // necessary. + // + + if (Unaligned && + CdFinishBuffers( IrpContext, IoRuns, RunCount, FALSE, FALSE )) { + + FlushIoBuffers = TRUE; + } + + CleanupRunCount = 0; + + // + // Exit this loop if there are no more bytes to transfer + // or we have any error. + // + + RemainingByteCount -= ThisByteCount; + CurrentOffset += ThisByteCount; + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + + // + // Otherwise this is an asynchronous request. Always return + // STATUS_PENDING. + // + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + CleanupRunCount = 0; + try_return( Status = STATUS_PENDING ); + break; + } + + FirstPass = FALSE; + } while (RemainingByteCount != 0); + + // + // Flush the hardware cache if we performed any copy operations. + // + + if (FlushIoBuffers) { + + KeFlushIoBuffers( IrpContext->Irp->MdlAddress, TRUE, FALSE ); + } + + try_exit: NOTHING; + } finally { + + // + // Perform final cleanup on the IoRuns if necessary. + // + + if (CleanupRunCount != 0) { + + CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE ); + } + } + + return Status; +} + + +NTSTATUS +CdNonCachedXARead ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached reads for 'raw' sectors (2352 bytes + per sector). We also prepend a hard-coded RIFF header of 44 bytes to the file. + All of this is already reflected in the file size. + + We start by checking whether to prepend any portion of the RIFF header. Then we check + if the last raw sector read was from the beginning portion of this file, deallocating + that buffer if necessary. Finally we do the following in a loop. + + Fill the IoRuns array for the next block of Io. + Send the Io to the device driver. + Perform any cleanup necessary on the IoRuns array. + + We will not do any async request in this path. The request would have been + posted to a worker thread before getting to this point. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + RIFF_HEADER LocalRiffHeader; + PRIFF_HEADER RiffHeader; + + RAW_READ_INFO RawReads[MAX_PARALLEL_IOS]; + IO_RUN IoRuns[MAX_PARALLEL_IOS]; + ULONG RunCount = 0; + ULONG CleanupRunCount = 0; + + PVOID UserBuffer; + ULONG UserBufferOffset = 0; + LONGLONG CurrentOffset = StartingOffset; + ULONG RemainingByteCount = ByteCount; + ULONG ThisByteCount; + + BOOLEAN TryingYellowbookMode2 = FALSE; + + TRACK_MODE_TYPE TrackMode; + + PAGED_CODE(); + + // + // We want to make sure the user's buffer is locked in all cases. + // + + if (IrpContext->Irp->MdlAddress == NULL) { + + CdCreateUserMdl( IrpContext, ByteCount, TRUE ); + } + + // + // The byte count was rounded up to a logical sector boundary. It has + // nothing to do with the raw sectors on disk. Limit the remaining + // byte count to file size. + // + + if (CurrentOffset + RemainingByteCount > Fcb->FileSize.QuadPart) { + + RemainingByteCount = (ULONG) (Fcb->FileSize.QuadPart - CurrentOffset); + } + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Use a try-finally to perform the final cleanup. + // + + try { + + // + // If the initial offset lies within the RIFF header then copy the + // necessary bytes to the user's buffer. + // + + if (CurrentOffset < sizeof( RIFF_HEADER )) { + + // + // Copy the appropriate RIFF header. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_DA_FILE )) { + + // + // Create the pseudo entries for a music disk. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + PAUDIO_PLAY_HEADER AudioPlayHeader; + PTRACK_DATA TrackData; + ULONG SectorCount; + + AudioPlayHeader = (PAUDIO_PLAY_HEADER) &LocalRiffHeader; + TrackData = &Fcb->Vcb->CdromToc->TrackData[Fcb->XAFileNumber]; + + // + // Copy the data header into our local buffer. + // + + RtlCopyMemory( AudioPlayHeader, + CdAudioPlayHeader, + sizeof( AUDIO_PLAY_HEADER )); + + // + // Copy the serial number into the Id field. Also + // the track number in the TOC. + // + + AudioPlayHeader->DiskID = Fcb->Vcb->Vpb->SerialNumber; + AudioPlayHeader->TrackNumber = TrackData->TrackNumber; + + // + // TOC contains MSF (Minute/Second/Frame) addresses. This is very + // arcane, and we wind up having to bias around by the size of the + // leadins and other such silliness to find real live sector addrs. + // + // One frame == One sector. + // One second == 75 frames (winds up being a 44.1khz sample) + // + + // + // Fill in the address and length fields. + // + + AudioPlayHeader->TrackAddress[2] = TrackData->Address[1]; + AudioPlayHeader->TrackAddress[1] = TrackData->Address[2]; + AudioPlayHeader->TrackAddress[0] = TrackData->Address[3]; + + AudioPlayHeader->StartingSector = TrackData->Address[3]; + AudioPlayHeader->StartingSector += (TrackData->Address[2] * 75); + AudioPlayHeader->StartingSector += (TrackData->Address[1] * 60 * 75); + + // + // Subtract 2 seconds for the block number. + // + + AudioPlayHeader->StartingSector -= 150; + + // + // Go to the next track and find the starting point. + // + + TrackData = &Fcb->Vcb->CdromToc->TrackData[Fcb->XAFileNumber + 1]; + + AudioPlayHeader->SectorCount = TrackData->Address[3]; + AudioPlayHeader->SectorCount += (TrackData->Address[2] * 75); + AudioPlayHeader->SectorCount += (TrackData->Address[1] * 60 * 75); + + // + // Bias the sector count by 2 seconds. + // Check that the offset is at least two seconds. + // + + if (AudioPlayHeader->SectorCount < 150) { + + AudioPlayHeader->SectorCount = 0; + + } else { + + AudioPlayHeader->SectorCount -= 150; + } + + // + // Now compute the difference. If there is an error then use + // a length of zero. + // + + if (AudioPlayHeader->SectorCount < AudioPlayHeader->StartingSector) { + + AudioPlayHeader->SectorCount = 0; + + } else { + + AudioPlayHeader->SectorCount -= AudioPlayHeader->StartingSector; + } + + // + // Use the sector count to determine the MSF length. + // + + SectorCount = AudioPlayHeader->SectorCount; + + AudioPlayHeader->TrackLength[0] = (UCHAR) (SectorCount % 75); + SectorCount /= 75; + + AudioPlayHeader->TrackLength[1] = (UCHAR) (SectorCount % 60); + SectorCount /= 60; + + AudioPlayHeader->TrackLength[2] = (UCHAR) (SectorCount % 60); + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + + RtlCopyMemory( UserBuffer, + Add2Ptr( AudioPlayHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + + // + // CD-XA CDDA + // + + } else { + + // + // The WAVE header format is actually much closer to an audio play + // header in format but we only need to modify the filesize fields. + // + + RiffHeader = &LocalRiffHeader; + + // + // Copy the data header into our local buffer and add the file size to it. + // + + RtlCopyMemory( RiffHeader, + CdXAAudioPhileHeader, + sizeof( RIFF_HEADER )); + + RiffHeader->ChunkSize += Fcb->FileSize.LowPart; + RiffHeader->RawSectors += Fcb->FileSize.LowPart; + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + RtlCopyMemory( UserBuffer, + Add2Ptr( RiffHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + } + + // + // CD-XA non-audio + // + + } else { + + ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_MODE2_FILE | FCB_STATE_MODE2FORM2_FILE )); + + RiffHeader = &LocalRiffHeader; + + // + // Copy the data header into our local buffer and add the file size to it. + // + + RtlCopyMemory( RiffHeader, + CdXAFileHeader, + sizeof( RIFF_HEADER )); + + RiffHeader->ChunkSize += Fcb->FileSize.LowPart; + RiffHeader->RawSectors += Fcb->FileSize.LowPart; + + RiffHeader->Attributes = (USHORT) Fcb->XAAttributes; + RiffHeader->FileNumber = (UCHAR) Fcb->XAFileNumber; + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + RtlCopyMemory( UserBuffer, + Add2Ptr( RiffHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + } + + // + // Adjust the starting offset and byte count to reflect that + // we copied over the RIFF bytes. + // + + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + CurrentOffset += ThisByteCount; + RemainingByteCount -= ThisByteCount; + } + + // + // Set up the appropriate trackmode + // + + TrackMode = CdFileTrackMode(Fcb); + + // + // Loop while there are more bytes to transfer. + // + + while (RemainingByteCount != 0) { + + // + // Call prepare buffers to set up the next entries + // in the IoRuns array. Remember if there are any + // unaligned entries. If we're just retrying the previous + // runs with a different track mode, then don't do anything here. + // + + if (!TryingYellowbookMode2) { + + RtlZeroMemory( IoRuns, sizeof( IoRuns )); + RtlZeroMemory( RawReads, sizeof( RawReads )); + + CdPrepareXABuffers( IrpContext, + IrpContext->Irp, + Fcb, + UserBuffer, + UserBufferOffset, + CurrentOffset, + RemainingByteCount, + IoRuns, + &CleanupRunCount, + &ThisByteCount ); + } + + // + // Perform multiple Io to read in the data. Note that + // there may be no Io to do if we were able to use an + // existing buffer from the Vcb. + // + + if (CleanupRunCount != 0) { + + RunCount = CleanupRunCount; + + CdMultipleXAAsync( IrpContext, + RunCount, + IoRuns, + RawReads, + TrackMode ); + // + // Wait for the request to complete. + // + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Exit this loop if there is an error. + // + + if (!NT_SUCCESS( Status )) { + + if (!TryingYellowbookMode2 && + FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE )) { + + // + // There are wacky cases where someone has mastered as CD-XA + // but the sectors they claim are Mode2Form2 are really, according + // to ATAPI devices, Yellowbook Mode2. We will try once more + // with these. Kodak PHOTO-CD has been observed to do this. + // + + TryingYellowbookMode2 = TRUE; + TrackMode = YellowMode2; + + // + // Clear our 'cumulative' error status value + // + + IrpContext->IoContext->Status = STATUS_SUCCESS; + + continue; + } + + try_return( NOTHING ); + } + + CleanupRunCount = 0; + + if (TryingYellowbookMode2) { + + // + // We succesfully got data when we tried switching the trackmode, + // so change the state of the FCB to remember that. + // + + SetFlag( Fcb->FcbState, FCB_STATE_MODE2_FILE ); + ClearFlag( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE ); + + TryingYellowbookMode2 = FALSE; + } + + // + // Perform post read operations on the IoRuns if + // necessary. + // + + CdFinishBuffers( IrpContext, IoRuns, RunCount, FALSE, TRUE ); + } + + // + // Adjust our loop variants. + // + + RemainingByteCount -= ThisByteCount; + CurrentOffset += ThisByteCount; + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + } + + // + // Always flush the hardware cache. + // + + KeFlushIoBuffers( IrpContext->Irp->MdlAddress, TRUE, FALSE ); + + try_exit: NOTHING; + } finally { + + // + // Perform final cleanup on the IoRuns if necessary. + // + + if (CleanupRunCount != 0) { + + CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE ); + } + } + + return Status; +} + + +BOOLEAN +CdReadSectors ( + IN PIRP_CONTEXT IrpContext, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN BOOLEAN ReturnError, + IN OUT PVOID Buffer, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called to transfer sectors from the disk to a + specified buffer. It is used for mount and volume verify operations. + + This routine is synchronous, it will not return until the operation + is complete or until the operation fails. + + The routine allocates an IRP and then passes this IRP to a lower + level driver. Errors may occur in the allocation of this IRP or + in the operation of the lower driver. + +Arguments: + + StartingOffset - Logical offset on the disk to start the read. This + must be on a sector boundary, no check is made here. + + ByteCount - Number of bytes to read. This is an integral number of + 2K sectors, no check is made here to confirm this. + + ReturnError - Indicates whether we should return TRUE or FALSE + to indicate an error or raise an error condition. This only applies + to the result of the IO. Any other error may cause a raise. + + Buffer - Buffer to transfer the disk data into. + + TargetDeviceObject - The device object for the volume to be read. + +Return Value: + + BOOLEAN - Depending on 'RaiseOnError' flag above. TRUE if operation + succeeded, FALSE otherwise. + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + PIRP Irp; + + PAGED_CODE(); + + // + // Initialize the event. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Attempt to allocate the IRP. If unsuccessful, raise + // STATUS_INSUFFICIENT_RESOURCES. + // + + Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + TargetDeviceObject, + Buffer, + ByteCount, + (PLARGE_INTEGER) &StartingOffset, + &Event, + &IrpContext->Irp->IoStatus ); + + if (Irp == NULL) { + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Ignore the change line (verify) for mount and verify requests + // + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Send the request down to the driver. If an error occurs return + // it to the caller. + // + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + // + // If the status was STATUS_PENDING then wait on the event. + // + + if (Status == STATUS_PENDING) { + + Status = KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + // + // On a successful wait pull the status out of the IoStatus block. + // + + if (NT_SUCCESS( Status )) { + + Status = IrpContext->Irp->IoStatus.Status; + } + } + + // + // Check whether we should raise in the error case. + // + + if (!NT_SUCCESS( Status )) { + + if (!ReturnError) { + + CdNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // We don't raise, but return FALSE to indicate an error. + // + + return FALSE; + + // + // The operation completed successfully. + // + + } else { + + return TRUE; + } +} + + +NTSTATUS +CdCreateUserMdl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG BufferLength, + IN BOOLEAN RaiseOnError + ) + +/*++ + +Routine Description: + + This routine locks the specified buffer for read access (we only write into + the buffer). The file system requires this routine since it does not + ask the I/O system to lock its buffers for direct I/O. This routine + may only be called from the Fsd while still in the user context. + + This routine is only called if there is not already an Mdl. + +Arguments: + + BufferLength - Length of user buffer. + + RaiseOnError - Indicates if our caller wants this routine to raise on + an error condition. + +Return Value: + + NTSTATUS - Status from this routine. Error status only returned if + RaiseOnError is FALSE. + +--*/ + +{ + NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; + PMDL Mdl; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( IrpContext->Irp ); + ASSERT( IrpContext->Irp->MdlAddress == NULL ); + + // + // Allocate the Mdl, and Raise if we fail. + // + + Mdl = IoAllocateMdl( IrpContext->Irp->UserBuffer, + BufferLength, + FALSE, + FALSE, + IrpContext->Irp ); + + if (Mdl != NULL) { + + // + // Now probe the buffer described by the Irp. If we get an exception, + // deallocate the Mdl and return the appropriate "expected" status. + // + + try { + + MmProbeAndLockPages( Mdl, IrpContext->Irp->RequestorMode, IoWriteAccess ); + + Status = STATUS_SUCCESS; + + } except(EXCEPTION_EXECUTE_HANDLER) { + + Status = GetExceptionCode(); + + IoFreeMdl( Mdl ); + IrpContext->Irp->MdlAddress = NULL; + + if (!FsRtlIsNtstatusExpected( Status )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + } + } + + // + // Check if we are to raise or return + // + + if (Status != STATUS_SUCCESS) { + + if (RaiseOnError) { + + CdRaiseStatus( IrpContext, Status ); + } + } + + // + // Return the status code. + // + + return Status; +} + + +NTSTATUS +CdPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to perform DevIoCtrl functions internally within + the filesystem. We take the status from the driver and return it to our + caller. + +Arguments: + + IoControlCode - Code to send to driver. + + Device - This is the device to send the request to. + + OutPutBuffer - Pointer to output buffer. + + OutputBufferLength - Length of output buffer above. + + InternalDeviceIoControl - Indicates if this is an internal or external + Io control code. + + OverrideVerify - Indicates if we should tell the driver not to return + STATUS_VERIFY_REQUIRED for mount and verify. + + Iosb - If specified, we return the results of the operation here. + +Return Value: + + NTSTATUS - Status returned by next lower driver. + +--*/ + +{ + NTSTATUS Status; + PIRP Irp; + KEVENT Event; + IO_STATUS_BLOCK LocalIosb; + PIO_STATUS_BLOCK IosbToUse = &LocalIosb; + + PAGED_CODE(); + + // + // Check if the user gave us an Iosb. + // + + if (ARGUMENT_PRESENT( Iosb )) { + + IosbToUse = Iosb; + } + + IosbToUse->Status = 0; + IosbToUse->Information = 0; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + Irp = IoBuildDeviceIoControlRequest( IoControlCode, + Device, + NULL, + 0, + OutputBuffer, + OutputBufferLength, + InternalDeviceIoControl, + &Event, + IosbToUse ); + + if (Irp == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (OverrideVerify) { + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + Status = IoCallDriver( Device, Irp ); + + // + // We check for device not ready by first checking Status + // and then if status pending was returned, the Iosb status + // value. + // + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = IosbToUse->Status; + } + + ASSERT( !(OverrideVerify && (STATUS_VERIFY_REQUIRED == Status))); + + return Status; +} + + +// +// Local support routine +// + +BOOLEAN +CdPrepareBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PVOID UserBuffer, + IN ULONG UserBufferOffset, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PIO_RUN IoRuns, + IN PULONG RunCount, + IN PULONG ThisByteCount + ) + +/*++ + +Routine Description: + + This routine is the worker routine which looks up each run of an IO + request and stores an entry for it in the IoRuns array. If the run + begins on an unaligned disk boundary then we will allocate a buffer + and Mdl for the unaligned portion and put it in the IoRuns entry. + + This routine will raise CANT_WAIT if an unaligned transfer is encountered + and this request can't wait. + +Arguments: + + Irp - Originating Irp for this request. + + Fcb - This is the Fcb for this data stream. It may be a file, directory, + path table or the volume file. + + UserBuffer - Current position in the user's buffer. + + UserBufferOffset - Offset from the start of the original user buffer. + + StartingOffset - Offset in the stream to begin the read. + + ByteCount - Number of bytes to read. We will fill the IoRuns array up + to this point. We will stop early if we exceed the maximum number + of parallel Ios we support. + + IoRuns - Pointer to the IoRuns array. The entire array is zeroes when + this routine is called. + + RunCount - Number of entries in the IoRuns array filled here. + + ThisByteCount - Number of bytes described by the IoRun entries. Will + not exceed the ByteCount passed in. + +Return Value: + + BOOLEAN - TRUE if one of the entries in an unaligned buffer (provided + this is synchronous). FALSE otherwise. + +--*/ + +{ + BOOLEAN FoundUnaligned = FALSE; + PIO_RUN ThisIoRun = IoRuns; + + // + // Following indicate where we are in the current transfer. Current + // position in the file and number of bytes yet to transfer from + // this position. + // + + ULONG RemainingByteCount = ByteCount; + LONGLONG CurrentFileOffset = StartingOffset; + + // + // Following indicate the state of the user's buffer. We have + // the destination of the next transfer and its offset in the + // buffer. We also have the next available position in the buffer + // available for a scratch buffer. We will align this up to a sector + // boundary. + // + + PVOID CurrentUserBuffer = UserBuffer; + ULONG CurrentUserBufferOffset = UserBufferOffset; + + PVOID ScratchUserBuffer = UserBuffer; + ULONG ScratchUserBufferOffset = UserBufferOffset; + + // + // The following is the next contiguous bytes on the disk to + // transfer. Read from the allocation package. + // + + LONGLONG DiskOffset; + ULONG CurrentByteCount; + + PAGED_CODE(); + + // + // Initialize the RunCount and ByteCount. + // + + *RunCount = 0; + *ThisByteCount = 0; + + // + // Loop while there are more bytes to process or there are + // available entries in the IoRun array. + // + + while (TRUE) { + + *RunCount += 1; + + // + // Initialize the current position in the IoRuns array. + // Find the user's buffer for this portion of the transfer. + // + + ThisIoRun->UserBuffer = CurrentUserBuffer; + + // + // Find the allocation information for the current offset in the + // stream. + // + + CdLookupAllocation( IrpContext, + Fcb, + CurrentFileOffset, + &DiskOffset, + &CurrentByteCount ); + + // + // Limit ourselves to the data requested. + // + + if (CurrentByteCount > RemainingByteCount) { + + CurrentByteCount = RemainingByteCount; + } + + // + // Handle the case where this is an unaligned transfer. The + // following must all be true for this to be an aligned transfer. + // + // Disk offset on a 2048 byte boundary (Start of transfer) + // + // Byte count is a multiple of 2048 (Length of transfer) + // + // Current buffer offset is also on a 2048 byte boundary. + // + // If the ByteCount is at least one sector then do the + // unaligned transfer only for the tail. We can use the + // user's buffer for the aligned portion. + // + + if (FlagOn( (ULONG) DiskOffset, SECTOR_MASK ) || + FlagOn( CurrentUserBufferOffset, SECTOR_MASK ) || + (FlagOn( (ULONG) CurrentByteCount, SECTOR_MASK ) && + (CurrentByteCount < SECTOR_SIZE))) { + + // + // If we can't wait then raise. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Remember the offset and the number of bytes out of + // the transfer buffer to copy into the user's buffer. + // We will truncate the current read to end on a sector + // boundary. + // + + ThisIoRun->TransferBufferOffset = SectorOffset( DiskOffset ); + + // + // Make sure this transfer ends on a sector boundary. + // + + ThisIoRun->DiskOffset = LlSectorTruncate( DiskOffset ); + + // + // Check if we can use a free portion of the user's buffer. + // If we can copy the bytes to an earlier portion of the + // buffer then read into that location and slide the bytes + // up. + // + // We can use the user's buffer if: + // + // The temporary location in the buffer is before the + // final destination. + // + // There is at least one sector of data to read. + // + + if ((ScratchUserBufferOffset + ThisIoRun->TransferBufferOffset < CurrentUserBufferOffset) && + (ThisIoRun->TransferBufferOffset + CurrentByteCount >= SECTOR_SIZE)) { + + ThisIoRun->DiskByteCount = SectorTruncate( ThisIoRun->TransferBufferOffset + CurrentByteCount ); + CurrentByteCount = ThisIoRun->DiskByteCount - ThisIoRun->TransferBufferOffset; + ThisIoRun->TransferByteCount = CurrentByteCount; + + // + // Point to the user's buffer and Mdl for this transfer. + // + + ThisIoRun->TransferBuffer = ScratchUserBuffer; + ThisIoRun->TransferMdl = Irp->MdlAddress; + ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer, + ScratchUserBufferOffset, + PVOID ); + + ScratchUserBuffer = Add2Ptr( ScratchUserBuffer, + ThisIoRun->DiskByteCount, + PVOID ); + + ScratchUserBufferOffset += ThisIoRun->DiskByteCount; + + // + // Otherwise we need to allocate an auxilary buffer for the next sector. + // + + } else { + + // + // Read up to a page containing the partial data + // + + ThisIoRun->DiskByteCount = SectorAlign( ThisIoRun->TransferBufferOffset + CurrentByteCount ); + + if (ThisIoRun->DiskByteCount > PAGE_SIZE) { + + ThisIoRun->DiskByteCount = PAGE_SIZE; + } + + if (ThisIoRun->TransferBufferOffset + CurrentByteCount > ThisIoRun->DiskByteCount) { + + CurrentByteCount = ThisIoRun->DiskByteCount - ThisIoRun->TransferBufferOffset; + } + + ThisIoRun->TransferByteCount = CurrentByteCount; + + // + // Allocate a buffer for the non-aligned transfer. + // + + ThisIoRun->TransferBuffer = FsRtlAllocatePoolWithTag( CdNonPagedPool, PAGE_SIZE, TAG_IO_BUFFER ); + + // + // Allocate and build the Mdl to describe this buffer. + // + + ThisIoRun->TransferMdl = IoAllocateMdl( ThisIoRun->TransferBuffer, + PAGE_SIZE, + FALSE, + FALSE, + NULL ); + + ThisIoRun->TransferVirtualAddress = ThisIoRun->TransferBuffer; + + if (ThisIoRun->TransferMdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + MmBuildMdlForNonPagedPool( ThisIoRun->TransferMdl ); + } + + // + // Remember we found an unaligned transfer. + // + + FoundUnaligned = TRUE; + + // + // Otherwise we use the buffer and Mdl from the original request. + // + + } else { + + // + // Truncate the read length to a sector-aligned value. We know + // the length must be at least one sector or we wouldn't be + // here now. + // + + CurrentByteCount = SectorTruncate( CurrentByteCount ); + + // + // Read these sectors from the disk. + // + + ThisIoRun->DiskOffset = DiskOffset; + ThisIoRun->DiskByteCount = CurrentByteCount; + + // + // Use the user's buffer and Mdl as our transfer buffer + // and Mdl. + // + + ThisIoRun->TransferBuffer = CurrentUserBuffer; + ThisIoRun->TransferMdl = Irp->MdlAddress; + ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer, + CurrentUserBufferOffset, + PVOID ); + + ScratchUserBuffer = Add2Ptr( CurrentUserBuffer, + CurrentByteCount, + PVOID ); + + ScratchUserBufferOffset += CurrentByteCount; + } + + // + // Update our position in the transfer and the RunCount and + // ByteCount for the user. + // + + RemainingByteCount -= CurrentByteCount; + + // + // Break out if no more positions in the IoRuns array or + // we have all of the bytes accounted for. + // + + *ThisByteCount += CurrentByteCount; + + if ((RemainingByteCount == 0) || (*RunCount == MAX_PARALLEL_IOS)) { + + break; + } + + // + // Update our pointers for the user's buffer. + // + + ThisIoRun += 1; + CurrentUserBuffer = Add2Ptr( CurrentUserBuffer, CurrentByteCount, PVOID ); + CurrentUserBufferOffset += CurrentByteCount; + CurrentFileOffset += CurrentByteCount; + } + + return FoundUnaligned; +} + + +// +// Local support routine +// + +VOID +CdPrepareXABuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PVOID UserBuffer, + IN ULONG UserBufferOffset, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PIO_RUN IoRuns, + IN PULONG RunCount, + IN PULONG ThisByteCount + ) + +/*++ + +Routine Description: + + This routine is the worker routine which looks up the individual runs + of an IO request and stores an entry for it in the IoRuns array. The + worker routine is for XA files where we need to convert the raw offset + in the file to logical cooked sectors. We store one raw sector in + the Vcb. If the current read is to that sector then we can simply copy + whatever bytes are needed from that sector. + +Arguments: + + Irp - Originating Irp for this request. + + Fcb - This is the Fcb for this data stream. It must be a data stream. + + UserBuffer - Current position in the user's buffer. + + UserBufferOffset - Offset of this buffer from the beginning of the user's + buffer for the original request. + + StartingOffset - Offset in the stream to begin the read. + + ByteCount - Number of bytes to read. We will fill the IoRuns array up + to this point. We will stop early if we exceed the maximum number + of parallel Ios we support. + + IoRuns - Pointer to the IoRuns array. The entire array is zeroes when + this routine is called. + + RunCount - Number of entries in the IoRuns array filled here. + + ThisByteCount - Number of bytes described by the IoRun entries. Will + not exceed the ByteCount passed in. + +Return Value: + + None + +--*/ + +{ + PIO_RUN ThisIoRun = IoRuns; + BOOLEAN PerformedCopy; + + // + // The following deal with where we are in the range of raw sectors. + // Note that we will bias the input file offset by the RIFF header + // to deal directly with the raw sectors. + // + + ULONG RawSectorOffset; + ULONG RemainingRawByteCount = ByteCount; + LONGLONG CurrentRawOffset = StartingOffset - sizeof( RIFF_HEADER ); + + // + // The following is the offset into the cooked sectors for the file. + // + + LONGLONG CurrentCookedOffset; + ULONG RemainingCookedByteCount; + + // + // Following indicate the state of the user's buffer. We have + // the destination of the next transfer and its offset in the + // buffer. We also have the next available position in the buffer + // available for a scratch buffer. + // + + PVOID CurrentUserBuffer = UserBuffer; + ULONG CurrentUserBufferOffset = UserBufferOffset; + + PVOID ScratchUserBuffer = UserBuffer; + ULONG ScratchUserBufferOffset = UserBufferOffset; + BOOLEAN RoundScratchBuffer = TRUE; + + // + // The following is the next contiguous bytes on the disk to + // transfer. These are represented by cooked byte offset and length. + // We also compute the number of raw bytes in the current transfer. + // + + LONGLONG DiskOffset; + ULONG CurrentCookedByteCount; + ULONG CurrentRawByteCount; + + PAGED_CODE(); + + // + // We need to maintain our position as we walk through the sectors on the disk. + // We keep separate values for the cooked offset as well as the raw offset. + // These are initialized on sector boundaries and we move through these + // the file sector-by-sector. + // + // Try to do 32-bit math. + // + + if (((PLARGE_INTEGER) &CurrentRawOffset)->HighPart == 0) { + + // + // Prefix/fast: Note that the following are safe since we only + // take this path for 32bit offsets. + // + + CurrentRawOffset = (LONGLONG) ((ULONG) CurrentRawOffset / RAW_SECTOR_SIZE); + + CurrentCookedOffset = (LONGLONG) ((ULONG) CurrentRawOffset << SECTOR_SHIFT ); + + CurrentRawOffset = (LONGLONG) ((ULONG) CurrentRawOffset * RAW_SECTOR_SIZE); + + // + // Otherwise we need to do 64-bit math (sigh). + // + + } else { + + CurrentRawOffset /= RAW_SECTOR_SIZE; + + CurrentCookedOffset = CurrentRawOffset << SECTOR_SHIFT; + + CurrentRawOffset *= RAW_SECTOR_SIZE; + } + + // + // Now compute the full number of sectors to be read. Count all of the raw + // sectors that need to be read and convert to cooked bytes. + // + + RawSectorOffset = (ULONG) ( StartingOffset - CurrentRawOffset) - sizeof( RIFF_HEADER ); + CurrentRawByteCount = (RawSectorOffset + RemainingRawByteCount + RAW_SECTOR_SIZE - 1) / RAW_SECTOR_SIZE; + + RemainingCookedByteCount = CurrentRawByteCount << SECTOR_SHIFT; + + // + // Initialize the RunCount and ByteCount. + // + + *RunCount = 0; + *ThisByteCount = 0; + + // + // Loop while there are more bytes to process or there are + // available entries in the IoRun array. + // + + while (TRUE) { + + PerformedCopy = FALSE; + *RunCount += 1; + + // + // Round the scratch buffer up to a sector boundary for alignment. + // + + if (RoundScratchBuffer) { + + if (SectorOffset( ScratchUserBuffer ) != 0) { + + CurrentRawByteCount = SECTOR_SIZE - SectorOffset( ScratchUserBuffer ); + + ScratchUserBuffer = Add2Ptr( ScratchUserBuffer, + CurrentRawByteCount, + PVOID ); + + ScratchUserBufferOffset += CurrentRawByteCount; + } + + RoundScratchBuffer = FALSE; + } + + // + // Initialize the current position in the IoRuns array. Find the + // eventual destination in the user's buffer for this portion of the transfer. + // + + ThisIoRun->UserBuffer = CurrentUserBuffer; + + // + // Find the allocation information for the current offset in the + // stream. + // + + CdLookupAllocation( IrpContext, + Fcb, + CurrentCookedOffset, + &DiskOffset, + &CurrentCookedByteCount ); + // + // Maybe we got lucky and this is the same sector as in the + // Vcb. + // + + if (DiskOffset == Fcb->Vcb->XADiskOffset) { + + // + // We will perform safe synchronization. Check again that + // this is the correct sector. + // + + CdLockVcb( IrpContext, Fcb->Vcb ); + + if ((DiskOffset == Fcb->Vcb->XADiskOffset) && + (Fcb->Vcb->XASector != NULL)) { + + // + // Copy any bytes we can from the current sector. + // + + CurrentRawByteCount = RAW_SECTOR_SIZE - RawSectorOffset; + + // + // Check whether we don't go to the end of the sector. + // + + if (CurrentRawByteCount > RemainingRawByteCount) { + + CurrentRawByteCount = RemainingRawByteCount; + } + + RtlCopyMemory( CurrentUserBuffer, + Add2Ptr( Fcb->Vcb->XASector, RawSectorOffset, PCHAR ), + CurrentRawByteCount ); + + CdUnlockVcb( IrpContext, Fcb->Vcb ); + + // + // Adjust the run count and pointer in the IoRuns array + // to show that we didn't use a position. + // + + *RunCount -= 1; + ThisIoRun -= 1; + + // + // Remember that we performed a copy operation and update + // the next available position in the scratch buffer. + // + + PerformedCopy = TRUE; + + ScratchUserBuffer = Add2Ptr( ScratchUserBuffer, + CurrentRawByteCount, + PVOID ); + + ScratchUserBufferOffset += CurrentRawByteCount; + + CurrentCookedByteCount = SECTOR_SIZE; + + // + // Set the flag indicating we want to round the scratch buffer + // to a sector boundary. + // + + RoundScratchBuffer = TRUE; + + } else { + + // + // The safe test showed no available buffer. Drop down to common code to + // perform the Io. + // + + CdUnlockVcb( IrpContext, Fcb->Vcb ); + } + } + + // + // No work in this pass if we did a copy operation. + // + + if (!PerformedCopy) { + + // + // Limit ourselves by the number of remaining cooked bytes. + // + + if (CurrentCookedByteCount > RemainingCookedByteCount) { + + CurrentCookedByteCount = RemainingCookedByteCount; + } + + ThisIoRun->DiskOffset = DiskOffset; + ThisIoRun->TransferBufferOffset = RawSectorOffset; + + // + // We will always need to perform copy operations for XA files. + // We allocate an auxillary buffer to read the start of the + // transfer. Then we can use a range of the user's buffer to + // perform the next range of the transfer. Finally we may + // need to allocate a buffer for the tail of the transfer. + // + // We can use the user's buffer (at the current scratch buffer) if the + // following are true: + // + // If we are to store the beginning of the raw sector in the user's buffer. + // The current scratch buffer precedes the destination in the user's buffer + // (and hence also lies within it) + // There are enough bytes remaining in the buffer for at least one + // raw sector. + // + + if ((RawSectorOffset == 0) && + (ScratchUserBufferOffset <= CurrentUserBufferOffset) && + (CurrentUserBufferOffset - ScratchUserBufferOffset + RemainingRawByteCount >= RAW_SECTOR_SIZE)) { + + // + // We can use the scratch buffer. We must ensure we don't send down reads + // greater than the device can handle, since the driver is unable to split + // raw requests. + // + + if (CurrentCookedByteCount <= Fcb->Vcb->MaximumTransferRawSectors * SECTOR_SIZE) { + + CurrentRawByteCount = (SectorAlign( CurrentCookedByteCount) >> SECTOR_SHIFT) * RAW_SECTOR_SIZE; + + } else { + + CurrentCookedByteCount = Fcb->Vcb->MaximumTransferRawSectors * SECTOR_SIZE; + CurrentRawByteCount = Fcb->Vcb->MaximumTransferRawSectors * RAW_SECTOR_SIZE; + } + + // + // Now make sure we are within the page transfer limit. + // + + while (ADDRESS_AND_SIZE_TO_SPAN_PAGES(ScratchUserBuffer, RawSectorAlign( CurrentRawByteCount)) > + Fcb->Vcb->MaximumPhysicalPages ) { + + CurrentRawByteCount -= RAW_SECTOR_SIZE; + CurrentCookedByteCount -= SECTOR_SIZE; + } + + // + // Trim the number of bytes to read if it won't fit into the current buffer. Take + // account of the fact that we must read in whole raw sector multiples. + // + + while ( RawSectorAlign( CurrentRawByteCount) > + (CurrentUserBufferOffset - ScratchUserBufferOffset + RemainingRawByteCount) ) { + + CurrentRawByteCount -= RAW_SECTOR_SIZE; + CurrentCookedByteCount -= SECTOR_SIZE; + } + + // + // Now trim the maximum number of raw bytes to the remaining bytes. + // + + if (CurrentRawByteCount > RemainingRawByteCount) { + + CurrentRawByteCount = RemainingRawByteCount; + } + + // + // Update the IO run array. We point to the scratch buffer as + // well as the buffer and Mdl in the original Irp. + // + + ThisIoRun->DiskByteCount = SectorAlign( CurrentCookedByteCount); + + // + // Store the number of bytes which we actually care about from this transfer + // + + ThisIoRun->TransferByteCount = CurrentRawByteCount; + + // + // Point to the user's buffer and Mdl for this transfer. + // + + ThisIoRun->TransferBuffer = ScratchUserBuffer; + ThisIoRun->TransferMdl = Irp->MdlAddress; + ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer, + ScratchUserBufferOffset, + PVOID); + // + // Update the scratch buffer pointer. Note that since the underlying + // driver stack will always transfer in multiples of raw sectors, + // we must round up here rather than simply advancing by the length of the + // of the data which we actually care about. + // + + ScratchUserBuffer = Add2Ptr( ScratchUserBuffer, + RawSectorAlign( CurrentRawByteCount), + PVOID ); + + ScratchUserBufferOffset += RawSectorAlign( CurrentRawByteCount);; + + // + // Set the flag indicating we want to round the scratch buffer + // to a cooked sector boundary. + // + + RoundScratchBuffer = TRUE; + + } else { + + // + // We need to determine the number of bytes to transfer and the + // offset into this page to begin the transfer. + // + // We will transfer only one raw sector. + // + + ThisIoRun->DiskByteCount = SECTOR_SIZE; + + CurrentCookedByteCount = SECTOR_SIZE; + + ThisIoRun->TransferByteCount = RAW_SECTOR_SIZE - RawSectorOffset; + ThisIoRun->TransferBufferOffset = RawSectorOffset; + + if (ThisIoRun->TransferByteCount > RemainingRawByteCount) { + + ThisIoRun->TransferByteCount = RemainingRawByteCount; + } + + CurrentRawByteCount = ThisIoRun->TransferByteCount; + + // + // We need to allocate an auxillary buffer. We will allocate + // a single page. Then we will build an Mdl to describe the buffer. + // + + ThisIoRun->TransferBuffer = FsRtlAllocatePoolWithTag( CdNonPagedPool, PAGE_SIZE, TAG_IO_BUFFER ); + + // + // Allocate and build the Mdl to describe this buffer. + // + + ThisIoRun->TransferMdl = IoAllocateMdl( ThisIoRun->TransferBuffer, + PAGE_SIZE, + FALSE, + FALSE, + NULL ); + + ThisIoRun->TransferVirtualAddress = ThisIoRun->TransferBuffer; + + if (ThisIoRun->TransferMdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + MmBuildMdlForNonPagedPool( ThisIoRun->TransferMdl ); + } + } + + // + // Update the byte count for our caller. + // + + RemainingRawByteCount -= CurrentRawByteCount; + *ThisByteCount += CurrentRawByteCount; + + // + // Break out if no more positions in the IoRuns array or + // we have all of the bytes accounted for. + // + + if ((RemainingRawByteCount == 0) || (*RunCount == MAX_PARALLEL_IOS)) { + + break; + } + + // + // Update our local pointers to allow for the current range of bytes. + // + + ThisIoRun += 1; + + CurrentUserBuffer = Add2Ptr( CurrentUserBuffer, CurrentRawByteCount, PVOID ); + CurrentUserBufferOffset += CurrentRawByteCount; + + RawSectorOffset = 0; + + CurrentCookedOffset += CurrentCookedByteCount; + RemainingCookedByteCount -= CurrentCookedByteCount; + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdFinishBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIO_RUN IoRuns, + IN ULONG RunCount, + IN BOOLEAN FinalCleanup, + IN BOOLEAN SaveXABuffer + ) + +/*++ + +Routine Description: + + This routine is called to perform any data transferred required for + unaligned Io or to perform the final cleanup of the IoRuns array. + + In all cases this is where we will deallocate any buffer and mdl + allocated to perform the unaligned transfer. If this is not the + final cleanup then we also transfer the bytes to the user buffer + and flush the hardware cache. + + We walk backwards through the run array because we may be shifting data + in the user's buffer. Typical case is where we allocated a buffer for + the first part of a read and then used the user's buffer for the + next section (but stored it at the beginning of the buffer. + +Arguments: + + IoRuns - Pointer to the IoRuns array. + + RunCount - Number of entries in the IoRuns array filled here. + + FinalCleanup - Indicates if we should be deallocating temporary buffers + (TRUE) or transferring bytes for a unaligned transfers and + deallocating the buffers (FALSE). Flush the system cache if + transferring data. + + SaveXABuffer - TRUE if we should try to save an XA buffer, FALSE otherwise + +Return Value: + + BOOLEAN - TRUE if this request needs the Io buffers to be flushed, FALSE otherwise. + +--*/ + +{ + BOOLEAN FlushIoBuffers = FALSE; + + ULONG RemainingEntries = RunCount; + PIO_RUN ThisIoRun = &IoRuns[RunCount - 1]; + PVCB Vcb; + + PAGED_CODE(); + + // + // Walk through each entry in the IoRun array. + // + + while (RemainingEntries != 0) { + + // + // We only need to deal with the case of an unaligned transfer. + // + + if (ThisIoRun->TransferByteCount != 0) { + + // + // If not the final cleanup then transfer the data to the + // user's buffer and remember that we will need to flush + // the user's buffer to memory. + // + + if (!FinalCleanup) { + + // + // If we are shifting in the user's buffer then use + // MoveMemory. + // + + if (ThisIoRun->TransferMdl == IrpContext->Irp->MdlAddress) { + + RtlMoveMemory( ThisIoRun->UserBuffer, + Add2Ptr( ThisIoRun->TransferBuffer, + ThisIoRun->TransferBufferOffset, + PVOID ), + ThisIoRun->TransferByteCount ); + + } else { + + RtlCopyMemory( ThisIoRun->UserBuffer, + Add2Ptr( ThisIoRun->TransferBuffer, + ThisIoRun->TransferBufferOffset, + PVOID ), + ThisIoRun->TransferByteCount ); + } + + FlushIoBuffers = TRUE; + } + + // + // Free any Mdl we may have allocated. If the Mdl isn't + // present then we must have failed during the allocation + // phase. + // + + if (ThisIoRun->TransferMdl != IrpContext->Irp->MdlAddress) { + + if (ThisIoRun->TransferMdl != NULL) { + + IoFreeMdl( ThisIoRun->TransferMdl ); + } + + // + // Now free any buffer we may have allocated. If the Mdl + // doesn't match the original Mdl then free the buffer. + // + + if (ThisIoRun->TransferBuffer != NULL) { + + // + // If this is the final buffer for an XA read then store this buffer + // into the Vcb so that we will have it when reading any remaining + // portion of this buffer. + // + + if (SaveXABuffer) { + + Vcb = IrpContext->Vcb; + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + } + + Vcb->XASector = ThisIoRun->TransferBuffer; + Vcb->XADiskOffset = ThisIoRun->DiskOffset; + + SaveXABuffer = FALSE; + + CdUnlockVcb( IrpContext, Vcb ); + + // + // Otherwise just free the buffer. + // + + } else { + + CdFreePool( &ThisIoRun->TransferBuffer ); + } + } + } + } + + // + // Now handle the case where we failed in the process + // of allocating associated Irps and Mdls. + // + + if (ThisIoRun->SavedIrp != NULL) { + + if (ThisIoRun->SavedIrp->MdlAddress != NULL) { + + IoFreeMdl( ThisIoRun->SavedIrp->MdlAddress ); + } + + IoFreeIrp( ThisIoRun->SavedIrp ); + } + + // + // Move to the previous IoRun entry. + // + + ThisIoRun -= 1; + RemainingEntries -= 1; + } + + // + // If we copied any data then flush the Io buffers. + // + + return FlushIoBuffers; +} + + +// +// Local support routine +// + +VOID +CdMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN ULONG RunCount, + IN PIO_RUN IoRuns + ) + +/*++ + +Routine Description: + + This routine first does the initial setup required of a Master IRP that is + going to be completed using associated IRPs. This routine should not + be used if only one async request is needed, instead the single read + async routines should be called. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. + + Next this routine reads or writes one or more contiguous sectors from + a device asynchronously, and is used if there are multiple reads for a + master IRP. A completion routine is used to synchronize with the + completion of all of the I/O requests started by calls to this routine. + + Also, prior to calling this routine the caller must initialize the + IoStatus field in the Context, with the correct success status and byte + count which are expected if all of the parallel transfers complete + successfully. After return this status will be unchanged if all requests + were, in fact, successful. However, if one or more errors occur, the + IoStatus will be modified to reflect the error status and byte count + from the first run (by Vbo) which encountered an error. I/O status + from all subsequent runs will not be indicated. + +Arguments: + + RunCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Offset and ByteCount for the + separate requests. + +Return Value: + + None. + +--*/ + +{ + PIO_COMPLETION_ROUTINE CompletionRoutine; + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + PIRP Irp; + PIRP MasterIrp; + ULONG UnwindRunCount; + + PAGED_CODE(); + + // + // Set up things according to whether this is truely async. + // + + CompletionRoutine = CdMultiSyncCompletionRoutine; + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CompletionRoutine = CdMultiAsyncCompletionRoutine; + } + + // + // Initialize some local variables. + // + + MasterIrp = IrpContext->Irp; + + // + // Itterate through the runs, doing everything that can fail. + // We let the cleanup in CdFinishBuffers clean up on error. + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount += 1) { + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + IoRuns[UnwindRunCount].SavedIrp = + Irp = IoMakeAssociatedIrp( MasterIrp, (CCHAR)(IrpContext->Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( IoRuns[UnwindRunCount].TransferVirtualAddress, + IoRuns[UnwindRunCount].DiskByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( IoRuns[UnwindRunCount].TransferMdl, + Mdl, + IoRuns[UnwindRunCount].TransferVirtualAddress, + IoRuns[UnwindRunCount].DiskByteCount ); + + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( Irp ); + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Setup the Stack location to describe our read. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].DiskOffset; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + CompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].DiskOffset; + } + + // + // We only need to set the associated IRP count in the master irp to + // make it a master IRP. But we set the count to one more than our + // caller requested, because we do not want the I/O system to complete + // the I/O. We also set our own count. + // + + IrpContext->IoContext->IrpCount = RunCount; + IrpContext->IoContext->MasterIrp = MasterIrp; + + // + // We set the count in the master Irp to 1 since typically we + // will clean up the associated irps ourselves. Setting this to one + // means completing the last associated Irp with SUCCESS (in the async + // case) will complete the master irp. + // + + MasterIrp->AssociatedIrp.IrpCount = 1; + + // + // Now that all the dangerous work is done, issue the Io requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + IoRuns[UnwindRunCount].SavedIrp = NULL; + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID) IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + } + + return; +} + + +// +// Local support routine +// + +VOID +CdMultipleXAAsync ( + IN PIRP_CONTEXT IrpContext, + IN ULONG RunCount, + IN PIO_RUN IoRuns, + IN PRAW_READ_INFO RawReads, + IN TRACK_MODE_TYPE TrackMode + ) + +/*++ + +Routine Description: + + This routine first does the initial setup required of a Master IRP that is + going to be completed using associated IRPs. This routine is used to generate + the associated Irps used to read raw sectors from the disk. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. + + Next this routine reads or writes one or more contiguous sectors from + a device asynchronously, and is used if there are multiple reads for a + master IRP. A completion routine is used to synchronize with the + completion of all of the I/O requests started by calls to this routine. + + Also, prior to calling this routine the caller must initialize the + IoStatus field in the Context, with the correct success status and byte + count which are expected if all of the parallel transfers complete + successfully. After return this status will be unchanged if all requests + were, in fact, successful. However, if one or more errors occur, the + IoStatus will be modified to reflect the error status and byte count + from the first run (by Vbo) which encountered an error. I/O status + from all subsequent runs will not be indicated. + +Arguments: + + RunCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Offset and ByteCount for the + separate requests. + + RawReads - Supplies an array of structures to store in the Irps passed to the + device driver to perform the low-level Io. + + TrackMode - Supplies the recording mode of sectors in these IoRuns + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + PIRP Irp; + PIRP MasterIrp; + ULONG UnwindRunCount; + ULONG RawByteCount; + + PIO_RUN ThisIoRun = IoRuns; + PRAW_READ_INFO ThisRawRead = RawReads; + + PAGED_CODE(); + + // + // Initialize some local variables. + // + + MasterIrp = IrpContext->Irp; + + // + // Itterate through the runs, doing everything that can fail. + // We let the cleanup in CdFinishBuffers clean up on error. + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount += 1, ThisIoRun += 1, ThisRawRead += 1) { + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + ThisIoRun->SavedIrp = + Irp = IoMakeAssociatedIrp( MasterIrp, (CCHAR)(IrpContext->Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Should have been passed a byte count of at least one sector, and + // must be a multiple of sector size + // + + ASSERT( ThisIoRun->DiskByteCount && !SectorOffset(ThisIoRun->DiskByteCount)); + + RawByteCount = SectorsFromBytes( ThisIoRun->DiskByteCount) * RAW_SECTOR_SIZE; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( ThisIoRun->TransferVirtualAddress, + RawByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( ThisIoRun->TransferMdl, + Mdl, + ThisIoRun->TransferVirtualAddress, + RawByteCount); + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( Irp ); + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Setup the Stack location to describe our read (using cooked values) + // These values won't be used for the raw read in any case. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = ThisIoRun->DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = ThisIoRun->DiskOffset; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + CdMultiSyncCompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the stack location to do a read of raw sectors at this location. + // Note that the storage stack always reads multiples of whole XA sectors. + // + + ThisRawRead->DiskOffset.QuadPart = ThisIoRun->DiskOffset; + ThisRawRead->SectorCount = ThisIoRun->DiskByteCount >> SECTOR_SHIFT; + ThisRawRead->TrackMode = TrackMode; + + IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + IrpSp->Parameters.DeviceIoControl.OutputBufferLength = ThisRawRead->SectorCount * RAW_SECTOR_SIZE; + Irp->UserBuffer = ThisIoRun->TransferVirtualAddress; + + IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof( RAW_READ_INFO ); + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = ThisRawRead; + + IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_CDROM_RAW_READ; + } + + // + // We only need to set the associated IRP count in the master irp to + // make it a master IRP. But we set the count to one more than our + // caller requested, because we do not want the I/O system to complete + // the I/O. We also set our own count. + // + + IrpContext->IoContext->IrpCount = RunCount; + IrpContext->IoContext->MasterIrp = MasterIrp; + + // + // We set the count in the master Irp to 1 since typically we + // will clean up the associated irps ourselves. Setting this to one + // means completing the last associated Irp with SUCCESS (in the async + // case) will complete the master irp. + // + + MasterIrp->AssociatedIrp.IrpCount = 1; + + // + // Now that all the dangerous work is done, issue the Io requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + IoRuns[UnwindRunCount].SavedIrp = NULL; + + // + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID) IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + } + + return; +} + + +// +// Local support routine +// + +VOID +CdSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN LONGLONG ByteOffset, + IN ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine reads one or more contiguous sectors from a device + asynchronously, and is used if there is only one read necessary to + complete the IRP. It implements the read by simply filling + in the next stack frame in the Irp, and passing it on. The transfer + occurs to the single buffer originally specified in the user request. + +Arguments: + + ByteOffset - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PIO_COMPLETION_ROUTINE CompletionRoutine; + + PAGED_CODE(); + + // + // Set up things according to whether this is truely async. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CompletionRoutine = CdSingleSyncCompletionRoutine; + + } else { + + CompletionRoutine = CdSingleAsyncCompletionRoutine; + } + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( IrpContext->Irp, + CompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( IrpContext->Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = ByteOffset; + + // + // Issue the Io request + // + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID)IoCallDriver( IrpContext->Vcb->TargetDeviceObject, IrpContext->Irp ); + + // + // And return to our caller + // + + return; +} + + +// +// Local support routine +// + +VOID +CdWaitSync ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine waits for one or more previously started I/O requests + from the above routines, by simply waiting on the event. + +Arguments: + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + KeWaitForSingleObject( &IrpContext->IoContext->SyncEvent, + Executive, + KernelMode, + FALSE, + NULL ); + + KeClearEvent( &IrpContext->IoContext->SyncEvent ); + + return; +} + + +// +// Local support routine +// + +NTSTATUS +CdMultiSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all synchronous reads + started via CdMultipleAsynch. + + The completion routine has has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + and remember the error status in the Context. + + If the IrpCount goes to 1, then it sets the event in the Context + parameter to signal the caller that all of the asynch requests + are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Context - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + PCD_IO_CONTEXT IoContext = Context; + + AssertVerifyDeviceIrp( Irp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + InterlockedExchange( &IoContext->Status, Irp->IoStatus.Status ); + IoContext->MasterIrp->IoStatus.Information = 0; + } + + // + // We must do this here since IoCompleteRequest won't get a chance + // on this associated Irp. + // + + IoFreeMdl( Irp->MdlAddress ); + IoFreeIrp( Irp ); + + if (InterlockedDecrement( &IoContext->IrpCount ) == 0) { + + // + // Update the Master Irp with any error status from the associated Irps. + // + + IoContext->MasterIrp->IoStatus.Status = IoContext->Status; + KeSetEvent( &IoContext->SyncEvent, 0, FALSE ); + } + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Local support routine +// + +NTSTATUS +CdMultiAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all asynchronous reads + started via CdMultipleAsynch. + + The completion routine has has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + and remember the error status in the Context. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Context - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PCD_IO_CONTEXT IoContext = Context; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + AssertVerifyDeviceIrp( Irp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + InterlockedExchange( &IoContext->Status, Irp->IoStatus.Status ); + } + + // + // Decrement IrpCount and see if it goes to zero. + // + + if (InterlockedDecrement( &IoContext->IrpCount ) == 0) { + + // + // Mark the master Irp pending + // + + IoMarkIrpPending( IoContext->MasterIrp ); + + // + // Update the Master Irp with any error status from the associated Irps. + // + + IoContext->MasterIrp->IoStatus.Status = IoContext->Status; + + // + // Update the information field with the correct value. + // + + IoContext->MasterIrp->IoStatus.Information = 0; + + if (NT_SUCCESS( IoContext->MasterIrp->IoStatus.Status )) { + + IoContext->MasterIrp->IoStatus.Information = IoContext->RequestedByteCount; + } + + // + // Now release the resource + // + + ExReleaseResourceForThreadLite( IoContext->Resource, + IoContext->ResourceThreadId ); + + // + // and finally, free the context record. + // + + CdFreeIoContext( IoContext ); + + // + // Return success in this case. + // + + return STATUS_SUCCESS; + + } else { + + // + // We need to cleanup the associated Irp and its Mdl. + // + + IoFreeMdl( Irp->MdlAddress ); + IoFreeIrp( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +// +// Local support routine +// + +NTSTATUS +CdSingleSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads started via CdSingleAsynch. + + The completion routine has has the following responsibilities: + + It sets the event in the Context parameter to signal the caller + that all of the asynch requests are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Context - The context parameter which was specified in the call to + CdSingleAsynch. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + AssertVerifyDeviceIrp( Irp ); + + // + // Store the correct information field into the Irp. + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + Irp->IoStatus.Information = 0; + } + + KeSetEvent( &((PCD_IO_CONTEXT)Context)->SyncEvent, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Local support routine +// + +NTSTATUS +CdSingleAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all asynchronous reads + started via CdSingleAsynch. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Context - The context parameter which was specified in the call to + CdSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + AssertVerifyDeviceIrp( Irp ); + + // + // Update the information field with the correct value for bytes read. + // + + Irp->IoStatus.Information = 0; + + if (NT_SUCCESS( Irp->IoStatus.Status )) { + + Irp->IoStatus.Information = ((PCD_IO_CONTEXT) Context)->RequestedByteCount; + } + + // + // Mark the Irp pending + // + + IoMarkIrpPending( Irp ); + + // + // Now release the resource + // + + ExReleaseResourceForThreadLite( ((PCD_IO_CONTEXT) Context)->Resource, + ((PCD_IO_CONTEXT) Context)->ResourceThreadId ); + + // + // and finally, free the context record. + // + + CdFreeIoContext( (PCD_IO_CONTEXT) Context ); + return STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +// +// Local support routine +// + +VOID +CdReadAudioSystemFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG StartingOffset, + IN ULONG ByteCount, + IN PVOID SystemBuffer + ) + +/*++ + +Routine Description: + + This routine is called to read the pseudo root directory and path + table for a music disk. We build the individual elements on the + stack and copy into the cache buffer. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + + SystemBuffer - Pointer to buffer to fill in. This will always be page + aligned. + +Return Value: + + None. + +--*/ + +{ + PRAW_PATH_ISO RawPath; + PRAW_DIRENT RawDirent; + + ULONG CurrentTrack; + ULONG SectorOffset; + ULONG EntryCount; + UCHAR TrackOnes; + UCHAR TrackTens; + PTRACK_DATA ThisTrack; + + LONGLONG CurrentOffset; + + PVOID CurrentSector; + + PSYSTEM_USE_XA SystemUse; + + ULONG BytesToCopy; + + UCHAR LocalBuffer[FIELD_OFFSET( RAW_DIRENT, FileId ) + 12]; + + PAGED_CODE(); + + // + // If this is the path table then we just need a single entry. + // + + if (SafeNodeType( Fcb ) == CDFS_NTC_FCB_PATH_TABLE) { + + // + // Sanity check that the offset is zero. + // + + ASSERT( StartingOffset == 0 ); + + // + // Store a pseudo path entry in our local buffer. + // + + RawPath = (PRAW_PATH_ISO) LocalBuffer; + + RtlZeroMemory( RawPath, sizeof( LocalBuffer )); + + RawPath->DirIdLen = 1; + RawPath->ParentNum = 1; + RawPath->DirId[0] = '\0'; + + // + // Now copy to the user's buffer. + // + + BytesToCopy = FIELD_OFFSET( RAW_PATH_ISO, DirId ) + 2; + + if (BytesToCopy > ByteCount) { + + BytesToCopy = ByteCount; + } + + RtlCopyMemory( SystemBuffer, + RawPath, + BytesToCopy ); + + // + // We need to deal with the multiple sector case for the root directory. + // + + } else { + + // + // Initialize the first track to return to our caller. + // + + CurrentTrack = 0; + + // + // If the offset is zero then store the entries for the self and parent + // entries. + // + + if (StartingOffset == 0) { + + RawDirent = SystemBuffer; + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, FIELD_OFFSET( RAW_DIRENT, FileId )); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = FIELD_OFFSET( RAW_DIRENT, FileId ) + 1; + RawDirent->FileIdLen = 1; + RawDirent->FileId[0] = '\0'; + SetFlag( RawDirent->FlagsISO, CD_ATTRIBUTE_DIRECTORY ); + + // + // Set the time stamp to be Jan 1, 1995 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + SectorOffset = RawDirent->DirLen; + + RawDirent = Add2Ptr( RawDirent, SectorOffset, PRAW_DIRENT ); + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, FIELD_OFFSET( RAW_DIRENT, FileId )); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = FIELD_OFFSET( RAW_DIRENT, FileId ) + 1; + RawDirent->FileIdLen = 1; + RawDirent->FileId[0] = '\1'; + SetFlag( RawDirent->FlagsISO, CD_ATTRIBUTE_DIRECTORY ); + + // + // Set the time stamp to be Jan 1, 1995 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + SectorOffset += RawDirent->DirLen; + EntryCount = 2; + + // + // Otherwise compute the starting track to write to the buffer. + // + + } else { + + // + // Count the tracks in each preceding sector. + // + + CurrentOffset = 0; + + do { + + CurrentTrack += CdAudioDirentsPerSector; + CurrentOffset += SECTOR_SIZE; + + } while (CurrentOffset < StartingOffset); + + // + // Bias the track count to reflect the two default entries. + // + + CurrentTrack -= 2; + + SectorOffset = 0; + EntryCount = 0; + } + + // + // We now know the first track to return as well as where we are in + // the current sector. We will walk through sector by sector adding + // the entries for the separate tracks in the TOC. We will zero + // any sectors or partial sectors without data. + // + + CurrentSector = SystemBuffer; + BytesToCopy = SECTOR_SIZE; + + // + // Loop for each sector. + // + + do { + + // + // Add entries until we reach our threshold for each sector. + // + + do { + + // + // If we are beyond the entries in the TOC then exit. + // + + if (CurrentTrack >= IrpContext->Vcb->TrackCount) { + + break; + } + + ThisTrack = &IrpContext->Vcb->CdromToc->TrackData[CurrentTrack]; + + // + // Point to the current position in the buffer. + // + + RawDirent = Add2Ptr( CurrentSector, SectorOffset, PRAW_DIRENT ); + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, CdAudioDirentSize ); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = (UCHAR) CdAudioDirentSize; + RawDirent->FileIdLen = CdAudioFileNameLength; + + RtlCopyMemory( RawDirent->FileId, + CdAudioFileName, + CdAudioFileNameLength ); + + // + // Set the time stamp to be Jan 1, 1995 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + // + // Now bias by the values in the TOC. + // + + RawDirent->RecordTime[4] = ThisTrack->Address[1] % 60; + RawDirent->RecordTime[5] = ThisTrack->Address[2] % 60; + + // + // Put the track number into the file name. + // + + TrackTens = TrackOnes = ThisTrack->TrackNumber; + + TrackOnes = (TrackOnes % 10) + '0'; + + TrackTens /= 10; + TrackTens = (TrackTens % 10) + '0'; + + RawDirent->FileId[AUDIO_NAME_TENS_OFFSET] = TrackTens; + RawDirent->FileId[AUDIO_NAME_ONES_OFFSET] = TrackOnes; + + SystemUse = Add2Ptr( RawDirent, CdAudioSystemUseOffset, PSYSTEM_USE_XA ); + + SystemUse->Attributes = SYSTEM_USE_XA_DA; + SystemUse->Signature = SYSTEM_XA_SIGNATURE; + + // + // Store the track number as the file number. + // + + SystemUse->FileNumber = (UCHAR) CurrentTrack; + + EntryCount += 1; + SectorOffset += CdAudioDirentSize; + CurrentTrack += 1; + + } while (EntryCount < CdAudioDirentsPerSector); + + // + // Zero the remaining portion of this buffer. + // + + RtlZeroMemory( Add2Ptr( CurrentSector, SectorOffset, PVOID ), + SECTOR_SIZE - SectorOffset ); + + // + // Prepare for the next sector. + // + + EntryCount = 0; + BytesToCopy += SECTOR_SIZE; + SectorOffset = 0; + CurrentSector = Add2Ptr( CurrentSector, SECTOR_SIZE, PVOID ); + + } while (BytesToCopy <= ByteCount); + } + + return; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/dirctrl.c b/reactos/drivers/filesystems/cdfs_new/dirctrl.c new file mode 100755 index 00000000000..9c363df1469 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/dirctrl.c @@ -0,0 +1,1520 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirCtrl.c + +Abstract: + + This module implements the File Directory Control routines for Cdfs called + by the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL) + +// +// Local support routines +// + +NTSTATUS +CdQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN PFCB Fcb, + IN PCCB Ccb + ); + +NTSTATUS +CdNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN PCCB Ccb + ); + +VOID +CdInitializeEnumeration ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PFCB Fcb, + IN OUT PCCB Ccb, + IN OUT PFILE_ENUM_CONTEXT FileContext, + OUT PBOOLEAN ReturnNextEntry, + OUT PBOOLEAN ReturnSingleEntry, + OUT PBOOLEAN InitialQuery + ); + +BOOLEAN +CdEnumerateIndex ( + IN PIRP_CONTEXT IrpContext, + IN PCCB Ccb, + IN OUT PFILE_ENUM_CONTEXT FileContext, + IN BOOLEAN ReturnNextEntry + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonDirControl) +#pragma alloc_text(PAGE, CdEnumerateIndex) +#pragma alloc_text(PAGE, CdInitializeEnumeration) +#pragma alloc_text(PAGE, CdNotifyChangeDirectory) +#pragma alloc_text(PAGE, CdQueryDirectory) +#endif + + +NTSTATUS +CdCommonDirControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the entry point for the directory control operations. These + are directory enumerations and directory notify calls. We verify the + user's handle is for a directory and then call the appropriate routine. + +Arguments: + + Irp - Irp for this request. + +Return Value: + + NTSTATUS - Status returned from the lower level routines. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the user file object and fail this request if it is not + // a user directory. + // + + if (CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ) != UserDirectoryOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // We know this is a directory control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_QUERY_DIRECTORY: + + Status = CdQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = CdNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +// +// Local support routines +// + +NTSTATUS +CdQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the query directory operation. It is responsible + for either completing of enqueuing the input Irp. We store the state of the + search in the Ccb. + +Arguments: + + Irp - Supplies the Irp to process + + IrpSp - Stack location for this Irp. + + Fcb - Fcb for this directory. + + Ccb - Ccb for this directory open. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Information = 0; + + ULONG LastEntry = 0; + ULONG NextEntry = 0; + + ULONG FileNameBytes; + ULONG SeparatorBytes; + ULONG VersionStringBytes; + + FILE_ENUM_CONTEXT FileContext; + PDIRENT ThisDirent; + BOOLEAN InitialQuery; + BOOLEAN ReturnNextEntry; + BOOLEAN ReturnSingleEntry; + BOOLEAN Found; + BOOLEAN DoCcbUpdate = FALSE; + + PCHAR UserBuffer; + ULONG BytesRemainingInBuffer; + + ULONG BaseLength; + + PFILE_BOTH_DIR_INFORMATION DirInfo; + PFILE_NAMES_INFORMATION NamesInfo; + PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; + PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; + + PAGED_CODE(); + + // + // Check if we support this search mode. Also remember the size of the base part of + // each of these structures. + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, + FileName[0] ); + break; + + case FileFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileNamesInformation: + + BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, + FileName[0] ); + break; + + case FileBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS ); + return STATUS_INVALID_INFO_CLASS; + } + + // + // Get the user buffer. + // + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Initialize our search context. + // + + CdInitializeFileContext( IrpContext, &FileContext ); + + // + // Acquire the directory. + // + + CdAcquireFileShared( IrpContext, Fcb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Fcb is still good. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Start by getting the initial state for the enumeration. This will set up the Ccb with + // the initial search parameters and let us know the starting offset in the directory + // to search. + // + + CdInitializeEnumeration( IrpContext, + IrpSp, + Fcb, + Ccb, + &FileContext, + &ReturnNextEntry, + &ReturnSingleEntry, + &InitialQuery ); + + // + // The current dirent is stored in the InitialDirent field. We capture + // this here so that we have a valid restart point even if we don't + // find a single entry. + // + + ThisDirent = &FileContext.InitialDirent->Dirent; + + // + // At this point we are about to enter our query loop. We have + // determined the index into the directory file to begin the + // search. LastEntry and NextEntry are used to index into the user + // buffer. LastEntry is the last entry we've added, NextEntry is + // current one we're working on. If NextEntry is non-zero, then + // at least one entry was added. + // + + while (TRUE) { + + // + // If the user had requested only a single match and we have + // returned that, then we stop at this point. We update the Ccb with + // the status based on the last entry returned. + // + + if ((NextEntry != 0) && ReturnSingleEntry) { + + DoCcbUpdate = TRUE; + try_leave( Status ); + } + + // + // We try to locate the next matching dirent. Our search if based on a starting + // dirent offset, whether we should return the current or next entry, whether + // we should be doing a short name search and finally whether we should be + // checking for a version match. + // + + Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry ); + + // + // Initialize the value for the next search. + // + + ReturnNextEntry = TRUE; + + // + // If we didn't receive a dirent, then we are at the end of the + // directory. If we have returned any files, we exit with + // success, otherwise we return STATUS_NO_MORE_FILES. + // + + if (!Found) { + + if (NextEntry == 0) { + + Status = STATUS_NO_MORE_FILES; + + if (InitialQuery) { + + Status = STATUS_NO_SUCH_FILE; + } + } + + DoCcbUpdate = TRUE; + try_leave( Status ); + } + + // + // Remember the dirent for the file we just found. + // + + ThisDirent = &FileContext.InitialDirent->Dirent; + + // + // Here are the rules concerning filling up the buffer: + // + // 1. The Io system garentees that there will always be + // enough room for at least one base record. + // + // 2. If the full first record (including file name) cannot + // fit, as much of the name as possible is copied and + // STATUS_BUFFER_OVERFLOW is returned. + // + // 3. If a subsequent record cannot completely fit into the + // buffer, none of it (as in 0 bytes) is copied, and + // STATUS_SUCCESS is returned. A subsequent query will + // pick up with this record. + // + + // + // Let's compute the number of bytes we need to transfer the current entry. + // + + SeparatorBytes = + VersionStringBytes = 0; + + // + // We can look directly at the dirent that we found. + // + + FileNameBytes = ThisDirent->CdFileName.FileName.Length; + + // + // Compute the number of bytes for the version string if + // we will return this. Allow directories with illegal ";". + // + + if (((Ccb->SearchExpression.VersionString.Length != 0) || + (FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) && + (ThisDirent->CdFileName.VersionString.Length != 0)) { + + SeparatorBytes = 2; + + VersionStringBytes = ThisDirent->CdFileName.VersionString.Length; + } + + // + // If the slot for the next entry would be beyond the length of the + // user's buffer just exit (we know we've returned at least one entry + // already). This will happen when we align the pointer past the end. + // + + if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) { + + ReturnNextEntry = FALSE; + DoCcbUpdate = TRUE; + try_leave( Status = STATUS_SUCCESS ); + } + + // + // Compute the number of bytes remaining in the buffer. Round this + // down to a WCHAR boundary so we can copy full characters. + // + + BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry; + ClearFlag( BytesRemainingInBuffer, 1 ); + + // + // If this won't fit and we have returned a previous entry then just + // return STATUS_SUCCESS. + // + + if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) { + + // + // If we already found an entry then just exit. + // + + if (NextEntry != 0) { + + ReturnNextEntry = FALSE; + DoCcbUpdate = TRUE; + try_leave( Status = STATUS_SUCCESS ); + } + + // + // Don't even try to return the version string if it doesn't all fit. + // Reduce the FileNameBytes to just fit in the buffer. + // + + if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { + + FileNameBytes = BytesRemainingInBuffer - BaseLength; + } + + // + // Don't return any version string bytes. + // + + VersionStringBytes = + SeparatorBytes = 0; + + // + // Use a status code of STATUS_BUFFER_OVERFLOW. Also set + // ReturnSingleEntry so that we will exit the loop at the top. + // + + Status = STATUS_BUFFER_OVERFLOW; + ReturnSingleEntry = TRUE; + } + + // + // Protect access to the user buffer with an exception handler. + // Since (at our request) IO doesn't buffer these requests, we have + // to guard against a user messing with the page protection and other + // such trickery. + // + + try { + + // + // Zero and initialize the base part of the current entry. + // + + RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ), + BaseLength ); + + // + // Now we have an entry to return to our caller. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileBothDirectoryInformation: + case FileFullDirectoryInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + case FileDirectoryInformation: + + DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION ); + + // + // Use the create time for all the time stamps. + // + + CdConvertCdTimeToNtTime( IrpContext, + FileContext.InitialDirent->Dirent.CdTime, + &DirInfo->CreationTime ); + + DirInfo->LastWriteTime = DirInfo->ChangeTime = DirInfo->CreationTime; + + // + // Set the attributes and sizes separately for directories and + // files. + // + + if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0; + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY ); + + } else { + + DirInfo->EndOfFile.QuadPart = FileContext.FileSize; + DirInfo->AllocationSize.QuadPart = LlSectorAlign( FileContext.FileSize ); + } + + // + // All Cdrom files are readonly. We also copy the existence + // bit to the hidden attribute. + // + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY ); + + if (FlagOn( ThisDirent->DirentFlags, + CD_ATTRIBUTE_HIDDEN )) { + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); + } + + DirInfo->FileIndex = ThisDirent->DirentOffset; + + DirInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; + + break; + + case FileNamesInformation: + + NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION ); + + NamesInfo->FileIndex = ThisDirent->DirentOffset; + + NamesInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; + + break; + } + + // + // Fill in the FileId + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileIdBothDirectoryInformation: + + IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION ); + CdSetFidFromParentAndDirent( IdBothDirInfo->FileId, Fcb, ThisDirent ); + break; + + case FileIdFullDirectoryInformation: + + IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION ); + CdSetFidFromParentAndDirent( IdFullDirInfo->FileId, Fcb, ThisDirent ); + break; + + default: + break; + } + + // + // Now copy as much of the name as possible. We also may have a version + // string to copy. + // + + if (FileNameBytes != 0) { + + // + // This is a Unicode name, we can copy the bytes directly. + // + + RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ), + ThisDirent->CdFileName.FileName.Buffer, + FileNameBytes ); + + if (SeparatorBytes != 0) { + + *(Add2Ptr( UserBuffer, + NextEntry + BaseLength + FileNameBytes, + PWCHAR )) = L';'; + + if (VersionStringBytes != 0) { + + RtlCopyMemory( Add2Ptr( UserBuffer, + NextEntry + BaseLength + FileNameBytes + sizeof( WCHAR ), + PVOID ), + ThisDirent->CdFileName.VersionString.Buffer, + VersionStringBytes ); + } + } + } + + // + // Fill in the short name if we got STATUS_SUCCESS. The short name + // may already be in the file context. Otherwise we will check + // whether the long name is 8.3. Special case the self and parent + // directory names. + // + + if ((Status == STATUS_SUCCESS) && + (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || + IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) && + (Ccb->SearchExpression.VersionString.Length == 0) && + !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + // + // If we already have the short name then copy into the user's buffer. + // + + if (FileContext.ShortName.FileName.Length != 0) { + + RtlCopyMemory( DirInfo->ShortName, + FileContext.ShortName.FileName.Buffer, + FileContext.ShortName.FileName.Length ); + + DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; + + // + // If the short name length is currently zero then check if + // the long name is not 8.3. We can copy the short name in + // unicode form directly into the caller's buffer. + // + + } else { + + if (!CdIs8dot3Name( IrpContext, + ThisDirent->CdFileName.FileName )) { + + CdGenerate8dot3Name( IrpContext, + &ThisDirent->CdCaseFileName.FileName, + ThisDirent->DirentOffset, + DirInfo->ShortName, + &FileContext.ShortName.FileName.Length ); + + DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; + } + } + + } + + // + // Sum the total number of bytes for the information field. + // + + FileNameBytes += SeparatorBytes + VersionStringBytes; + + // + // Update the information with the number of bytes stored in the + // buffer. We quad-align the existing buffer to add any necessary + // pad bytes. + // + + Information = NextEntry + BaseLength + FileNameBytes; + + // + // Go back to the previous entry and fill in the update to this entry. + // + + *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry; + + // + // Set up our variables for the next dirent. + // + + InitialQuery = FALSE; + + LastEntry = NextEntry; + NextEntry = QuadAlign( Information ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // We had a problem filling in the user's buffer, so stop and + // fail this request. This is the only reason any exception + // would have occured at this level. + // + + Information = 0; + try_leave( Status = GetExceptionCode()); + } + } + + DoCcbUpdate = TRUE; + + } finally { + + // + // Cleanup our search context - *before* aquiring the FCB mutex exclusive, + // else can block on threads in cdcreateinternalstream/purge which + // hold the FCB but are waiting for all maps in this stream to be released. + // + + CdCleanupFileContext( IrpContext, &FileContext ); + + // + // Now we can safely aqure the FCB mutex if we need to. + // + + if (DoCcbUpdate && !NT_ERROR( Status )) { + + // + // Update the Ccb to show the current state of the enumeration. + // + + CdLockFcb( IrpContext, Fcb ); + + Ccb->CurrentDirentOffset = ThisDirent->DirentOffset; + + ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + + if (ReturnNextEntry) { + + SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + } + + CdUnlockFcb( IrpContext, Fcb ); + } + + // + // Release the Fcb. + // + + CdReleaseFile( IrpContext, Fcb ); + } + + // + // Complete the request here. + // + + Irp->IoStatus.Information = Information; + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routines +// + +NTSTATUS +CdNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the notify change directory operation. It is + responsible for either completing of enqueuing the input Irp. Although there + will never be a notify signalled on a CDROM disk we still support this call. + + We have already checked that this is not an OpenById handle. + +Arguments: + + Irp - Supplies the Irp to process + + IrpSp - Io stack location for this request. + + Ccb - Handle to the directory being watched. + +Return Value: + + NTSTATUS - STATUS_PENDING, any other error will raise. + +--*/ + +{ + PAGED_CODE(); + + // + // Always set the wait bit in the IrpContext so the initial wait can't fail. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Acquire the Vcb shared. + // + + CdAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, IrpContext->Vcb ); + + // + // Call the Fsrtl package to process the request. We cast the + // unicode strings to ansi strings as the dir notify package + // only deals with memory matching. + // + + FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync, + &IrpContext->Vcb->DirNotifyList, + Ccb, + (PSTRING) &IrpSp->FileObject->FileName, + BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ), + FALSE, + IrpSp->Parameters.NotifyDirectory.CompletionFilter, + Irp, + NULL, + NULL ); + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, IrpContext->Vcb ); + } + + // + // Cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return STATUS_PENDING; +} + + +// +// Local support routine +// + +VOID +CdInitializeEnumeration ( + IN PIRP_CONTEXT IrpContext, + IN PIO_STACK_LOCATION IrpSp, + IN PFCB Fcb, + IN OUT PCCB Ccb, + IN OUT PFILE_ENUM_CONTEXT FileContext, + OUT PBOOLEAN ReturnNextEntry, + OUT PBOOLEAN ReturnSingleEntry, + OUT PBOOLEAN InitialQuery + ) + +/*++ + +Routine Description: + + This routine is called to initialize the enumeration variables and structures. + We look at the state of a previous enumeration from the Ccb as well as any + input values from the user. On exit we will position the FileContext at + a file in the directory and let the caller know whether this entry or the + next entry should be returned. + +Arguments: + + IrpSp - Irp stack location for this request. + + Fcb - Fcb for this directory. + + Ccb - Ccb for the directory handle. + + FileContext - FileContext to use for this enumeration. + + ReturnNextEntry - Address to store whether we should return the entry at + the FileContext position or the next entry. + + ReturnSingleEntry - Address to store whether we should only return + a single entry. + + InitialQuery - Address to store whether this is the first enumeration + query on this handle. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + PUNICODE_STRING FileName; + CD_NAME WildCardName; + CD_NAME SearchExpression; + + ULONG CcbFlags; + + ULONG DirentOffset; + ULONG LastDirentOffset; + BOOLEAN KnownOffset; + + BOOLEAN Found; + + PAGED_CODE(); + + // + // If this is the initial query then build a search expression from the input + // file name. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { + + FileName = IrpSp->Parameters.QueryDirectory.FileName; + + CcbFlags = 0; + + // + // If the filename is not specified or is a single '*' then we will + // match all names. + // + + if ((FileName == NULL) || + (FileName->Buffer == NULL) || + (FileName->Length == 0) || + ((FileName->Length == sizeof( WCHAR )) && + (FileName->Buffer[0] == L'*'))) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL ); + RtlZeroMemory( &SearchExpression, sizeof( SearchExpression )); + + // + // Otherwise build the CdName from the name in the stack location. + // This involves building both the name and version portions and + // checking for wild card characters. We also upcase the string if + // this is a case-insensitive search. + // + + } else { + + // + // Create a CdName to check for wild cards. + // + + WildCardName.FileName = *FileName; + + CdConvertNameToCdName( IrpContext, &WildCardName ); + + // + // The name better have at least one character. + // + + if (WildCardName.FileName.Length == 0) { + + CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER ); + } + + // + // Check for wildcards in the separate components. + // + + if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ); + } + + if ((WildCardName.VersionString.Length != 0) && + (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD ); + + // + // Check if this is a wild card only and match all version + // strings. + // + + if ((WildCardName.VersionString.Length == sizeof( WCHAR )) && + (WildCardName.VersionString.Buffer[0] == L'*')) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL ); + } + } + + // + // Now create the search expression to store in the Ccb. + // + + SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + FileName->Length, + TAG_ENUM_EXPRESSION ); + + SearchExpression.FileName.MaximumLength = FileName->Length; + + // + // Either copy the name directly or perform the upcase. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) { + + Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName, + FileName, + FALSE ); + + // + // This should never fail. + // + + ASSERT( Status == STATUS_SUCCESS ); + + } else { + + RtlCopyMemory( SearchExpression.FileName.Buffer, + FileName->Buffer, + FileName->Length ); + } + + // + // Now split into the separate name and version components. + // + + SearchExpression.FileName.Length = WildCardName.FileName.Length; + SearchExpression.VersionString.Length = WildCardName.VersionString.Length; + SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength; + + SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer, + SearchExpression.FileName.Length + sizeof( WCHAR ), + PWCHAR ); + } + + // + // But we do not want to return the constant "." and ".." entries for + // the root directory, for consistency with the rest of Microsoft's + // filesystems. + // + + if (Fcb == Fcb->Vcb->RootIndexFcb) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY ); + } + + // + // Now lock the Fcb in order to update the Ccb with the inital + // enumeration values. + // + + CdLockFcb( IrpContext, Fcb ); + + // + // Check again that this is the initial search. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { + + // + // Update the values in the Ccb. + // + + Ccb->CurrentDirentOffset = Fcb->StreamOffset; + Ccb->SearchExpression = SearchExpression; + + // + // Set the appropriate flags in the Ccb. + // + + SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED ); + + // + // Otherwise cleanup any buffer allocated here. + // + + } else { + + if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) { + + CdFreePool( &SearchExpression.FileName.Buffer ); + } + } + + // + // Otherwise lock the Fcb so we can read the current enumeration values. + // + + } else { + + CdLockFcb( IrpContext, Fcb ); + } + + // + // Capture the current state of the enumeration. + // + // If the user specified an index then use his offset. We always + // return the next entry in this case. + // + + if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) { + + KnownOffset = FALSE; + DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex; + *ReturnNextEntry = TRUE; + + // + // If we are restarting the scan then go from the self entry. + // + + } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) { + + KnownOffset = TRUE; + DirentOffset = Fcb->StreamOffset; + *ReturnNextEntry = FALSE; + + // + // Otherwise use the values from the Ccb. + // + + } else { + + KnownOffset = TRUE; + DirentOffset = Ccb->CurrentDirentOffset; + *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + } + + // + // Unlock the Fcb. + // + + CdUnlockFcb( IrpContext, Fcb ); + + // + // We have the starting offset in the directory and whether to return + // that entry or the next. If we are at the beginning of the directory + // and are returning that entry, then tell our caller this is the + // initial query. + // + + *InitialQuery = FALSE; + + if ((DirentOffset == Fcb->StreamOffset) && + !(*ReturnNextEntry)) { + + *InitialQuery = TRUE; + } + + // + // If there is no file object then create it now. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Determine the offset in the stream to position the FileContext and + // whether this offset is known to be a file offset. + // + // If this offset is known to be safe then go ahead and position the + // file context. This handles the cases where the offset is the beginning + // of the stream, the offset is from a previous search or this is the + // initial query. + // + + if (KnownOffset) { + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset ); + + // + // Otherwise we walk through the directory from the beginning until + // we reach the entry which contains this offset. + // + + } else { + + LastDirentOffset = Fcb->StreamOffset; + Found = TRUE; + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); + + // + // If the requested offset is prior to the beginning offset in the stream + // then don't return the next entry. + // + + if (DirentOffset < LastDirentOffset) { + + *ReturnNextEntry = FALSE; + + // + // Else look for the last entry which ends past the desired index. + // + + } else { + + // + // Keep walking through the directory until we run out of + // entries or we find an entry which ends beyond the input + // index value. + // + + do { + + // + // If we have passed the index value then exit. + // + + if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) { + + Found = FALSE; + break; + } + + // + // Remember the current position in case we need to go back. + // + + LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset; + + // + // Exit if the next entry is beyond the desired index value. + // + + if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) { + + break; + } + + Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ); + + } while (Found); + + // + // If we didn't find the entry then go back to the last known entry. + // This can happen if the index lies in the unused range at the + // end of a sector. + // + + if (!Found) { + + CdCleanupFileContext( IrpContext, FileContext ); + CdInitializeFileContext( IrpContext, FileContext ); + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); + } + } + } + + // + // Only update the dirent name if we will need it for some reason. + // Don't update this name if we are returning the next entry and + // the search string has a version component. + // + + FileContext->ShortName.FileName.Length = 0; + + if (!(*ReturnNextEntry) || + (Ccb->SearchExpression.VersionString.Length == 0)) { + + // + // Update the name in the dirent into filename and version components. + // + + CdUpdateDirentName( IrpContext, + &FileContext->InitialDirent->Dirent, + FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); + } + + // + // Look at the flag in the IrpSp indicating whether to return just + // one entry. + // + + *ReturnSingleEntry = FALSE; + + if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) { + + *ReturnSingleEntry = TRUE; + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdEnumerateIndex ( + IN PIRP_CONTEXT IrpContext, + IN PCCB Ccb, + IN OUT PFILE_ENUM_CONTEXT FileContext, + IN BOOLEAN ReturnNextEntry + ) + +/*++ + +Routine Description: + + This routine is the worker routine for index enumeration. We are positioned + at some dirent in the directory and will either return the first match + at that point or look to the next entry. The Ccb contains details about + the type of matching to do. If the user didn't specify a version in + his search string then we only return the first version of a sequence + of files with versions. We also don't return any associated files. + +Arguments: + + Ccb - Ccb for this directory handle. + + FileContext - File context already positioned at some entry in the directory. + + ReturnNextEntry - Indicates if we are returning this entry or should start + with the next entry. + +Return Value: + + BOOLEAN - TRUE if next entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT PreviousDirent = NULL; + PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Loop until we find a match or exaust the directory. + // + + while (TRUE) { + + // + // Move to the next entry unless we want to consider the current + // entry. + // + + if (ReturnNextEntry) { + + if (!CdLookupNextInitialFileDirent( IrpContext, Ccb->Fcb, FileContext )) { + + break; + } + + PreviousDirent = ThisDirent; + ThisDirent = &FileContext->InitialDirent->Dirent; + + CdUpdateDirentName( IrpContext, ThisDirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); + + } else { + + ReturnNextEntry = TRUE; + } + + // + // Don't bother if we have a constant entry and are ignoring them. + // + + if (FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) && + FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) { + + continue; + } + + // + // Look at the current entry if it is not an associated file + // and the name doesn't match the previous file if the version + // name is not part of the search. + // + + if (!FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { + + // + // Check if this entry matches the previous entry except + // for version number and whether we should return the + // entry in that case. Go directly to the name comparison + // if: + // + // There is no previous entry. + // The search expression has a version component. + // The name length doesn't match the length of the previous entry. + // The base name strings don't match. + // + + if ((PreviousDirent == NULL) || + (Ccb->SearchExpression.VersionString.Length != 0) || + (PreviousDirent->CdCaseFileName.FileName.Length != ThisDirent->CdCaseFileName.FileName.Length) || + FlagOn( PreviousDirent->DirentFlags, CD_ATTRIBUTE_ASSOC ) || + !RtlEqualMemory( PreviousDirent->CdCaseFileName.FileName.Buffer, + ThisDirent->CdCaseFileName.FileName.Buffer, + ThisDirent->CdCaseFileName.FileName.Length )) { + + // + // If we match all names then return to our caller. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) { + + FileContext->ShortName.FileName.Length = 0; + Found = TRUE; + break; + } + + // + // Check if the long name matches the search expression. + // + + if (CdIsNameInExpression( IrpContext, + &ThisDirent->CdCaseFileName, + &Ccb->SearchExpression, + Ccb->Flags, + TRUE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + FileContext->ShortName.FileName.Length = 0; + break; + } + + // + // The long name didn't match so we need to check for a + // possible short name match. There is no match if the + // long name is 8dot3 or the search expression has a + // version component. Special case the self and parent + // entries. + // + + if ((Ccb->SearchExpression.VersionString.Length == 0) && + !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) && + !CdIs8dot3Name( IrpContext, + ThisDirent->CdFileName.FileName )) { + + CdGenerate8dot3Name( IrpContext, + &ThisDirent->CdCaseFileName.FileName, + ThisDirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Check if this name matches. + // + + if (CdIsNameInExpression( IrpContext, + &FileContext->ShortName, + &Ccb->SearchExpression, + Ccb->Flags, + FALSE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + break; + } + } + } + } + } + + // + // If we found the entry then make sure we walk through all of the + // file dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Ccb->Fcb, FileContext ); + } + + return Found; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/dirsup.c b/reactos/drivers/filesystems/cdfs_new/dirsup.c new file mode 100755 index 00000000000..e3b0cc2b08a --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/dirsup.c @@ -0,0 +1,1863 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirSup.c + +Abstract: + + This module implements the dirent support routines for Cdfs. + + Directories on a CD consist of a number of contiguous sectors on + the disk. File descriptors consist of one or more directory entries + (dirents) within a directory. Files may contain version numbers. If + present all like-named files will be ordered contiguously in the + directory by decreasing version numbers. We will only return the + first of these on a directory query unless the user explicitly + asks for version numbers. Finally dirents will not span sector + boundaries. Unused bytes at the end of a sector will be zero + filled. + + Directory sector: Offset + 2048 + +---------------------------------------------------------------+ + | | | | | | | + | foo;4 | foo;4 | foo;3 | hat | zebra | Zero| + | | | | | | Fill| + | | final | single | | | | + | | extent | extent | | | | + +---------------------------------------------------------------+ + + Dirent operations: + + - Position scan at known offset in directory. Dirent at this + offset must exist and is valid. Used when scanning a directory + from the beginning when the self entry is known to be valid. + Used when positioning at the first dirent for an open + file to scan the allocation information. Used when resuming + a directory enumeration from a valid directory entry. + + - Position scan at known offset in directory. Dirent is known to + start at this position but must be checked for validity. + Used to read the self-directory entry. + + - Move to the next dirent within a directory. + + - Given a known starting dirent, collect all the dirents for + that file. Scan will finish positioned at the last dirent + for the file. We will accumulate the extent lengths to + find the size of the file. + + - Given a known starting dirent, position the scan for the first + dirent of the following file. Used when not interested in + all of the details for the current file and are looking for + the next file. + + - Update a common dirent structure with the details of the on-disk + structure. This is used to smooth out the differences + + - Build the filename (name and version strings) out of the stream + of bytes in the file name on disk. For Joliet disks we will have + to convert to little endian. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP) + +// +// Local macros +// + +// +// PRAW_DIRENT +// CdRawDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIR_ENUM_CONTEXT DirContext +// ); +// + +#define CdRawDirent(IC,DC) \ + Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT ) + +// +// Local support routines +// + +ULONG +CdCheckRawDirentBounds ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT_ENUM_CONTEXT DirContext + ); + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + IN PIRP_CONTEXT IrpContext, + IN PRAW_DIRENT RawDirent, + IN OUT PDIRENT Dirent + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCheckForXAExtent) +#pragma alloc_text(PAGE, CdCheckRawDirentBounds) +#pragma alloc_text(PAGE, CdCleanupFileContext) +#pragma alloc_text(PAGE, CdFindFile) +#pragma alloc_text(PAGE, CdFindDirectory) +#pragma alloc_text(PAGE, CdFindFileByShortName) +#pragma alloc_text(PAGE, CdLookupDirent) +#pragma alloc_text(PAGE, CdLookupLastFileDirent) +#pragma alloc_text(PAGE, CdLookupNextDirent) +#pragma alloc_text(PAGE, CdLookupNextInitialFileDirent) +#pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent) +#pragma alloc_text(PAGE, CdUpdateDirentName) +#endif + + +VOID +CdLookupDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG DirentOffset, + OUT PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine is called to initiate a walk through a directory. We will + position ourselves in the directory at offset DirentOffset. We know that + a dirent begins at this boundary but may have to verify the dirent bounds. + We will call this routine when looking up the first entry of a known + file or verifying the self entry of a directory. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + DirentOffset - This is our target point in the directory. We will map the + page containing this entry and possibly verify the dirent bounds at + this location. + + DirContext - This is the dirent context for this scan. We update it with + the location of the dirent we found. This structure has been initialized + outside of this call. + +Return Value: + + None. + +--*/ + +{ + LONGLONG BaseOffset; + + PAGED_CODE(); + + // + // Initialize the offset of the first dirent we want to map. + // + + DirContext->BaseOffset = SectorTruncate( DirentOffset ); + BaseOffset = DirContext->BaseOffset; + + DirContext->DataLength = SECTOR_SIZE; + + DirContext->SectorOffset = SectorOffset( DirentOffset ); + + // + // Truncate the data length if we are at the end of the file. + // + + if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) { + + DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); + } + + // + // Now map the data at this offset. + // + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + DirContext->DataLength, + TRUE, + &DirContext->Bcb, + &DirContext->Sector ); + + // + // Verify the dirent bounds. + // + + DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + DirContext ); + + return; +} + + +BOOLEAN +CdLookupNextDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT CurrentDirContext, + OUT PDIRENT_ENUM_CONTEXT NextDirContext + ) + +/*++ + +Routine Description: + + This routine is called to find the next dirent in the directory. The + current position is given and we look for the next. We leave the context + for the starting position untouched and update the context for the + dirent we found. The target context may already be initialized so we + may already have the sector in memory. + + This routine will position the enumeration context for the next dirent and + verify the dirent bounds. + + NOTE - This routine can be called with CurrentDirContext and NextDirContext + pointing to the same enumeration context. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + CurrentDirContext - This is the dirent context for this scan. We update + it with the location of the dirent we found. This is currently + pointing to a dirent location. The dirent bounds at this location + have already been verified. + + NextDirContext - This is the dirent context to update with the dirent we + find. This may already point to a dirent so we need to check if + we are in the same sector and unmap any buffer as necessary. + + This dirent is left in an indeterminant state if we don't find a dirent. + +Return Value: + + BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise. + This routine can cause a raise if the directory is corrupt. + +--*/ + +{ + LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset; + ULONG TempUlong; + + BOOLEAN FoundDirent = FALSE; + + PAGED_CODE(); + + // + // Check if a different sector is mapped. If so then move our target + // enumeration context to the same sector. + // + + if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) || + (NextDirContext->Bcb == NULL)) { + + // + // Unpin the current target Bcb and map the next sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + CurrentDirContext->DataLength, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + // + // Copy the data length and sector offset. + // + + NextDirContext->DataLength = CurrentDirContext->DataLength; + NextDirContext->BaseOffset = CurrentDirContext->BaseOffset; + } + + // + // Now move to the same offset in the sector. + // + + NextDirContext->SectorOffset = CurrentDirContext->SectorOffset; + + // + // If the value is zero then unmap the current sector and set up + // the base offset to the beginning of the next sector. + // + + if (CurrentDirContext->NextDirentOffset == 0) { + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + + // + // Unmap the current sector. We test the value of the Bcb in the + // loop below to see if we need to read in another sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + // + // There is another possible dirent in the current sector. Update the + // enumeration context to reflect this. + // + + } else { + + NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset; + } + + // + // Now loop until we find the next possible dirent or walk off the directory. + // + + while (TRUE) { + + // + // If we don't currently have a sector mapped then map the + // directory at the current offset. + // + + if (NextDirContext->Bcb == NULL) { + + TempUlong = SECTOR_SIZE; + + if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) { + + TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset); + + // + // If the length is zero then there is no dirent. + // + + if (TempUlong == 0) { + + break; + } + } + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + TempUlong, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset; + NextDirContext->SectorOffset = 0; + NextDirContext->DataLength = TempUlong; + } + + // + // The CDFS spec allows for sectors in a directory to contain all zeroes. + // In this case we need to move to the next sector. So look at the + // current potential dirent for a zero length. Move to the next + // dirent if length is zero. + // + + if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) { + + FoundDirent = TRUE; + break; + } + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + } + + // + // Check the dirent bounds if we found a dirent. + // + + if (FoundDirent) { + + NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + NextDirContext ); + } + + return FoundDirent; +} + + +VOID +CdUpdateDirentFromRawDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT DirContext, + IN OUT PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to safely copy the data from the dirent on disk + to the in-memory dirent. The fields on disk are unaligned so we + need to safely copy them to our structure. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + DirContext - Enumeration context for the raw disk dirent. + + Dirent - In-memory dirent to update. + +Return Value: + + None. + +--*/ + +{ + PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext ); + + PAGED_CODE(); + + // + // Clear all of the current state flags except the flag indicating that + // we allocated a name string. + // + + ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT ); + + // + // The dirent offset is the sum of the start of the sector and the + // sector offset. + // + + Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset; + + // + // Copy the dirent length from the raw dirent. + // + + Dirent->DirentLength = RawDirent->DirLen; + + // + // The starting offset on disk is computed by finding the starting + // logical block and stepping over the Xar block. + // + + CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc ); + + Dirent->StartingOffset += RawDirent->XarLen; + + // + // Do a safe copy to get the data length. + // + + CopyUchar4( &Dirent->DataLength, RawDirent->DataLen ); + + // + // Save a pointer to the time stamps. + // + + Dirent->CdTime = RawDirent->RecordTime; + + // + // Copy the dirent flags. + // + + Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent ); + + // + // For both the file unit and interleave skip we want to take the + // logical block count. + // + + Dirent->FileUnitSize = + Dirent->InterleaveGapSize = 0; + + if (RawDirent->IntLeaveSize != 0) { + + Dirent->FileUnitSize = RawDirent->IntLeaveSize; + Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip; + } + + // + // Get the name length and remember a pointer to the start of the + // name string. We don't do any processing on the name at this + // point. + // + // Check that the name length is non-zero. + // + + if (RawDirent->FileIdLen == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + Dirent->FileNameLen = RawDirent->FileIdLen; + Dirent->FileName = RawDirent->FileId; + + // + // If there are any remaining bytes at the end of the dirent then + // there may be a system use area. We protect ourselves from + // disks which don't pad the dirent entries correctly by using + // a fudge factor of one. All system use areas must have a length + // greater than one. Don't bother with the system use area + // if this is a directory. + // + + Dirent->XAAttributes = 0; + Dirent->XAFileNumber = 0; + Dirent->ExtentType = Form1Data; + Dirent->SystemUseOffset = 0; + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) && + (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) { + + Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen ); + } + + return; +} + + +VOID +CdUpdateDirentName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN ULONG IgnoreCase + ) + +/*++ + +Routine Description: + + This routine is called to update the name in the dirent with the name + from the disk. We will look for the special case of the self and + parent entries and also construct the Unicode name for the Joliet disk + in order to work around the BigEndian on-disk structure. + +Arguments: + + Dirent - Pointer to the in-memory dirent structure. + + IgnoreCase - TRUE if we should build the upcased version. Otherwise we + use the exact case name. + +Return Value: + + None. + +--*/ + +{ + UCHAR DirectoryValue; + ULONG Length; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check if this is a self or parent entry. There is no version number + // in these cases. We use a fixed string for these. + // + // Self-Entry - Length is 1, value is 0. + // Parent-Entry - Length is 1, value is 1. + // + + if ((Dirent->FileNameLen == 1) && + FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + DirectoryValue = *((PCHAR) Dirent->FileName); + + if ((DirectoryValue == 0) || (DirectoryValue == 1)) { + + // + // We should not have allocated a name by the time we see these cases. + // If we have, this means that the image is in violation of ISO 9660 7.6.2, + // which states that the ./.. entries must be the first two in the directory. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now use one of the hard coded directory names. + // + + Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue]; + + // + // Show that there is no version number. + // + + Dirent->CdFileName.VersionString.Length = 0; + + // + // The case name is the same as the exact name. + // + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Mark this as a constant value entry. + // + + SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); + + // + // Return now. + // + + return; + } + } + + // + // Mark this as a non-constant value entry. + // + + ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); + + // + // Compute how large a buffer we will need. If this is an ignore + // case operation then we will want a double size buffer. If the disk is not + // a Joliet disk then we might need two bytes for each byte in the name. + // + + Length = Dirent->FileNameLen; + + if (IgnoreCase) { + + Length *= 2; + } + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Length *= sizeof( WCHAR ); + } + + // + // Now decide if we need to allocate a new buffer. We will if + // this name won't fit in the embedded name buffer and it is + // larger than the current allocated buffer. We always use the + // allocated buffer if present. + // + // If we haven't allocated a buffer then use the embedded buffer if the data + // will fit. This is the typical case. + // + + if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) && + (Length <= sizeof( Dirent->NameBuffer ))) { + + Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer ); + Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer; + + } else { + + // + // We need to use an allocated buffer. Check if the current buffer + // is large enough. + // + + if (Length > Dirent->CdFileName.FileName.MaximumLength) { + + // + // Free any allocated buffer. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { + + CdFreePool( &Dirent->CdFileName.FileName.Buffer ); + ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + } + + Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + Length, + TAG_DIRENT_NAME ); + + SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + + Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length; + } + } + + // + // We now have a buffer for the name. We need to either convert the on-disk bigendian + // to little endian or covert the name to Unicode. + // + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength, + &Length, + Dirent->FileName, + Dirent->FileNameLen ); + + ASSERT( Status == STATUS_SUCCESS ); + Dirent->CdFileName.FileName.Length = (USHORT) Length; + + } else { + + // + // Convert this string to little endian. + // + + CdConvertBigToLittleEndian( IrpContext, + Dirent->FileName, + Dirent->FileNameLen, + (PCHAR) Dirent->CdFileName.FileName.Buffer ); + + Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen; + } + + // + // Split the name into name and version strings. + // + + CdConvertNameToCdName( IrpContext, + &Dirent->CdFileName ); + + // + // The name length better be non-zero. + // + + if (Dirent->CdFileName.FileName.Length == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If the filename ends with a period then back up one character. + // + + if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { + + // + // Slide the version string down. + // + + if (Dirent->CdFileName.VersionString.Length != 0) { + + PWCHAR NewVersion; + + // + // Start from the position currently containing the separator. + // + + NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.Length, + PWCHAR ); + + // + // Now overwrite the period. + // + + RtlMoveMemory( NewVersion - 1, + NewVersion, + Dirent->CdFileName.VersionString.Length + sizeof( WCHAR )); + + // + // Now point to the new version string. + // + + Dirent->CdFileName.VersionString.Buffer = NewVersion; + } + + // + // Shrink the filename length. + // + + Dirent->CdFileName.FileName.Length -= sizeof( WCHAR ); + } + + // + // If this an exact case operation then use the filename exactly. + // + + if (!IgnoreCase) { + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Otherwise perform our upcase operation. We already have guaranteed the buffers are + // there. + // + + } else { + + Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength / 2, + PWCHAR); + + Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2; + + CdUpcaseName( IrpContext, + &Dirent->CdFileName, + &Dirent->CdCaseFileName ); + } + + return; +} + + +BOOLEAN +CdFindFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext, + OUT PCD_NAME *MatchingName + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a file matching the input + name. This name has been upcased at this point if this a case-insensitive + search. The name has been separated into separate name and version strings. + We look for an exact match in the name and only consider the version if + there is a version specified in the search name. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + + MatchingName - Pointer to buffer containing matching name. We need this + in case we don't match the name in the directory but match the + short name instead. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + ULONG ShortNameDirentOffset; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Check to see whether we need to check for a possible short name. + // + + ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName ); + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only consider files which don't have the associated bit set. + // We also only look for files. All directories would already + // have been found. + // + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Don't bother with constant entries. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + continue; + } + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + *MatchingName = &Dirent->CdCaseFileName; + Found = TRUE; + break; + } + + // + // The names didn't match. If the input name is a possible short + // name and we are at the correct offset in the directory then + // check if the short names match. + // + + if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) && + (Name->VersionString.Length == 0) && + !CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + // + // Create the short name and check for a match. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &FileContext->ShortName, + Name, + 0, + FALSE )) { + + *MatchingName = &FileContext->ShortName, + Found = TRUE; + break; + } + } + } + + // + // Go to the next initial dirent for a file. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdFindDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a directory matching the input + name. This name has been upcased at this point if this a case-insensitive + search. We look for an exact match in the name and do not look for shortname + equivalents. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only look for directories. Directories cannot have the + // associated bit set. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Don't bother with constant entries. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + continue; + } + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + Found = TRUE; + break; + } + } + + // + // Go to the next initial dirent. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + return Found; +} + + +BOOLEAN +CdFindFileByShortName ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN ULONG ShortNameDirentOffset, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to find the file name entry whose short name + is defined by the input DirentOffset. The dirent offset here is + multiplied by 32 and we look for the dirent begins in this 32 byte offset in + directory. The minimum dirent length is 34 so we are guaranteed that only + one dirent can begin in each 32 byte block in the directory. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name we are trying to match. We know this contains the tilde + character followed by decimal characters. + + IgnoreCase - Indicates whether we need to upcase the long name and + generated short name. + + ShortNameDirentOffset - This is the shifted value for the offset of the + name in the directory. + + FileContext - This is the initialized file context to use for the search. + +Return Value: + + BOOLEAN - TRUE if a matching name was found, FALSE otherwise. + +--*/ + +{ + BOOLEAN Found = FALSE; + PDIRENT Dirent; + + ULONG ThisShortNameDirentOffset; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Position ourselves at the start of the directory and update + // + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop until we have found the entry or are beyond this dirent. + // + + do { + + // + // Compute the short name dirent offset for the current dirent. + // + + Dirent = &FileContext->InitialDirent->Dirent; + ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT; + + // + // If beyond the target then exit. + // + + if (ThisShortNameDirentOffset > ShortNameDirentOffset) { + + break; + } + + // + // If equal to the target then check if we have a name match. + // We will either match or fail here. + // + + if (ThisShortNameDirentOffset == ShortNameDirentOffset) { + + // + // If this is an associated file then get out. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { + + break; + } + + // + // Update the name in the dirent and check if it is not + // an 8.3 name. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + if (CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + break; + } + + // + // Generate the 8.3 name see if it matches our input name. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Check if this name matches. + // + + if (CdIsNameInExpression( IrpContext, + Name, + &FileContext->ShortName, + 0, + FALSE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + } + + // + // Break out of the loop. + // + + break; + } + + // + // Continue until there are no more entries. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdLookupNextInitialFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to walk through the directory until we find the + first possible dirent for file. We are positioned at some point described + by the FileContext. We will walk through any remaing dirents for the + current file until we find the first dirent for some subsequent file. + + We can be called when we have found just one dirent for a file or all + of them. We first check the CurrentDirContext. In the typical + single-extent case this is unused. Then we look to the InitialDirContext + which must be initialized. + + This routine will save the initial DirContext to the PriorDirContext and + clean up any existing DirContext for the Prior or Current positions in + the enumeration context. + +Arguments: + + Fcb - This is the directory to scan. + + FileContext - This is the file enumeration context. It is currently pointing + at some file in the directory. + +Return Value: + +--*/ + +{ + PRAW_DIRENT RawDirent; + + PDIRENT_ENUM_CONTEXT CurrentDirContext; + PDIRENT_ENUM_CONTEXT TargetDirContext; + PCOMPOUND_DIRENT TempDirent; + + BOOLEAN FoundDirent = FALSE; + BOOLEAN FoundLastDirent; + + PAGED_CODE(); + + // + // Start by saving the initial dirent of the current file as the + // previous file. + // + + TempDirent = FileContext->PriorDirent; + FileContext->PriorDirent = FileContext->InitialDirent; + FileContext->InitialDirent = TempDirent; + + // + // We will use the initial dirent of the prior file unless the + // previous search returned multiple extents. + // + + CurrentDirContext = &FileContext->PriorDirent->DirContext; + + if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) { + + CurrentDirContext = &FileContext->CurrentDirent->DirContext; + } + + // + // Clear all of the flags and file size for the next file. + // + + FileContext->Flags = 0; + FileContext->FileSize = 0; + + FileContext->ShortName.FileName.Length = 0; + + // + // We always want to store the result into the updated initial dirent + // context. + // + + TargetDirContext = &FileContext->InitialDirent->DirContext; + + // + // Loop until we find the first dirent after the last dirent of the + // current file. We may not be at the last dirent for the current file yet + // so we may walk forward looking for the last and then find the + // initial dirent for the next file after that. + // + + while (TRUE) { + + // + // Remember if the last dirent we visited was the last dirent for + // a file. + // + + RawDirent = CdRawDirent( IrpContext, CurrentDirContext ); + + FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI ); + + // + // Try to find another dirent. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + CurrentDirContext, + TargetDirContext ); + + // + // Exit the loop if no entry found. + // + + if (!FoundDirent) { + + break; + + } + + // + // Update the in-memory dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + TargetDirContext, + &FileContext->InitialDirent->Dirent ); + + // + // Exit the loop if we had the end for the previous file. + // + + if (FoundLastDirent) { + + break; + } + + // + // Always use a single dirent from this point on. + // + + CurrentDirContext = TargetDirContext; + } + + return FoundDirent; +} + + +VOID +CdLookupLastFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called when we've found the matching initial dirent for + a file. Now we want to find all of the dirents for a file as well as + compute the running total for the file size. + + We also go out to the system use area and check whether this is an + XA sector. In that case we will compute the real file size. + + The dirent in the initial compound dirent has been updated from the + raw dirent when this routine is called. + +Arguments: + + Fcb - Directory containing the entries for the file. + + FileContext - Enumeration context for this search. It currently points + to the first dirent of the file and the in-memory dirent has been + updated. + +Return Value: + + None. This routine may raise STATUS_FILE_CORRUPT. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType; + PCOMPOUND_DIRENT CurrentCompoundDirent; + PDIRENT CurrentDirent; + + BOOLEAN FirstPass = TRUE; + BOOLEAN FoundDirent; + + PAGED_CODE(); + + // + // The current dirent to look at is the initial dirent for the file. + // + + CurrentCompoundDirent = FileContext->InitialDirent; + + // + // Loop until we reach the last dirent for the file. + // + + while (TRUE) { + + CurrentDirent = &CurrentCompoundDirent->Dirent; + + // + // Check if this extent has XA sectors. + // + + if ((CurrentDirent->SystemUseOffset != 0) && + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) && + CdCheckForXAExtent( IrpContext, + CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ), + CurrentDirent )) { + + // + // Any previous dirent must describe XA sectors as well. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If there are XA sectors then the data on the disk must + // be correctly aligned on sectors and be an integral number of + // sectors. Only an issue if the logical block size is not + // 2048. + // + + if (Fcb->Vcb->BlockSize != SECTOR_SIZE) { + + // + // We will do the following checks. + // + // Data must start on a sector boundary. + // Data length must be integral number of sectors. + // + + if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If interleaved then both the file unit and interleave + // gap must be integral number of sectors. + // + + if ((CurrentDirent->FileUnitSize != 0) && + ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + } + + // + // If this is the first dirent then add the bytes for the RIFF + // header. + // + + if (FirstPass) { + + FileContext->FileSize = sizeof( RIFF_HEADER ); + } + + // + // Add the size of the mode2-form2 sector for each sector + // we have here. + // + + FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT, + XA_SECTOR_SIZE); + + } else { + + // + // This extent does not have XA sectors. Any previous dirent + // better not have XA sectors. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Add these bytes to the file size. + // + + FileContext->FileSize += CurrentDirent->DataLength; + } + + // + // If we are at the last dirent then exit. + // + + if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + // + // Remember the extent type of the current extent. + // + + ExtentType = CurrentDirent->ExtentType; + + // + // Look for the next dirent of the file. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &FileContext->CurrentDirent->DirContext ); + + // + // If we didn't find the entry then this is a corrupt directory. + // + + if (!FoundDirent) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Remember the dirent we just found. + // + + CurrentCompoundDirent = FileContext->CurrentDirent; + FirstPass = FALSE; + + // + // Look up all of the dirent information for the given dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &CurrentCompoundDirent->Dirent ); + + // + // Set flag to show there were multiple extents. + // + + SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS ); + } + + return; +} + + +VOID +CdCleanupFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to cleanup the enumeration context for a file + search in a directory. We will unpin any remaining Bcbs and free + any allocated buffers. + +Arguments: + + FileContext - Enumeration context for the file search. + +Return Value: + + None. + +--*/ + +{ + PCOMPOUND_DIRENT CurrentCompoundDirent; + ULONG Count = 2; + + PAGED_CODE(); + + // + // Cleanup the individual compound dirents. + // + + do { + + CurrentCompoundDirent = &FileContext->Dirents[ Count ]; + CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext ); + CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent ); + + } while (Count--); + + return; +} + + +// +// Local support routine +// + +ULONG +CdCheckRawDirentBounds ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine takes a Dirent enumeration context and computes the offset + to the next dirent. A non-zero value indicates the offset within this + sector. A zero value indicates to move to the next sector. If the + current dirent does not fit within the sector then we will raise + STATUS_CORRUPT. + +Arguments: + + DirContext - Enumeration context indicating the current position in + the sector. + +Return Value: + + ULONG - Offset to the next dirent in this sector or zero if the + next dirent is in the next sector. + + This routine will raise on a dirent which does not fit into the + described data buffer. + +--*/ + +{ + ULONG NextDirentOffset; + PRAW_DIRENT RawDirent; + + PAGED_CODE(); + + // + // We should always have at least a byte still available in the + // current buffer. + // + + ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 ); + + // + // Get a pointer to the current dirent. + // + + RawDirent = CdRawDirent( IrpContext, DirContext ); + + // + // If the dirent length is non-zero then look at the current dirent. + // + + if (RawDirent->DirLen != 0) { + + // + // Check the following bound for the dirent length. + // + // - Fits in the available bytes in the sector. + // - Is at least the minimal dirent size. + // - Is large enough to hold the file name. + // + + if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) || + (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) || + (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Copy the dirent length field. + // + + NextDirentOffset = RawDirent->DirLen; + + // + // If we are exactly at the next sector then tell our caller by + // returning zero. + // + + if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) { + + NextDirentOffset = 0; + } + + } else { + + NextDirentOffset = 0; + } + + return NextDirentOffset; +} + + +// +// Local support routine +// + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + IN PIRP_CONTEXT IrpContext, + IN PRAW_DIRENT RawDirent, + IN OUT PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to scan through the system use area to test if + the current dirent has the XA bit set. The bit in the in-memory + dirent will be set as appropriate. + +Arguments: + + RawDirent - Pointer to the on-disk dirent. + + Dirent - Pointer to the in-memory dirent. We will update this with the + appropriate XA flag. + +Return Value: + + XA_EXTENT_TYPE - Type of physical extent for this on disk dirent. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType = Form1Data; + PSYSTEM_USE_XA SystemUseArea; + + PAGED_CODE(); + + // + // Check if there is enough space for the XA system use area. + // + + if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) { + + SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA ); + + // + // Check for a valid signature. + // + + if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) { + + // + // Check for an audio track. + // + + if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) { + + ExtentType = CDAudio; + + } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) { + + // + // Check for XA data. Note that a number of discs (video CDs) + // have files marked as type XA Mode 2 Form 1 (2048 bytes of + // user data), but actually record these sectors as Mode2 Form 2 + // (2352). We will fail to read these files, since for M2F1, + // a normal read CD command is issued (as per SCSI specs). + // + + ExtentType = Mode2Form2Data; + } + + Dirent->XAAttributes = SystemUseArea->Attributes; + Dirent->XAFileNumber = SystemUseArea->FileNumber; + } + } + + Dirent->ExtentType = ExtentType; + return ExtentType; +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/fieldoff.c b/reactos/drivers/filesystems/cdfs_new/fieldoff.c new file mode 100755 index 00000000000..c307c108347 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/fieldoff.c @@ -0,0 +1,478 @@ +#include "CdProcs.h" +#include + +#define doit(a,b) { printf("%s %04lx %4lx %s\n", #a, FIELD_OFFSET(a,b), sizeof(d.b), #b); } + +VOID +__cdecl +main (argc, argv) + int argc; + char *argv[]; +{ + printf(" \n\n"); + { + CD_MCB d; + doit( CD_MCB, MaximumEntryCount ); + doit( CD_MCB, CurrentEntryCount ); + doit( CD_MCB, McbArray ); + } + printf("\n"); + { + CD_MCB_ENTRY d; + doit( CD_MCB_ENTRY, DiskOffset ); + doit( CD_MCB_ENTRY, ByteCount ); + doit( CD_MCB_ENTRY, FileOffset ); + doit( CD_MCB_ENTRY, DataBlockByteCount ); + doit( CD_MCB_ENTRY, TotalBlockByteCount ); + } + printf("\n"); + { + CD_NAME d; + doit( CD_NAME, FileName ); + doit( CD_NAME, VersionString ); + } + printf("\n"); + { + NAME_LINK d; + doit( NAME_LINK, Links ); + doit( NAME_LINK, FileName ); + } + printf("\n"); + { + PREFIX_ENTRY d; + doit( PREFIX_ENTRY, Fcb ); + doit( PREFIX_ENTRY, PrefixFlags ); + doit( PREFIX_ENTRY, ExactCaseName ); + doit( PREFIX_ENTRY, IgnoreCaseName ); + doit( PREFIX_ENTRY, FileNameBuffer ); + } + printf("\n"); + { + CD_DATA d; + doit( CD_DATA, NodeTypeCode ); + doit( CD_DATA, NodeByteSize ); + doit( CD_DATA, DriverObject ); + doit( CD_DATA, VcbQueue ); + doit( CD_DATA, IrpContextDepth ); + doit( CD_DATA, IrpContextMaxDepth ); + doit( CD_DATA, IrpContextList ); + doit( CD_DATA, FileSystemDeviceObject ); + doit( CD_DATA, AsyncCloseQueue ); + doit( CD_DATA, AsyncCloseCount ); + doit( CD_DATA, FspCloseActive ); + doit( CD_DATA, ReduceDelayedClose ); + doit( CD_DATA, PadUshort ); + doit( CD_DATA, DelayedCloseQueue ); + doit( CD_DATA, DelayedCloseCount ); + doit( CD_DATA, MinDelayedCloseCount ); + doit( CD_DATA, MaxDelayedCloseCount ); + doit( CD_DATA, CdDataLockThread ); + doit( CD_DATA, CdDataMutex ); + doit( CD_DATA, DataResource ); + doit( CD_DATA, CacheManagerCallbacks ); + doit( CD_DATA, CacheManagerVolumeCallbacks ); + doit( CD_DATA, CloseItem ); + } + printf("\n"); + { + VCB d; + doit( VCB, NodeTypeCode ); + doit( VCB, NodeByteSize ); + doit( VCB, Vpb ); + doit( VCB, TargetDeviceObject ); + doit( VCB, VolumeLockFileObject ); + doit( VCB, VcbLinks ); + doit( VCB, VcbState ); + doit( VCB, VcbCondition ); + doit( VCB, VcbCleanup ); + doit( VCB, VcbReference ); + doit( VCB, VcbUserReference ); + doit( VCB, VolumeDasdFcb ); + doit( VCB, RootIndexFcb ); + doit( VCB, PathTableFcb ); + doit( VCB, BaseSector ); + doit( VCB, VdSectorOffset ); + doit( VCB, PrimaryVdSectorOffset ); + doit( VCB, XASector ); + doit( VCB, XADiskOffset ); + doit( VCB, VcbResource ); + doit( VCB, FileResource ); + doit( VCB, VcbMutex ); + doit( VCB, VcbLockThread ); + doit( VCB, NotifySync ); + doit( VCB, DirNotifyList ); + doit( VCB, BlockSize ); + doit( VCB, BlockToSectorShift ); + doit( VCB, BlockToByteShift ); + doit( VCB, BlocksPerSector ); + doit( VCB, BlockMask ); + doit( VCB, BlockInverseMask ); + doit( VCB, FcbTable ); + doit( VCB, CdromToc ); + doit( VCB, TocLength ); + doit( VCB, TrackCount ); + doit( VCB, DiskFlags ); + doit( VCB, BlockFactor ); + } + printf("\n"); + { + VOLUME_DEVICE_OBJECT d; + doit( VOLUME_DEVICE_OBJECT, DeviceObject ); + doit( VOLUME_DEVICE_OBJECT, PostedRequestCount ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueueCount ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueue ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueueSpinLock ); + doit( VOLUME_DEVICE_OBJECT, Vcb ); + } + printf("\n"); + { + FCB_DATA d; + doit( FCB_DATA, Oplock ); + doit( FCB_DATA, FileLock ); + } + printf("\n"); + { + FCB_INDEX d; + doit( FCB_INDEX, FileObject ); + doit( FCB_INDEX, StreamOffset ); + doit( FCB_INDEX, FcbQueue ); + doit( FCB_INDEX, Ordinal ); + doit( FCB_INDEX, ChildPathTableOffset ); + doit( FCB_INDEX, ChildOrdinal ); + doit( FCB_INDEX, ExactCaseRoot ); + doit( FCB_INDEX, IgnoreCaseRoot ); + } + printf("\n"); + { + FCB_NONPAGED d; + doit( FCB_NONPAGED, NodeTypeCode ); + doit( FCB_NONPAGED, NodeByteSize ); + doit( FCB_NONPAGED, SegmentObject ); + doit( FCB_NONPAGED, FcbResource ); + doit( FCB_NONPAGED, FcbMutex ); + } + printf("\n"); + { + FCB d; + doit( FCB, Header ); + doit( FCB, Vcb ); + doit( FCB, ParentFcb ); + doit( FCB, FcbLinks ); + doit( FCB, FileId ); + doit( FCB, FcbCleanup ); + doit( FCB, FcbReference ); + doit( FCB, FcbUserReference ); + doit( FCB, FcbState ); + doit( FCB, FileAttributes ); + doit( FCB, XAAttributes ); + doit( FCB, XAFileNumber ); + doit( FCB, FcbLockThread ); + doit( FCB, FcbLockCount ); + doit( FCB, FcbNonpaged ); + doit( FCB, ShareAccess ); + doit( FCB, McbEntry ); + doit( FCB, Mcb ); + doit( FCB, ShortNamePrefix ); + doit( FCB, FileNamePrefix ); + doit( FCB, CreationTime ); + doit( FCB, FcbType ); + } + printf("\n"); + { + CCB d; + doit( CCB, NodeTypeCode ); + doit( CCB, NodeByteSize ); + doit( CCB, Flags ); + doit( CCB, Fcb ); + doit( CCB, CurrentDirentOffset ); + doit( CCB, SearchExpression ); + } + printf("\n"); + { + IRP_CONTEXT d; + doit( IRP_CONTEXT, NodeTypeCode ); + doit( IRP_CONTEXT, NodeByteSize ); + doit( IRP_CONTEXT, Irp ); + doit( IRP_CONTEXT, Vcb ); + doit( IRP_CONTEXT, ExceptionStatus ); + doit( IRP_CONTEXT, Flags ); + doit( IRP_CONTEXT, RealDevice ); + doit( IRP_CONTEXT, IoContext ); + doit( IRP_CONTEXT, TeardownFcb ); + doit( IRP_CONTEXT, TopLevel ); + doit( IRP_CONTEXT, MajorFunction ); + doit( IRP_CONTEXT, MinorFunction ); + doit( IRP_CONTEXT, ThreadContext ); + doit( IRP_CONTEXT, WorkQueueItem ); + } + printf("\n"); + { + IRP_CONTEXT_LITE d; + doit( IRP_CONTEXT_LITE, NodeTypeCode ); + doit( IRP_CONTEXT_LITE, NodeByteSize ); + doit( IRP_CONTEXT_LITE, Fcb ); + doit( IRP_CONTEXT_LITE, DelayedCloseLinks ); + doit( IRP_CONTEXT_LITE, UserReference ); + doit( IRP_CONTEXT_LITE, RealDevice ); + } + printf("\n"); + { + CD_IO_CONTEXT d; + doit( CD_IO_CONTEXT, IrpCount ); + doit( CD_IO_CONTEXT, MasterIrp ); + doit( CD_IO_CONTEXT, Status ); + doit( CD_IO_CONTEXT, AllocatedContext ); + doit( CD_IO_CONTEXT, Resource ); + doit( CD_IO_CONTEXT, ResourceThreadId ); + doit( CD_IO_CONTEXT, SyncEvent ); + } + printf("\n"); + { + THREAD_CONTEXT d; + doit( THREAD_CONTEXT, Cdfs ); + doit( THREAD_CONTEXT, SavedTopLevelIrp ); + doit( THREAD_CONTEXT, TopLevelIrpContext ); + } + printf("\n"); + { + PATH_ENUM_CONTEXT d; + doit( PATH_ENUM_CONTEXT, Data ); + doit( PATH_ENUM_CONTEXT, BaseOffset ); + doit( PATH_ENUM_CONTEXT, DataLength ); + doit( PATH_ENUM_CONTEXT, Bcb ); + doit( PATH_ENUM_CONTEXT, DataOffset ); + doit( PATH_ENUM_CONTEXT, AllocatedData ); + doit( PATH_ENUM_CONTEXT, LastDataBlock ); + } + printf("\n"); + { + PATH_ENTRY d; + doit( PATH_ENTRY, Ordinal ); + doit( PATH_ENTRY, PathTableOffset ); + doit( PATH_ENTRY, DiskOffset ); + doit( PATH_ENTRY, PathEntryLength ); + doit( PATH_ENTRY, ParentOrdinal ); + doit( PATH_ENTRY, DirNameLen ); + doit( PATH_ENTRY, DirName ); + doit( PATH_ENTRY, Flags ); + doit( PATH_ENTRY, CdDirName ); + doit( PATH_ENTRY, CdCaseDirName ); + doit( PATH_ENTRY, NameBuffer ); + } + printf("\n"); + { + COMPOUND_PATH_ENTRY d; + doit( COMPOUND_PATH_ENTRY, PathContext ); + doit( COMPOUND_PATH_ENTRY, PathEntry ); + } + printf("\n"); + { + DIRENT_ENUM_CONTEXT d; + doit( DIRENT_ENUM_CONTEXT, Sector ); + doit( DIRENT_ENUM_CONTEXT, BaseOffset ); + doit( DIRENT_ENUM_CONTEXT, DataLength ); + doit( DIRENT_ENUM_CONTEXT, Bcb ); + doit( DIRENT_ENUM_CONTEXT, SectorOffset ); + doit( DIRENT_ENUM_CONTEXT, NextDirentOffset ); + } + printf("\n"); + { + DIRENT d; + doit( DIRENT, DirentOffset ); + doit( DIRENT, DirentLength ); + doit( DIRENT, StartingOffset ); + doit( DIRENT, DataLength ); + doit( DIRENT, CdTime ); + doit( DIRENT, DirentFlags ); + doit( DIRENT, Flags ); + doit( DIRENT, FileUnitSize ); + doit( DIRENT, InterleaveGapSize ); + doit( DIRENT, SystemUseOffset ); + doit( DIRENT, XAAttributes ); + doit( DIRENT, XAFileNumber ); + doit( DIRENT, FileNameLen ); + doit( DIRENT, FileName ); + doit( DIRENT, CdFileName ); + doit( DIRENT, CdCaseFileName ); + doit( DIRENT, ExtentType ); + doit( DIRENT, NameBuffer ); + } + printf("\n"); + { + COMPOUND_DIRENT d; + doit( COMPOUND_DIRENT, DirContext ); + doit( COMPOUND_DIRENT, Dirent ); + } + printf("\n"); + { + FILE_ENUM_CONTEXT d; + doit( FILE_ENUM_CONTEXT, PriorDirent ); + doit( FILE_ENUM_CONTEXT, InitialDirent ); + doit( FILE_ENUM_CONTEXT, CurrentDirent ); + doit( FILE_ENUM_CONTEXT, Flags ); + doit( FILE_ENUM_CONTEXT, FileSize ); + doit( FILE_ENUM_CONTEXT, ShortName ); + doit( FILE_ENUM_CONTEXT, ShortNameBuffer ); + doit( FILE_ENUM_CONTEXT, Dirents ); + } + printf("\n"); + { + RIFF_HEADER d; + doit( RIFF_HEADER, ChunkId ); + doit( RIFF_HEADER, ChunkSize ); + doit( RIFF_HEADER, SignatureCDXA ); + doit( RIFF_HEADER, SignatureFMT ); + doit( RIFF_HEADER, XAChunkSize ); + doit( RIFF_HEADER, OwnerId ); + doit( RIFF_HEADER, Attributes ); + doit( RIFF_HEADER, SignatureXA ); + doit( RIFF_HEADER, FileNumber ); + doit( RIFF_HEADER, Reserved ); + doit( RIFF_HEADER, SignatureData ); + doit( RIFF_HEADER, RawSectors ); + } + printf("\n"); + { + AUDIO_PLAY_HEADER d; + doit( AUDIO_PLAY_HEADER, Chunk ); + doit( AUDIO_PLAY_HEADER, ChunkSize ); + doit( AUDIO_PLAY_HEADER, SignatureCDDA ); + doit( AUDIO_PLAY_HEADER, SignatureFMT ); + doit( AUDIO_PLAY_HEADER, FMTChunkSize ); + doit( AUDIO_PLAY_HEADER, FormatTag ); + doit( AUDIO_PLAY_HEADER, TrackNumber ); + doit( AUDIO_PLAY_HEADER, DiskID ); + doit( AUDIO_PLAY_HEADER, StartingSector ); + doit( AUDIO_PLAY_HEADER, SectorCount ); + doit( AUDIO_PLAY_HEADER, TrackAddress ); + doit( AUDIO_PLAY_HEADER, TrackLength ); + } + printf("\n"); + { + RAW_ISO_VD d; + doit( RAW_ISO_VD, DescType ); + doit( RAW_ISO_VD, StandardId ); + doit( RAW_ISO_VD, Version ); + doit( RAW_ISO_VD, VolumeFlags ); + doit( RAW_ISO_VD, SystemId ); + doit( RAW_ISO_VD, VolumeId ); + doit( RAW_ISO_VD, Reserved ); + doit( RAW_ISO_VD, VolSpaceI ); + doit( RAW_ISO_VD, VolSpaceM ); + doit( RAW_ISO_VD, CharSet ); + doit( RAW_ISO_VD, VolSetSizeI ); + doit( RAW_ISO_VD, VolSetSizeM ); + doit( RAW_ISO_VD, VolSeqNumI ); + doit( RAW_ISO_VD, VolSeqNumM ); + doit( RAW_ISO_VD, LogicalBlkSzI ); + doit( RAW_ISO_VD, LogicalBlkSzM ); + doit( RAW_ISO_VD, PathTableSzI ); + doit( RAW_ISO_VD, PathTableSzM ); + doit( RAW_ISO_VD, PathTabLocI ); + doit( RAW_ISO_VD, PathTabLocM ); + doit( RAW_ISO_VD, RootDe ); + doit( RAW_ISO_VD, VolSetId ); + doit( RAW_ISO_VD, PublId ); + doit( RAW_ISO_VD, PreparerId ); + doit( RAW_ISO_VD, AppId ); + doit( RAW_ISO_VD, Copyright ); + doit( RAW_ISO_VD, Abstract ); + doit( RAW_ISO_VD, Bibliograph ); + doit( RAW_ISO_VD, CreateDate ); + doit( RAW_ISO_VD, ModDate ); + doit( RAW_ISO_VD, ExpireDate ); + doit( RAW_ISO_VD, EffectDate ); + doit( RAW_ISO_VD, FileStructVer ); + doit( RAW_ISO_VD, Reserved3 ); + doit( RAW_ISO_VD, ResApp ); + doit( RAW_ISO_VD, Reserved4 ); + } + printf("\n"); + { + RAW_HSG_VD d; + doit( RAW_HSG_VD, BlkNumI ); + doit( RAW_HSG_VD, BlkNumM ); + doit( RAW_HSG_VD, DescType ); + doit( RAW_HSG_VD, StandardId ); + doit( RAW_HSG_VD, Version ); + doit( RAW_HSG_VD, VolumeFlags ); + doit( RAW_HSG_VD, SystemId ); + doit( RAW_HSG_VD, VolumeId ); + doit( RAW_HSG_VD, Reserved ); + doit( RAW_HSG_VD, VolSpaceI ); + doit( RAW_HSG_VD, VolSpaceM ); + doit( RAW_HSG_VD, CharSet ); + doit( RAW_HSG_VD, VolSetSizeI ); + doit( RAW_HSG_VD, VolSetSizeM ); + doit( RAW_HSG_VD, VolSeqNumI ); + doit( RAW_HSG_VD, VolSeqNumM ); + doit( RAW_HSG_VD, LogicalBlkSzI ); + doit( RAW_HSG_VD, LogicalBlkSzM ); + doit( RAW_HSG_VD, PathTableSzI ); + doit( RAW_HSG_VD, PathTableSzM ); + doit( RAW_HSG_VD, PathTabLocI ); + doit( RAW_HSG_VD, PathTabLocM ); + doit( RAW_HSG_VD, RootDe ); + doit( RAW_HSG_VD, VolSetId ); + doit( RAW_HSG_VD, PublId ); + doit( RAW_HSG_VD, PreparerId ); + doit( RAW_HSG_VD, AppId ); + doit( RAW_HSG_VD, Copyright ); + doit( RAW_HSG_VD, Abstract ); + doit( RAW_HSG_VD, CreateDate ); + doit( RAW_HSG_VD, ModDate ); + doit( RAW_HSG_VD, ExpireDate ); + doit( RAW_HSG_VD, EffectDate ); + doit( RAW_HSG_VD, FileStructVer ); + doit( RAW_HSG_VD, Reserved3 ); + doit( RAW_HSG_VD, ResApp ); + doit( RAW_HSG_VD, Reserved4 ); + } + printf("\n"); + { + RAW_DIRENT d; + doit( RAW_DIRENT, DirLen ); + doit( RAW_DIRENT, XarLen ); + doit( RAW_DIRENT, FileLoc ); + doit( RAW_DIRENT, FileLocMot ); + doit( RAW_DIRENT, DataLen ); + doit( RAW_DIRENT, DataLenMot ); + doit( RAW_DIRENT, RecordTime ); + doit( RAW_DIRENT, FlagsHSG ); + doit( RAW_DIRENT, FlagsISO ); + doit( RAW_DIRENT, IntLeaveSize ); + doit( RAW_DIRENT, IntLeaveSkip ); + doit( RAW_DIRENT, Vssn ); + doit( RAW_DIRENT, VssnMot ); + doit( RAW_DIRENT, FileIdLen ); + doit( RAW_DIRENT, FileId ); + } + printf("\n"); + { + RAW_PATH_ISO d; + doit( RAW_PATH_ISO, DirIdLen ); + doit( RAW_PATH_ISO, XarLen ); + doit( RAW_PATH_ISO, DirLoc ); + doit( RAW_PATH_ISO, ParentNum ); + doit( RAW_PATH_ISO, DirId ); + } + printf("\n"); + { + RAW_PATH_HSG d; + doit( RAW_PATH_HSG, DirLoc ); + doit( RAW_PATH_HSG, XarLen ); + doit( RAW_PATH_HSG, DirIdLen ); + doit( RAW_PATH_HSG, ParentNum ); + doit( RAW_PATH_HSG, DirId ); + } + printf("\n"); + { + SYSTEM_USE_XA d; + doit( SYSTEM_USE_XA, OwnerId ); + doit( SYSTEM_USE_XA, Attributes ); + doit( SYSTEM_USE_XA, Signature ); + doit( SYSTEM_USE_XA, FileNumber ); + doit( SYSTEM_USE_XA, Reserved ); + } +} + diff --git a/reactos/drivers/filesystems/cdfs_new/fileinfo.c b/reactos/drivers/filesystems/cdfs_new/fileinfo.c new file mode 100755 index 00000000000..cd6052fc227 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/fileinfo.c @@ -0,0 +1,1501 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FileInfo.c + +Abstract: + + This module implements the File Information routines for Cdfs called by + the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FILEINFO) + +// +// Local support routines +// + +VOID +CdQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PULONG Length + ); + +VOID +CdQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PULONG Length + ); + +VOID +CdQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PULONG Length + ); + +VOID +CdQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PULONG Length + ); + +VOID +CdQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +CdQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +CdQueryAlternateNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +VOID +CdQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonQueryInfo) +#pragma alloc_text(PAGE, CdCommonSetInfo) +#pragma alloc_text(PAGE, CdFastQueryBasicInfo) +#pragma alloc_text(PAGE, CdFastQueryStdInfo) +#pragma alloc_text(PAGE, CdFastQueryNetworkInfo) +#pragma alloc_text(PAGE, CdQueryAlternateNameInfo) +#pragma alloc_text(PAGE, CdQueryBasicInfo) +#pragma alloc_text(PAGE, CdQueryEaInfo) +#pragma alloc_text(PAGE, CdQueryInternalInfo) +#pragma alloc_text(PAGE, CdQueryNameInfo) +#pragma alloc_text(PAGE, CdQueryNetworkInfo) +#pragma alloc_text(PAGE, CdQueryPositionInfo) +#pragma alloc_text(PAGE, CdQueryStandardInfo) +#endif + + +NTSTATUS +CdCommonQueryInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for query file information called by both the + fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + NTSTATUS - The return status for this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + ULONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PFILE_ALL_INFORMATION Buffer; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN ReleaseFcb = FALSE; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryFile.Length; + FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // We only support query on file and directory handles. + // + + switch (TypeOfOpen) { + + case UserDirectoryOpen : + case UserFileOpen : + + // + // Acquire shared access to this file. NOTE that this could be + // a recursive acquire, if we already preacquired in + // CdAcquireForCreateSection(). + // + + CdAcquireFileShared( IrpContext, Fcb ); + ReleaseFcb = TRUE; + + // + // Make sure we have the correct sizes for a directory. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { + + ASSERT( TypeOfOpen == UserDirectoryOpen ); + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Make sure the Fcb is in a usable condition. This will raise + // an error condition if the volume is unusable + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Based on the information class we'll do different + // actions. Each of hte procedures that we're calling fills + // up the output buffer, if possible. They will raise the + // status STATUS_BUFFER_OVERFLOW for an insufficient buffer. + // This is considered a somewhat unusual case and is handled + // more cleanly with the exception mechanism rather than + // testing a return status value for each call. + // + + switch (FileInformationClass) { + + case FileAllInformation: + + // + // We don't allow this operation on a file opened by file Id. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // In this case go ahead and call the individual routines to + // fill in the buffer. Only the name routine will + // pointer to the output buffer and then call the + // individual routines to fill in the buffer. + // + + Length -= (sizeof( FILE_ACCESS_INFORMATION ) + + sizeof( FILE_MODE_INFORMATION ) + + sizeof( FILE_ALIGNMENT_INFORMATION )); + + CdQueryBasicInfo( IrpContext, Fcb, &Buffer->BasicInformation, &Length ); + CdQueryStandardInfo( IrpContext, Fcb, &Buffer->StandardInformation, &Length ); + CdQueryInternalInfo( IrpContext, Fcb, &Buffer->InternalInformation, &Length ); + CdQueryEaInfo( IrpContext, Fcb, &Buffer->EaInformation, &Length ); + CdQueryPositionInfo( IrpContext, IrpSp->FileObject, &Buffer->PositionInformation, &Length ); + Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, &Buffer->NameInformation, &Length ); + + break; + + case FileBasicInformation: + + CdQueryBasicInfo( IrpContext, Fcb, (PFILE_BASIC_INFORMATION) Buffer, &Length ); + break; + + case FileStandardInformation: + + CdQueryStandardInfo( IrpContext, Fcb, (PFILE_STANDARD_INFORMATION) Buffer, &Length ); + break; + + case FileInternalInformation: + + CdQueryInternalInfo( IrpContext, Fcb, (PFILE_INTERNAL_INFORMATION) Buffer, &Length ); + break; + + case FileEaInformation: + + CdQueryEaInfo( IrpContext, Fcb, (PFILE_EA_INFORMATION) Buffer, &Length ); + break; + + case FilePositionInformation: + + CdQueryPositionInfo( IrpContext, IrpSp->FileObject, (PFILE_POSITION_INFORMATION) Buffer, &Length ); + break; + + case FileNameInformation: + + // + // We don't allow this operation on a file opened by file Id. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, (PFILE_NAME_INFORMATION) Buffer, &Length ); + + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + case FileAlternateNameInformation: + + if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = CdQueryAlternateNameInfo( IrpContext, Fcb, Ccb, (PFILE_NAME_INFORMATION) Buffer, &Length ); + + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + case FileNetworkOpenInformation: + + CdQueryNetworkInfo( IrpContext, Fcb, (PFILE_NETWORK_OPEN_INFORMATION) Buffer, &Length ); + break; + + default : + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + default : + + Status = STATUS_INVALID_PARAMETER; + } + + // + // Set the information field to the number of bytes actually filled in + // and then complete the request + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; + + } finally { + + // + // Release the file. + // + + if (ReleaseFcb) { + + CdReleaseFile( IrpContext, Fcb ); + } + } + + // + // Complete the request if we didn't raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +CdCommonSetInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for set file information called by both the + fsd and fsp threads. We only support operations which set the file position. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + NTSTATUS - The return status for this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFILE_POSITION_INFORMATION Buffer; + + PAGED_CODE(); + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // We only support a SetPositionInformation on a user file. + // + + if ((TypeOfOpen != UserFileOpen) || + (IrpSp->Parameters.QueryFile.FileInformationClass != FilePositionInformation)) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Acquire shared access to this file. + // + + CdAcquireFileShared( IrpContext, Fcb ); + + try { + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check if the file does not use intermediate buffering. If it + // does not use intermediate buffering then the new position we're + // supplied must be aligned properly for the device + // + + if (FlagOn( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) && + ((Buffer->CurrentByteOffset.LowPart & Fcb->Vcb->BlockMask) != 0)) { + + try_return( NOTHING ); + } + + // + // The input parameter is fine so set the current byte offset and + // complete the request + // + + // + // Lock the Fcb to provide synchronization. + // + + CdLockFcb( IrpContext, Fcb ); + IrpSp->FileObject->CurrentByteOffset = Buffer->CurrentByteOffset; + CdUnlockFcb( IrpContext, Fcb ); + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } finally { + + CdReleaseFile( IrpContext, Fcb ); + } + + // + // Complete the request if there was no raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +BOOLEAN +CdFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_BASIC_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for basic file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Fill in the input buffer from the Fcb fields. + // + + Buffer->CreationTime.QuadPart = + Buffer->LastWriteTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_BASIC_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +BOOLEAN +CdFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for standard file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on initialized user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED ))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Check whether this is a directory. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + Buffer->Directory = TRUE; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + + Buffer->Directory = FALSE; + } + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = FALSE; + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_STANDARD_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +BOOLEAN +CdFastQueryNetworkInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for network file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Fill in the input buffer from the Fcb fields. + // + + Buffer->CreationTime.QuadPart = + Buffer->LastWriteTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Check whether this is a directory. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + } + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +// +// Local support routine +// + +VOID +CdQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + + Description: + + This routine performs the query basic information function for Cdfs + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // We only support creation, last modify and last write times on Cdfs. + // + + Buffer->LastWriteTime.QuadPart = + Buffer->CreationTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_BASIC_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PULONG Length + ) +/*++ + +Routine Description: + + This routine performs the query standard information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // There is only one link and delete is never pending on a Cdrom file. + // + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = FALSE; + + // + // We get the sizes from the header. Return a size of zero + // for all directories. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + Buffer->Directory = TRUE; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + + Buffer->Directory = FALSE; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_STANDARD_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query internal information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Index number is the file Id number in the Fcb. + // + + Buffer->IndexNumber = Fcb->FileId; + *Length -= sizeof( FILE_INTERNAL_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query Ea information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // No Ea's on Cdfs volumes. + // + + Buffer->EaSize = 0; + *Length -= sizeof( FILE_EA_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query position information function for cdfs. + +Arguments: + + FileObject - Supplies the File object being queried + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Get the current position found in the file object. + // + + Buffer->CurrentByteOffset = FileObject->CurrentByteOffset; + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_POSITION_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query name information function for cdfs. + +Arguments: + + FileObject - Supplies the file object containing the name. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + NTSTATUS - STATUS_BUFFER_OVERFLOW if the entire name can't be copied. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG LengthToCopy; + + PAGED_CODE(); + + ASSERT(*Length >= sizeof(ULONG)); + + // + // Simply copy the name in the file object to the user's buffer. + // + + // + // Place the size of the filename in the user's buffer and reduce the remaining + // size to match. + // + + Buffer->FileNameLength = LengthToCopy = FileObject->FileName.Length; + *Length -= sizeof(ULONG); + + if (LengthToCopy > *Length) { + + LengthToCopy = *Length; + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( Buffer->FileName, FileObject->FileName.Buffer, LengthToCopy ); + + // + // Reduce the available bytes by the amount stored into this buffer. In the overflow + // case, this simply drops to zero. The returned filenamelength will indicate to the + // caller how much space is required. + // + + *Length -= LengthToCopy; + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryAlternateNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query alternate name information function. + We lookup the dirent for this file and then check if there is a + short name. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified. + + Ccb - Ccb for this open handle. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned. + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer, + STATUS_OBJECT_NAME_NOT_FOUND if we can't return the name, + STATUS_BUFFER_OVERFLOW otherwise. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + DIRENT_ENUM_CONTEXT DirContext; + DIRENT Dirent; + + PUNICODE_STRING NameToUse; + ULONG DirentOffset; + + COMPOUND_PATH_ENTRY CompoundPathEntry; + FILE_ENUM_CONTEXT FileContext; + + PFCB ParentFcb; + BOOLEAN ReleaseParentFcb = FALSE; + + BOOLEAN CleanupFileLookup = FALSE; + BOOLEAN CleanupDirectoryLookup = FALSE; + + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / 2 ]; + USHORT ShortNameLength; + + PAGED_CODE(); + + // + // Initialize the buffer length to zero. + // + + Buffer->FileNameLength = 0; + + // + // If this is the root or this file was opened using a version number then + // there is no short name. + // + + if ((Fcb == Fcb->Vcb->RootIndexFcb) || + FlagOn( Ccb->Flags, CCB_FLAG_OPEN_WITH_VERSION)) { + + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + // + // Use a try-finally to cleanup the structures. + // + + try { + + ParentFcb = Fcb->ParentFcb; + CdAcquireFileShared( IrpContext, ParentFcb ); + ReleaseParentFcb = TRUE; + + // + // Do an unsafe test to see if we need to create a file object. + // + + if (ParentFcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, ParentFcb->Vcb, ParentFcb ); + } + + if (CdFidIsDirectory( Fcb->FileId)) { + + // + // Fcb is for a directory, so we need to dig the dirent from the parent. In + // order to do this we need to get the name of the directory from its pathtable + // entry and then search in the parent for a matching dirent. + // + // This could be optimized somewhat. + // + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdInitializeFileContext( IrpContext, &FileContext ); + + CleanupDirectoryLookup = TRUE; + + CdLookupPathEntry( IrpContext, + CdQueryFidPathTableOffset( Fcb->FileId ), + Fcb->Ordinal, + FALSE, + &CompoundPathEntry ); + + CdUpdatePathEntryName( IrpContext, &CompoundPathEntry.PathEntry, TRUE ); + + if (!CdFindDirectory( IrpContext, + ParentFcb, + &CompoundPathEntry.PathEntry.CdCaseDirName, + TRUE, + &FileContext )) { + + // + // If we failed to find the child directory by name in the parent + // something is quite wrong with this disc. + // + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + NameToUse = &FileContext.InitialDirent->Dirent.CdCaseFileName.FileName; + DirentOffset = FileContext.InitialDirent->Dirent.DirentOffset; + + } else { + + // + // Initialize the search dirent structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + + CleanupFileLookup = TRUE; + + CdLookupDirent( IrpContext, + ParentFcb, + CdQueryFidDirentOffset( Fcb->FileId ), + &DirContext ); + + CdUpdateDirentFromRawDirent( IrpContext, + ParentFcb, + &DirContext, + &Dirent ); + + // + // Now update the dirent name. + // + + CdUpdateDirentName( IrpContext, &Dirent, TRUE ); + + NameToUse = &Dirent.CdCaseFileName.FileName; + DirentOffset = Dirent.DirentOffset; + } + + // + // If the name is 8.3 then fail this request. + // + + if (CdIs8dot3Name( IrpContext, + *NameToUse )) { + + + try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + CdGenerate8dot3Name( IrpContext, + NameToUse, + DirentOffset, + ShortNameBuffer, + &ShortNameLength ); + + // + // We now have the short name. We have left it in Unicode form so copy it directly. + // + + Buffer->FileNameLength = ShortNameLength; + + if (Buffer->FileNameLength + sizeof( ULONG ) > *Length) { + + Buffer->FileNameLength = *Length - sizeof( ULONG ); + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( Buffer->FileName, ShortNameBuffer, Buffer->FileNameLength ); + + try_exit: NOTHING; + } finally { + + if (CleanupFileLookup) { + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + + } else if (CleanupDirectoryLookup) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdCleanupFileContext( IrpContext, &FileContext ); + } + + if (ReleaseParentFcb) { + + CdReleaseFile( IrpContext, ParentFcb ); + } + } + + // + // Reduce the available bytes by the amount stored into this buffer. + // + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + + *Length -= sizeof( ULONG ) + Buffer->FileNameLength; + } + + return Status; +} + + +// +// Local support routine +// + +VOID +CdQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + + Description: + + This routine performs the query network open information function for Cdfs + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // We only support creation, last modify and last write times on Cdfs. + // + + Buffer->LastWriteTime.QuadPart = + Buffer->CreationTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // We get the sizes from the header. Return a size of zero + // for all directories. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + return; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/filobsup.c b/reactos/drivers/filesystems/cdfs_new/filobsup.c new file mode 100755 index 00000000000..697a98a8168 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/filobsup.c @@ -0,0 +1,234 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FilObSup.c + +Abstract: + + This module implements the Cdfs File object support routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FILOBSUP) + +// +// Local constants. +// + +#define TYPE_OF_OPEN_MASK (0x00000007) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdDecodeFileObject) +#pragma alloc_text(PAGE, CdFastDecodeFileObject) +#pragma alloc_text(PAGE, CdSetFileObject) +#endif + + +VOID +CdSetFileObject ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN TYPE_OF_OPEN TypeOfOpen, + IN PFCB Fcb OPTIONAL, + IN PCCB Ccb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine will initialize the FileObject context fields based on the + input type and data structures. + +Arguments: + + FileObject - Supplies the file object pointer being initialized. + + TypeOfOpen - Sets the type of open. + + Fcb - Fcb for this file object. Ignored for UnopenedFileObject. + + Ccb - Ccb for the handle corresponding to this file object. Will not + be present for stream file objects. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + // + // We only have values 0 to 7 available so make sure we didn't + // inadvertantly add a new type. + // + + ASSERTMSG( "FileObject types exceed available bits\n", BeyondValidType <= 8 ); + + // + // Setting a file object to type UnopenedFileObject means just + // clearing all of the context fields. All the other input + // + + if (TypeOfOpen == UnopenedFileObject) { + + FileObject->FsContext = + FileObject->FsContext2 = NULL; + + return; + } + + // + // Check that the 3 low-order bits of the Ccb are clear. + // + + ASSERTMSG( "Ccb is not quad-aligned\n", !FlagOn( ((ULONG_PTR) Ccb), TYPE_OF_OPEN_MASK )); + + // + // We will or the type of open into the low order bits of FsContext2 + // along with the Ccb value. + // The Fcb is stored into the FsContext field. + // + + FileObject->FsContext = Fcb; + FileObject->FsContext2 = Ccb; + + SetFlag( ((ULONG_PTR) FileObject->FsContext2), TypeOfOpen ); + + // + // Set the Vpb field in the file object. + // + + FileObject->Vpb = Fcb->Vcb->Vpb; + + return; +} + + + +TYPE_OF_OPEN +CdDecodeFileObject ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb, + OUT PCCB *Ccb + ) + +/*++ + +Routine Description: + + This routine takes a file object and extracts the Fcb and Ccb (possibly NULL) + and returns the type of open. + +Arguments: + + FileObject - Supplies the file object pointer being initialized. + + Fcb - Address to store the Fcb contained in the file object. + + Ccb - Address to store the Ccb contained in the file object. + +Return Value: + + TYPE_OF_OPEN - Indicates the type of file object. + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + + PAGED_CODE(); + + // + // If this is an unopened file object then return NULL for the + // Fcb/Ccb. Don't trust any other values in the file object. + // + + TypeOfOpen = (TYPE_OF_OPEN) FlagOn( (ULONG_PTR) FileObject->FsContext2, + TYPE_OF_OPEN_MASK ); + + if (TypeOfOpen == UnopenedFileObject) { + + *Fcb = NULL; + *Ccb = NULL; + + } else { + + // + // The Fcb is pointed to by the FsContext field. The Ccb is in + // FsContext2 (after clearing the low three bits). The low three + // bits are the file object type. + // + + *Fcb = FileObject->FsContext; + *Ccb = FileObject->FsContext2; + + ClearFlag( (ULONG_PTR) *Ccb, TYPE_OF_OPEN_MASK ); + } + + // + // Now return the type of open. + // + + return TypeOfOpen; +} + + +TYPE_OF_OPEN +CdFastDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb + ) + +/*++ + +Routine Description: + + This procedure takes a pointer to a file object, that has already been + opened by Cdfs and does a quick decode operation. It will only return + a non null value if the file object is a user file open + +Arguments: + + FileObject - Supplies the file object pointer being interrogated + + Fcb - Address to store Fcb if this is a user file object. NULL + otherwise. + +Return Value: + + TYPE_OF_OPEN - type of open of this file object. + +--*/ + +{ + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + // + // The Fcb is in the FsContext field. The type of open is in the low + // bits of the Ccb. + // + + *Fcb = FileObject->FsContext; + + return (TYPE_OF_OPEN) + FlagOn( (ULONG_PTR) FileObject->FsContext2, TYPE_OF_OPEN_MASK ); +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/fsctrl.c b/reactos/drivers/filesystems/cdfs_new/fsctrl.c new file mode 100755 index 00000000000..82ac916455a --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/fsctrl.c @@ -0,0 +1,3166 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FsCtrl.c + +Abstract: + + This module implements the File System Control routines for Cdfs called + by the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FSCTRL) + +// +// Local constants +// + +BOOLEAN CdDisable = FALSE; +BOOLEAN CdNoJoliet = FALSE; + +// +// Local support routines +// + +NTSTATUS +CdUserFsctl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +CdReMountOldVcb( + IN PIRP_CONTEXT IrpContext, + IN PVCB OldVcb, + IN PVCB NewVcb, + IN PDEVICE_OBJECT DeviceObjectWeTalkTo + ); + +NTSTATUS +CdMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdOplockRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +CdIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +CdInvalidateVolumes ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +CdScanForDismountedVcb ( + IN PIRP_CONTEXT IrpContext + ); + +BOOLEAN +CdFindPrimaryVd ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PCHAR RawIsoVd, + IN ULONG BlockFactor, + IN BOOLEAN ReturnOnError, + IN BOOLEAN VerifyVolume + ); + +BOOLEAN +CdIsRemount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PVCB *OldVcb + ); + +VOID +CdFindActiveVolDescriptor ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PCHAR RawIsoVd, + IN BOOLEAN VerifyVolume + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonFsControl) +#pragma alloc_text(PAGE, CdDismountVolume) +#pragma alloc_text(PAGE, CdFindActiveVolDescriptor) +#pragma alloc_text(PAGE, CdFindPrimaryVd) +#pragma alloc_text(PAGE, CdIsPathnameValid) +#pragma alloc_text(PAGE, CdIsRemount) +#pragma alloc_text(PAGE, CdIsVolumeDirty) +#pragma alloc_text(PAGE, CdIsVolumeMounted) +#pragma alloc_text(PAGE, CdLockVolume) +#pragma alloc_text(PAGE, CdMountVolume) +#pragma alloc_text(PAGE, CdOplockRequest) +#pragma alloc_text(PAGE, CdScanForDismountedVcb) +#pragma alloc_text(PAGE, CdUnlockVolume) +#pragma alloc_text(PAGE, CdUserFsctl) +#pragma alloc_text(PAGE, CdVerifyVolume) +#endif + + +// +// Local support routine +// + +NTSTATUS +CdLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +Routine Description: + + This routine performs the actual lock volume operation. It will be called + by anyone wishing to try to protect the volume for a long duration. PNP + operations are such a user. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KIRQL SavedIrql; + NTSTATUS FinalStatus = (FileObject? STATUS_ACCESS_DENIED: STATUS_DEVICE_BUSY); + ULONG RemainingUserReferences = (FileObject? 1: 0); + + // + // The cleanup count for the volume only reflects the fileobject that + // will lock the volume. Otherwise, we must fail the request. + // + // Since the only cleanup is for the provided fileobject, we will try + // to get rid of all of the other user references. If there is only one + // remaining after the purge then we can allow the volume to be locked. + // + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + // + // Now back out of our synchronization and wait for the lazy writer + // to finish off any lazy closes that could have been outstanding. + // + // Since we purged, we know that the lazy writer will issue all + // possible lazy closes in the next tick - if we hadn't, an otherwise + // unopened file with a large amount of dirty data could have hung + // around for a while as the data trickled out to the disk. + // + // This is even more important now since we send notification to + // alert other folks that this style of check is about to happen so + // that they can close their handles. We don't want to enter a fast + // race with the lazy writer tearing down his references to the file. + // + + CdReleaseVcb( IrpContext, Vcb ); + + Status = CcWaitForCurrentLazyWriterActivity(); + + // + // This is intentional. If we were able to get the Vcb before, just + // wait for it and take advantage of knowing that it is OK to leave + // the flag up. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + CdFspClose( Vcb ); + + // + // If the volume is already explicitly locked then fail. We use the + // Vpb locked flag as an 'explicit lock' flag in the same way as Fat. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (!FlagOn( Vcb->Vpb->Flags, VPB_LOCKED ) && + (Vcb->VcbCleanup == RemainingUserReferences) && + (Vcb->VcbUserReference == CDFS_RESIDUAL_USER_REFERENCE + RemainingUserReferences)) { + + SetFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + SetFlag( Vcb->Vpb->Flags, VPB_LOCKED); + Vcb->VolumeLockFileObject = FileObject; + FinalStatus = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return FinalStatus; +} + + +NTSTATUS +CdUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +Routine Description: + + This routine performs the actual unlock volume operation. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + + Attempting to remove a system lock that did not exist is OK. + +--*/ + +{ + NTSTATUS Status = STATUS_NOT_LOCKED; + KIRQL SavedIrql; + + // + // Note that we check the VPB_LOCKED flag here rather than the Vcb + // lock flag. The Vpb flag is only set for an explicit lock request, not + // for the implicit lock obtained on a volume open with zero share mode. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && + (FileObject == Vcb->VolumeLockFileObject)) { + + ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED); + Vcb->VolumeLockFileObject = NULL; + Status = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Status; +} + + +NTSTATUS +CdCommonFsControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing FileSystem control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // We know this is a file system control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_USER_FS_REQUEST: + + Status = CdUserFsctl( IrpContext, Irp ); + break; + + case IRP_MN_MOUNT_VOLUME: + + Status = CdMountVolume( IrpContext, Irp ); + break; + + case IRP_MN_VERIFY_VOLUME: + + Status = CdVerifyVolume( IrpContext, Irp ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdUserFsctl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This is the common routine for implementing the user's requests made + through NtFsControlFile. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // Case on the control code. + // + + switch ( IrpSp->Parameters.FileSystemControl.FsControlCode ) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1 : + case FSCTL_REQUEST_OPLOCK_LEVEL_2 : + case FSCTL_REQUEST_BATCH_OPLOCK : + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE : + case FSCTL_OPBATCH_ACK_CLOSE_PENDING : + case FSCTL_OPLOCK_BREAK_NOTIFY : + case FSCTL_OPLOCK_BREAK_ACK_NO_2 : + case FSCTL_REQUEST_FILTER_OPLOCK : + + Status = CdOplockRequest( IrpContext, Irp ); + break; + + case FSCTL_LOCK_VOLUME : + + Status = CdLockVolume( IrpContext, Irp ); + break; + + case FSCTL_UNLOCK_VOLUME : + + Status = CdUnlockVolume( IrpContext, Irp ); + break; + + case FSCTL_DISMOUNT_VOLUME : + + Status = CdDismountVolume( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_DIRTY : + + Status = CdIsVolumeDirty( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_MOUNTED : + + Status = CdIsVolumeMounted( IrpContext, Irp ); + break; + + case FSCTL_IS_PATHNAME_VALID : + + Status = CdIsPathnameValid( IrpContext, Irp ); + break; + + case FSCTL_INVALIDATE_VOLUMES : + + Status = CdInvalidateVolumes( IrpContext, Irp ); + break; + + + // + // We don't support any of the known or unknown requests. + // + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +VOID +CdReMountOldVcb( + IN PIRP_CONTEXT IrpContext, + IN PVCB OldVcb, + IN PVCB NewVcb, + IN PDEVICE_OBJECT DeviceObjectWeTalkTo + ) +{ + KIRQL SavedIrql; + + ObDereferenceObject( OldVcb->TargetDeviceObject ); + + IoAcquireVpbSpinLock( &SavedIrql ); + + NewVcb->Vpb->RealDevice->Vpb = OldVcb->Vpb; + + OldVcb->Vpb->RealDevice = NewVcb->Vpb->RealDevice; + OldVcb->TargetDeviceObject = DeviceObjectWeTalkTo; + + CdUpdateVcbCondition( OldVcb, VcbMounted); + CdUpdateMediaChangeCount( OldVcb, NewVcb->MediaChangeCount); + + ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + + IoReleaseVpbSpinLock( SavedIrql ); +} + + +// +// Local support routine +// + +NTSTATUS +CdMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the mount volume operation. It is responsible for + either completing of enqueuing the input Irp. + + Its job is to verify that the volume denoted in the IRP is a Cdrom volume, + and create the VCB and root DCB structures. The algorithm it + uses is essentially as follows: + + 1. Create a new Vcb Structure, and initialize it enough to do I/O + through the on-disk volume descriptors. + + 2. Read the disk and check if it is a Cdrom volume. + + 3. If it is not a Cdrom volume then delete the Vcb and + complete the IRP back with an appropriate status. + + 4. Check if the volume was previously mounted and if it was then do a + remount operation. This involves deleting the VCB, hook in the + old VCB, and complete the IRP. + + 5. Otherwise create a Vcb and root DCB for each valid volume descriptor. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PVOLUME_DEVICE_OBJECT VolDo = NULL; + PVCB Vcb = NULL; + PVCB OldVcb; + + BOOLEAN FoundPvd = FALSE; + BOOLEAN SetDoVerifyOnFail; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + PDEVICE_OBJECT DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject; + PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb; + + PFILE_OBJECT FileObjectToNotify = NULL; + + ULONG BlockFactor; + DISK_GEOMETRY DiskGeometry; + + IO_SCSI_CAPABILITIES Capabilities; + + IO_STATUS_BLOCK Iosb; + + PCHAR RawIsoVd = NULL; + + PCDROM_TOC CdromToc = NULL; + ULONG TocLength = 0; + ULONG TocTrackCount = 0; + ULONG TocDiskFlags = 0; + ULONG MediaChangeCount = 0; + + PAGED_CODE(); + + // + // Check that we are talking to a Cdrom device. This request should + // always be waitable. + // + + ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ); + ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + + // + // Update the real device in the IrpContext from the Vpb. There was no available + // file object when the IrpContext was created. + // + + IrpContext->RealDevice = Vpb->RealDevice; + + SetDoVerifyOnFail = CdRealDevNeedsVerify( IrpContext->RealDevice); + + // + // Check if we have disabled the mount process. + // + + if (CdDisable) { + + CdCompleteRequest( IrpContext, Irp, STATUS_UNRECOGNIZED_VOLUME ); + return STATUS_UNRECOGNIZED_VOLUME; + } + + // + // Do a CheckVerify here to lift the MediaChange ticker from the driver + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + DeviceObjectWeTalkTo, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // Now let's make Jeff delirious and call to get the disk geometry. This + // will fix the case where the first change line is swallowed. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_GET_DRIVE_GEOMETRY, + DeviceObjectWeTalkTo, + &DiskGeometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + // + // Return insufficient sources to our caller. + // + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Now check the block factor for addressing the volume descriptors. + // If the call for the disk geometry failed then assume there is one + // block per sector. + // + + BlockFactor = 1; + + if (NT_SUCCESS( Status ) && + (DiskGeometry.BytesPerSector != 0) && + (DiskGeometry.BytesPerSector < SECTOR_SIZE)) { + + BlockFactor = SECTOR_SIZE / DiskGeometry.BytesPerSector; + } + + // + // Acquire the global resource to do mount operations. + // + + CdAcquireCdData( IrpContext ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Allocate a buffer to query the TOC. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); + + // + // Do a quick check to see if there any Vcb's which can be removed. + // + + CdScanForDismountedVcb( IrpContext ); + + // + // Get our device object and alignment requirement. + // + + Status = IoCreateDevice( CdData.DriverObject, + sizeof( VOLUME_DEVICE_OBJECT ) - sizeof( DEVICE_OBJECT ), + NULL, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + (PDEVICE_OBJECT *) &VolDo ); + + if (!NT_SUCCESS( Status )) { try_leave( Status ); } + + // + // Our alignment requirement is the larger of the processor alignment requirement + // already in the volume device object and that in the DeviceObjectWeTalkTo + // + + if (DeviceObjectWeTalkTo->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) { + + VolDo->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement; + } + + // + // We must initialize the stack size in our device object before + // the following reads, because the I/O system has not done it yet. + // + + ((PDEVICE_OBJECT) VolDo)->StackSize = (CCHAR) (DeviceObjectWeTalkTo->StackSize + 1); + + ClearFlag( VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING ); + + // + // Initialize the overflow queue for the volume + // + + VolDo->OverflowQueueCount = 0; + InitializeListHead( &VolDo->OverflowQueue ); + + VolDo->PostedRequestCount = 0; + KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock ); + + // + // Let's query for the Toc now and handle any error we get from this operation. + // + + Status = CdProcessToc( IrpContext, + DeviceObjectWeTalkTo, + CdromToc, + &TocLength, + &TocTrackCount, + &TocDiskFlags ); + + // + // If we failed to read the TOC, then bail out. Probably blank media. + // + + if (Status != STATUS_SUCCESS) { + + try_leave( Status ); + } + + // + // Now before we can initialize the Vcb we need to set up the + // device object field in the VPB to point to our new volume device + // object. + // + + Vpb->DeviceObject = (PDEVICE_OBJECT) VolDo; + + // + // Initialize the Vcb. This routine will raise on an allocation + // failure. + // + + CdInitializeVcb( IrpContext, + &VolDo->Vcb, + DeviceObjectWeTalkTo, + Vpb, + CdromToc, + TocLength, + TocTrackCount, + TocDiskFlags, + BlockFactor, + MediaChangeCount ); + + // + // Show that we initialized the Vcb and can cleanup with the Vcb. + // + + Vcb = &VolDo->Vcb; + VolDo = NULL; + Vpb = NULL; + CdromToc = NULL; + + // + // Store the Vcb in the IrpContext as we didn't have one before. + // + + IrpContext->Vcb = Vcb; + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Let's reference the Vpb to make sure we are the one to + // have the last dereference. + // + + Vcb->Vpb->ReferenceCount += 1; + + // + // Clear the verify bit for the start of mount. + // + + CdMarkRealDevVerifyOk( Vcb->Vpb->RealDevice); + + if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + // + // Allocate a buffer to read in the volume descriptors. We allocate a full + // page to make sure we don't hit any alignment problems. + // + + RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool, + ROUND_TO_PAGES( SECTOR_SIZE ), + TAG_VOL_DESC ); + + // + // Try to find the primary volume descriptor. + // + + FoundPvd = CdFindPrimaryVd( IrpContext, + Vcb, + RawIsoVd, + BlockFactor, + TRUE, + FALSE ); + + if (!FoundPvd) { + + // + // We failed to find a valid VD in the data track, but there were also + // audio tracks on this disc, so we'll try to mount it as an audio CD. + // Since we're always last in the mount order, we won't be preventing + // any other FS from trying to mount the data track. However if the + // data track was at the start of the disc, then we abort, to avoid + // having to filter it from our synthesised directory listing later. We + // already filtered off any data track at the end. + // + + if (!(TocDiskFlags & CDROM_DISK_AUDIO_TRACK) || + BooleanFlagOn( Vcb->CdromToc->TrackData[0].Control, TOC_DATA_TRACK)) { + + try_leave( Status = STATUS_UNRECOGNIZED_VOLUME); + } + + SetFlag( Vcb->VcbState, VCB_STATE_AUDIO_DISK | VCB_STATE_CDXA ); + + CdFreePool( &RawIsoVd ); + RawIsoVd = NULL; + } + } + + // + // Look and see if there is a secondary volume descriptor we want to + // use. + // + + if (FoundPvd) { + + // + // Store the primary volume descriptor in the second half of + // RawIsoVd. Then if our search for a secondary fails we can + // recover this immediately. + // + + RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + RawIsoVd, + SECTOR_SIZE ); + + // + // We have the initial volume descriptor. Locate a secondary + // volume descriptor if present. + // + + CdFindActiveVolDescriptor( IrpContext, + Vcb, + RawIsoVd, + FALSE); + } + + // + // Check if this is a remount operation. If so then clean up + // the data structures passed in and created here. + // + + if (CdIsRemount( IrpContext, Vcb, &OldVcb )) { + + KIRQL SavedIrql; + + ASSERT( NULL != OldVcb->SwapVpb ); + + // + // Link the old Vcb to point to the new device object that we + // should be talking to, dereferencing the previous. Call a + // nonpaged routine to do this since we take the Vpb spinlock. + // + + CdReMountOldVcb( IrpContext, + OldVcb, + Vcb, + DeviceObjectWeTalkTo); + + // + // See if we will need to provide notification of the remount. This is the readonly + // filesystem's form of dismount/mount notification - we promise that whenever a + // volume is "dismounted", that a mount notification will occur when it is revalidated. + // Note that we do not send mount on normal remounts - that would duplicate the media + // arrival notification of the device driver. + // + + if (FlagOn( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) { + + ClearFlag( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT ); + + FileObjectToNotify = OldVcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + try_leave( Status = STATUS_SUCCESS ); + } + + // + // This is a new mount. Go ahead and initialize the + // Vcb from the volume descriptor. + // + + CdUpdateVcbFromVolDescriptor( IrpContext, + Vcb, + RawIsoVd ); + + // + // Drop an extra reference on the root dir file so we'll be able to send + // notification. + // + + if (Vcb->RootIndexFcb) { + + FileObjectToNotify = Vcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + // + // Now check the maximum transfer limits on the device in case we + // get raw reads on this volume. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_SCSI_GET_CAPABILITIES, + DeviceObjectWeTalkTo, + &Capabilities, + sizeof( IO_SCSI_CAPABILITIES ), + FALSE, + TRUE, + NULL ); + + if (NT_SUCCESS(Status)) { + + Vcb->MaximumTransferRawSectors = Capabilities.MaximumTransferLength / RAW_SECTOR_SIZE; + Vcb->MaximumPhysicalPages = Capabilities.MaximumPhysicalPages; + + } else { + + // + // This should never happen, but we can safely assume 64k and 16 pages. + // + + Vcb->MaximumTransferRawSectors = (64 * 1024) / RAW_SECTOR_SIZE; + Vcb->MaximumPhysicalPages = 16; + } + + // + // The new mount is complete. Remove the additional references on this + // Vcb and the device we are mounted on top of. + // + + Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE; + ASSERT( Vcb->VcbReference == CDFS_RESIDUAL_REFERENCE ); + + ObDereferenceObject( Vcb->TargetDeviceObject ); + + CdUpdateVcbCondition( Vcb, VcbMounted); + + CdReleaseVcb( IrpContext, Vcb ); + Vcb = NULL; + + Status = STATUS_SUCCESS; + + } finally { + + // + // Free the TOC buffer if not in the Vcb. + // + + if (CdromToc != NULL) { + + CdFreePool( &CdromToc ); + } + + // + // Free the sector buffer if allocated. + // + + if (RawIsoVd != NULL) { + + CdFreePool( &RawIsoVd ); + } + + // + // If we are not mounting the device, then set the verify bit again. + // + + if ((AbnormalTermination() || (Status != STATUS_SUCCESS)) && + SetDoVerifyOnFail) { + + CdMarkRealDevForVerify( IrpContext->RealDevice); + } + + // + // If we didn't complete the mount then cleanup any remaining structures. + // + + if (Vpb != NULL) { Vpb->DeviceObject = NULL; } + + if (Vcb != NULL) { + + // + // Make sure there is no Vcb in the IrpContext since it could go away + // + + IrpContext->Vcb = NULL; + + Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE; + + if (CdDismountVcb( IrpContext, Vcb )) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + } else if (VolDo != NULL) { + + IoDeleteDevice( (PDEVICE_OBJECT) VolDo ); + } + + // + // Release the global resource. + // + + CdReleaseCdData( IrpContext ); + } + + // + // Now send mount notification. + // + + if (FileObjectToNotify) { + + FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( FileObjectToNotify ); + } + + // + // Complete the request if no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the verify volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb; + PVCB Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->Parameters.VerifyVolume.DeviceObject)->Vcb; + + PCHAR RawIsoVd = NULL; + + PCDROM_TOC CdromToc = NULL; + ULONG TocLength = 0; + ULONG TocTrackCount = 0; + ULONG TocDiskFlags = 0; + + ULONG MediaChangeCount = Vcb->MediaChangeCount; + + PFILE_OBJECT FileObjectToNotify = NULL; + + BOOLEAN ReturnError; + BOOLEAN ReleaseVcb = FALSE; + + IO_STATUS_BLOCK Iosb; + + STRING AnsiLabel; + UNICODE_STRING UnicodeLabel; + + WCHAR VolumeLabel[ VOLUME_ID_LENGTH ]; + ULONG VolumeLabelLength; + + ULONG Index; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // We check that we are talking to a Cdrom device. + // + + ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ); + ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + + // + // Update the real device in the IrpContext from the Vpb. There was no available + // file object when the IrpContext was created. + // + + IrpContext->RealDevice = Vpb->RealDevice; + + // + // Acquire the global resource to synchronise against mounts and teardown, + // finally clause releases. + // + + CdAcquireCdData( IrpContext ); + + try { + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + ReleaseVcb = TRUE; + + // + // Verify that there is a disk here. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then return WRONG_VOLUME to + // allow the volume to be mounted by raw. + // + + if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) { + + Status = STATUS_WRONG_VOLUME; + } + + try_return( Status ); + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // Verify that the device actually saw a change. If the driver does not + // support the MCC, then we must verify the volume in any case. + // + + if (MediaChangeCount == 0 || + (Vcb->MediaChangeCount != MediaChangeCount)) { + + // + // Allocate a buffer to query the TOC. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); + + // + // Let's query for the Toc now and handle any error we get from this operation. + // + + Status = CdProcessToc( IrpContext, + Vcb->TargetDeviceObject, + CdromToc, + &TocLength, + &TocTrackCount, + &TocDiskFlags ); + + // + // If we failed to read the TOC, then give up now. Drives will fail + // a TOC read on, for example, erased CD-RW media. + // + + if (Status != STATUS_SUCCESS) { + + // + // For any errors other than no media and not ready, commute the + // status to ensure that the current VPB is kicked off the device + // below - there is probably blank media in the drive, since we got + // further than the check verify. + // + + if (!CdIsRawDevice( IrpContext, Status )) { + + Status = STATUS_WRONG_VOLUME; + } + + try_return( Status ); + + // + // We got a TOC. Verify that it matches the previous Toc. + // + + } else if ((Vcb->TocLength != TocLength) || + (Vcb->TrackCount != TocTrackCount) || + (Vcb->DiskFlags != TocDiskFlags) || + !RtlEqualMemory( CdromToc, + Vcb->CdromToc, + TocLength )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // If the disk to verify is an audio disk then we already have a + // match. Otherwise we need to check the volume descriptor. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + // + // Allocate a buffer for the sector buffer. + // + + RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool, + ROUND_TO_PAGES( 2 * SECTOR_SIZE ), + TAG_VOL_DESC ); + + // + // Read the primary volume descriptor for this volume. If we + // get an io error and this verify was a the result of DASD open, + // commute the Io error to STATUS_WRONG_VOLUME. Note that if we currently + // expect a music disk then this request should fail. + // + + ReturnError = FALSE; + + if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) { + + ReturnError = TRUE; + } + + if (!CdFindPrimaryVd( IrpContext, + Vcb, + RawIsoVd, + Vcb->BlockFactor, + ReturnError, + TRUE )) { + + // + // If the previous Vcb did not represent a raw disk + // then show this volume was dismounted. + // + + try_return( Status = STATUS_WRONG_VOLUME ); + + } + else { + + // + // Look for a supplementary VD. + // + // Store the primary volume descriptor in the second half of + // RawIsoVd. Then if our search for a secondary fails we can + // recover this immediately. + // + + RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + RawIsoVd, + SECTOR_SIZE ); + + // + // We have the initial volume descriptor. Locate a secondary + // volume descriptor if present. + // + + CdFindActiveVolDescriptor( IrpContext, + Vcb, + RawIsoVd, + TRUE); + // + // Compare the serial numbers. If they don't match, set the + // status to wrong volume. + // + + if (Vpb->SerialNumber != CdSerial32( RawIsoVd, SECTOR_SIZE )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Verify the volume labels. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) { + + // + // Compute the length of the volume name + // + + AnsiLabel.Buffer = CdRvdVolId( RawIsoVd, Vcb->VcbState ); + AnsiLabel.MaximumLength = AnsiLabel.Length = VOLUME_ID_LENGTH; + + UnicodeLabel.MaximumLength = VOLUME_ID_LENGTH * sizeof( WCHAR ); + UnicodeLabel.Buffer = VolumeLabel; + + // + // Convert this to unicode. If we get any error then use a name + // length of zero. + // + + VolumeLabelLength = 0; + + if (NT_SUCCESS( RtlOemStringToCountedUnicodeString( &UnicodeLabel, + &AnsiLabel, + FALSE ))) { + + VolumeLabelLength = UnicodeLabel.Length; + } + + // + // We need to convert from big-endian to little endian. + // + + } else { + + CdConvertBigToLittleEndian( IrpContext, + CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH, + (PCHAR) VolumeLabel ); + + VolumeLabelLength = VOLUME_ID_LENGTH; + } + + // + // Strip the trailing spaces or zeroes from the name. + // + + Index = VolumeLabelLength / sizeof( WCHAR ); + + while (Index > 0) { + + if ((VolumeLabel[ Index - 1 ] != L'\0') && + (VolumeLabel[ Index - 1 ] != L' ')) { + + break; + } + + Index -= 1; + } + + // + // Now set the final length for the name. + // + + VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR )); + + // + // Now check that the label matches. + // + if ((Vpb->VolumeLabelLength != VolumeLabelLength) || + !RtlEqualMemory( Vpb->VolumeLabel, + VolumeLabel, + VolumeLabelLength )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + } + } + + // + // The volume is OK, clear the verify bit. + // + + CdUpdateVcbCondition( Vcb, VcbMounted); + + CdMarkRealDevVerifyOk( Vpb->RealDevice); + + // + // See if we will need to provide notification of the remount. This is the readonly + // filesystem's form of dismount/mount notification. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) { + + ClearFlag( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT ); + + FileObjectToNotify = Vcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + try_exit: NOTHING; + + // + // Update the media change count to note that we have verified the volume + // at this value - regardless of the outcome. + // + + CdUpdateMediaChangeCount( Vcb, MediaChangeCount); + + // + // If we got the wrong volume then free any remaining XA sector in + // the current Vcb. Also mark the Vcb as not mounted. + // + + if (Status == STATUS_WRONG_VOLUME) { + + CdUpdateVcbCondition( Vcb, VcbNotMounted); + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + Vcb->XASector = 0; + Vcb->XADiskOffset = 0; + } + + // + // Now, if there are no user handles to the volume, try to spark + // teardown by purging the volume. + // + + if (Vcb->VcbCleanup == 0) { + + if (NT_SUCCESS( CdPurgeVolume( IrpContext, Vcb, FALSE ))) { + + ReleaseVcb = CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + } + + } finally { + + // + // Free the TOC buffer if allocated. + // + + if (CdromToc != NULL) { + + CdFreePool( &CdromToc ); + } + + if (RawIsoVd != NULL) { + + CdFreePool( &RawIsoVd ); + } + + if (ReleaseVcb) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + CdReleaseCdData( IrpContext ); + } + + // + // Now send mount notification. + // + + if (FileObjectToNotify) { + + FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( FileObjectToNotify ); + } + + // + // Complete the request if no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdOplockRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine to handle oplock requests made via the + NtFsControlFile call. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PFCB Fcb; + PCCB Ccb; + + ULONG OplockCount = 0; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // We only permit oplock requests on files. + // + + if (CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ) != UserFileOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Make this a waitable Irpcontext so we don't fail to acquire + // the resources. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + + // + // Switch on the function control code. We grab the Fcb exclusively + // for oplock requests, shared for oplock break acknowledgement. + // + + switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1 : + case FSCTL_REQUEST_OPLOCK_LEVEL_2 : + case FSCTL_REQUEST_BATCH_OPLOCK : + case FSCTL_REQUEST_FILTER_OPLOCK : + + CdAcquireFcbExclusive( IrpContext, Fcb, FALSE ); + + if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { + + if (Fcb->FileLock != NULL) { + + OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( Fcb->FileLock ); + } + + } else { + + OplockCount = Fcb->FcbCleanup; + } + + break; + + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + + CdAcquireFcbShared( IrpContext, Fcb, FALSE ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Use a try finally to free the Fcb. + // + + try { + + // + // Verify the Fcb. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Call the FsRtl routine to grant/acknowledge oplock. + // + + Status = FsRtlOplockFsctrl( &Fcb->Oplock, + Irp, + OplockCount ); + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + // + // The oplock package will complete the Irp. + // + + Irp = NULL; + + } finally { + + // + // Release all of our resources + // + + CdReleaseFcb( IrpContext, Fcb ); + } + + // + // Complete the request if there was no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the lock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + return STATUS_INVALID_PARAMETER; + } + + // + // Send our notification so that folks that like to hold handles on + // volumes can get out of the way. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK ); + + // + // Acquire exclusive access to the Vcb. + // + + Vcb = Fcb->Vcb; + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, Vcb ); + + Status = CdLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Vcb ); + + if (AbnormalTermination() || !NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED ); + } + } + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the unlock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the Vcb. + // + + Vcb = Fcb->Vcb; + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // We won't check for a valid Vcb for this request. An unlock will always + // succeed on a locked volume. + // + + Status = CdUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + // + // Release all of our resources + // + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Send notification that the volume is avaliable. + // + + if (NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK ); + } + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + + +// +// Local support routine +// + +NTSTATUS +CdDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the dismount volume operation. It is responsible for + either completing of enqueuing the input Irp. We only dismount a volume which + has been locked. The intent here is that someone has locked the volume (they are the + only remaining handle). We set the verify bit here and the user will close his handle. + We will dismount a volume with no user's handles in the verify path. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + Vcb = Fcb->Vcb; + + // + // Make this request waitable. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Acquire exclusive access to the Vcb, and take the global resource to + // sync. against mounts, verifies etc. + // + + CdAcquireCdData( IrpContext ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Mark the volume as needs to be verified, but only do it if + // the vcb is locked by this handle and the volume is currently mounted. + // + + if (Vcb->VcbCondition != VcbMounted) { + + Status = STATUS_VOLUME_DISMOUNTED; + + } else { + + // + // Invalidate the volume right now. + // + // The intent here is to make every subsequent operation + // on the volume fail and grease the rails toward dismount. + // By definition there is no going back from a SURPRISE. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // Set flag to tell the close path that we want to force dismount + // the volume when this handle is closed. + // + + SetFlag( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE); + + Status = STATUS_SUCCESS; + } + + // + // Release all of our resources + // + + CdReleaseVcb( IrpContext, Vcb ); + CdReleaseCdData( IrpContext); + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +CdIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently dirty. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PULONG VolumeState; + + // + // Get the current stack location and extract the output + // buffer information. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Get a pointer to the output buffer. + // + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + VolumeState = Irp->AssociatedIrp.SystemBuffer; + + } else { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER ); + return STATUS_INVALID_USER_BUFFER; + } + + // + // Make sure the output buffer is large enough and then initialize + // the answer to be that the volume isn't dirty. + // + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + *VolumeState = 0; + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (Fcb->Vcb->VcbCondition != VcbMounted) { + + CdCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED ); + return STATUS_VOLUME_DISMOUNTED; + } + + // + // Now set up to return the clean state. CDs obviously can never be dirty + // but we want to make sure we have enforced the full semantics of this call. + // + + Irp->IoStatus.Information = sizeof( ULONG ); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently mounted. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object. + // + + CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (Fcb != NULL) { + + // + // Disable PopUps, we want to return any error. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS ); + + // + // Verify the Vcb. This will raise in the error condition. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + } + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if pathname is a valid CDFS pathname. + We always succeed this request. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdInvalidateVolumes ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine searches for all the volumes mounted on the same real device + of the current DASD handle, and marks them all bad. The only operation + that can be done on such handles is cleanup and close. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + KIRQL SavedIrql; + + BOOLEAN UnlockVcb = FALSE; + + LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; + + HANDLE Handle; + + PVCB Vcb; + + PLIST_ENTRY Links; + + PFILE_OBJECT FileToMarkBad; + PDEVICE_OBJECT DeviceToMarkBad; + + // + // We only allow the invalidate call to come in on our file system devices. + // + + if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Check for the correct security access. + // The caller must have the SeTcbPrivilege. + // + + if (!SeSinglePrivilegeCheck( TcbPrivilege, Irp->RequestorMode )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_PRIVILEGE_NOT_HELD ); + + return STATUS_PRIVILEGE_NOT_HELD; + } + + // + // Try to get a pointer to the device object from the handle passed in. + // + +#if defined(_WIN64) + if (IoIs32bitProcess( Irp )) { + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( UINT32 )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + Handle = (HANDLE) LongToHandle( *((PUINT32) Irp->AssociatedIrp.SystemBuffer) ); + + } else { +#endif + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( HANDLE )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + Handle = *((PHANDLE) Irp->AssociatedIrp.SystemBuffer); +#if defined(_WIN64) + } +#endif + + Status = ObReferenceObjectByHandle( Handle, + 0, + *IoFileObjectType, + KernelMode, + &FileToMarkBad, + NULL ); + + if (!NT_SUCCESS(Status)) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Grab the DeviceObject from the FileObject. + // + + DeviceToMarkBad = FileToMarkBad->DeviceObject; + + // + // We only needed the device object involved, not a reference to the file. + // + + ObDereferenceObject( FileToMarkBad ); + + // + // Make sure this request can wait. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + + // + // Synchronise with pnp/mount/verify paths. + // + + CdAcquireCdData( IrpContext ); + + // + // Nothing can go wrong now. + // + + // + // Now walk through all the mounted Vcb's looking for candidates to + // mark invalid. + // + // On volumes we mark invalid, check for dismount possibility (which is + // why we have to get the next link so early). + // + + Links = CdData.VcbQueue.Flink; + + while (Links != &CdData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks); + + Links = Links->Flink; + + // + // If we get a match, mark the volume Bad, and also check to + // see if the volume should go away. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->Vpb->RealDevice == DeviceToMarkBad) { + + // + // Take the VPB spinlock, and look to see if this volume is the + // one currently mounted on the actual device. If it is, pull it + // off immediately. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (DeviceToMarkBad->Vpb == Vcb->Vpb) { + + PVPB NewVpb = Vcb->SwapVpb; + + ASSERT( FlagOn( Vcb->Vpb->Flags, VPB_MOUNTED)); + ASSERT( NULL != NewVpb); + + RtlZeroMemory( NewVpb, sizeof( VPB ) ); + + NewVpb->Type = IO_TYPE_VPB; + NewVpb->Size = sizeof( VPB ); + NewVpb->RealDevice = DeviceToMarkBad; + NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING ); + + DeviceToMarkBad->Vpb = NewVpb; + Vcb->SwapVpb = NULL; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE); + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + UnlockVcb = CdCheckForDismount( IrpContext, Vcb, FALSE ); + + if (UnlockVcb) { + + CdReleaseVcb( IrpContext, Vcb); + } + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + } + + CdReleaseCdData( IrpContext ); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +VOID +CdScanForDismountedVcb ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine walks through the list of Vcb's looking for any which may + now be deleted. They may have been left on the list because there were + outstanding references. + +Arguments: + +Return Value: + + None + +--*/ + +{ + PVCB Vcb; + PLIST_ENTRY Links; + + PAGED_CODE(); + + // + // Walk through all of the Vcb's attached to the global data. + // + + Links = CdData.VcbQueue.Flink; + + while (Links != &CdData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + + // + // Move to the next link now since the current Vcb may be deleted. + // + + Links = Links->Flink; + + // + // If dismount is already underway then check if this Vcb can + // go away. + // + + if ((Vcb->VcbCondition == VcbDismountInProgress) || + (Vcb->VcbCondition == VcbInvalid) || + ((Vcb->VcbCondition == VcbNotMounted) && (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE))) { + + CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdFindPrimaryVd ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PCHAR RawIsoVd, + IN ULONG BlockFactor, + IN BOOLEAN ReturnOnError, + IN BOOLEAN VerifyVolume + ) + +/*++ + +Routine Description: + + This routine is called to walk through the volume descriptors looking + for a primary volume descriptor. When/if a primary is found a 32-bit + serial number is generated and stored into the Vpb. We also store the + location of the primary volume descriptor in the Vcb. + +Arguments: + + Vcb - Pointer to the VCB for the volume. + + RawIsoVd - Pointer to a sector buffer which will contain the primary + volume descriptor on exit, if successful. + + BlockFactor - Block factor used by the current device for the TableOfContents. + + ReturnOnError - Indicates that we should raise on I/O errors rather than + returning a FALSE value. + + VerifyVolume - Indicates if we were called from the verify path. We + do a few things different in this path. We don't update the Vcb in + the verify path. + +Return Value: + + BOOLEAN - TRUE if a valid primary volume descriptor found, FALSE + otherwise. + +--*/ + +{ + NTSTATUS Status; + ULONG ThisPass = 1; + BOOLEAN FoundVd = FALSE; + + ULONG BaseSector; + ULONG SectorOffset; + + PCDROM_TOC CdromToc; + + ULONG VolumeFlags; + + PAGED_CODE(); + + // + // If there are no data tracks, don't even bother hunting for descriptors. + // + // This explicitly breaks various non-BlueBook compliant CDs that scribble + // an ISO filesystem on media claiming only audio tracks. Since these + // disks can cause serious problems in some CDROM units, fail fast. I admit + // that it is possible that someone can still record the descriptors in the + // audio track, record a data track (but fail to record descriptors there) + // and still have the disk work. As this form of error worked in NT 4.0, and + // since these disks really do exist, I don't want to change them. + // + // If we wished to support all such media (we don't), it would be neccesary + // to clear this flag on finding ISO or HSG descriptors below. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + return FALSE; + } + + // + // We will make at most two passes through the volume descriptor sequence. + // + // On the first pass we will query for the last session. Using this + // as a starting offset we will attempt to mount the volume. On any failure + // we will go to the second pass and try without using any multi-session + // information. + // + // On the second pass we will start offset from sector zero. + // + + while (!FoundVd && (ThisPass <= 2)) { + + // + // If we aren't at pass 1 then we start at sector 0. Otherwise we + // try to look up the multi-session information. + // + + BaseSector = 0; + + if (ThisPass == 1) { + + CdromToc = NULL; + + // + // Check for whether this device supports XA and multi-session. + // + + try { + + // + // Allocate a buffer for the last session information. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); + + // + // Query the last session information from the driver. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_GET_LAST_SESSION, + Vcb->TargetDeviceObject, + CdromToc, + sizeof( CDROM_TOC ), + FALSE, + TRUE, + NULL ); + + // + // Raise an exception if there was an allocation failure. + // + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + + CdRaiseStatus( IrpContext, Status ); + } + + // + // We don't handle any errors yet. We will hit that below + // as we try to scan the disk. If we have last session information + // then modify the base sector. + // + + if (NT_SUCCESS( Status ) && + (CdromToc->FirstTrack != CdromToc->LastTrack)) { + + PCHAR Source, Dest; + ULONG Count; + + Count = 4; + + // + // The track address is BigEndian, we need to flip the bytes. + // + + Source = (PUCHAR) &CdromToc->TrackData[0].Address[3]; + Dest = (PUCHAR) &BaseSector; + + do { + + *Dest++ = *Source--; + + } while (--Count); + + // + // Now adjust the base sector by the block factor of the + // device. + // + + BaseSector /= BlockFactor; + + // + // Make this look like the second pass since we are only using the + // first session. No reason to retry on error. + // + + } else { + + ThisPass += 1; + } + + } finally { + + if (CdromToc != NULL) { CdFreePool( &CdromToc ); } + } + } + + // + // Compute the starting sector offset from the start of the session. + // + + SectorOffset = FIRST_VD_SECTOR; + + // + // Start by assuming we have neither Hsg or Iso volumes. + // + + VolumeFlags = 0; + + // + // Loop until either error encountered, primary volume descriptor is + // found or a terminal volume descriptor is found. + // + + while (TRUE) { + + // + // Attempt to read the desired sector. Exit directly if operation + // not completed. + // + // If this is pass 1 we will ignore errors in read sectors and just + // go to the next pass. + // + + if (!CdReadSectors( IrpContext, + LlBytesFromSectors( BaseSector + SectorOffset ), + SECTOR_SIZE, + (BOOLEAN) ((ThisPass == 1) || ReturnOnError), + RawIsoVd, + Vcb->TargetDeviceObject )) { + + break; + } + + // + // Check if either an ISO or HSG volume. + // + + if (RtlEqualMemory( CdIsoId, + CdRvdId( RawIsoVd, VCB_STATE_ISO ), + VOL_ID_LEN )) { + + SetFlag( VolumeFlags, VCB_STATE_ISO ); + + } else if (RtlEqualMemory( CdHsgId, + CdRvdId( RawIsoVd, VCB_STATE_HSG ), + VOL_ID_LEN )) { + + SetFlag( VolumeFlags, VCB_STATE_HSG ); + + // + // We have neither so break out of the loop. + // + + } else { + + break; + } + + // + // Break out if the version number is incorrect or this is + // a terminator. + // + + if ((CdRvdVersion( RawIsoVd, VolumeFlags ) != VERSION_1) || + (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_TERMINATOR)) { + + break; + } + + // + // If this is a primary volume descriptor then our search is over. + // + + if (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_PRIMARY) { + + // + // If we are not in the verify path then initialize the + // fields in the Vcb with basic information from this + // descriptor. + // + + if (!VerifyVolume) { + + // + // Set the flag for the volume type. + // + + SetFlag( Vcb->VcbState, VolumeFlags ); + + // + // Store the base sector and sector offset for the + // primary volume descriptor. + // + + Vcb->BaseSector = BaseSector; + Vcb->VdSectorOffset = SectorOffset; + Vcb->PrimaryVdSectorOffset = SectorOffset; + } + + FoundVd = TRUE; + break; + } + + // + // Indicate that we're at the next sector. + // + + SectorOffset += 1; + } + + ThisPass += 1; + } + + return FoundVd; +} + + +// +// Local support routine +// + +BOOLEAN +CdIsRemount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PVCB *OldVcb + ) +/*++ + +Routine Description: + + This routine walks through the links of the Vcb chain in the global + data structure. The remount condition is met when the following + conditions are all met: + + If the new Vcb is a device only Mvcb and there is a previous + device only Mvcb. + + Otherwise following conditions must be matched. + + 1 - The 32 serial in the current VPB matches that in a previous + VPB. + + 2 - The volume label in the Vpb matches that in the previous + Vpb. + + 3 - The system pointer to the real device object in the current + VPB matches that in the same previous VPB. + + 4 - Finally the previous Vcb cannot be invalid or have a dismount + underway. + + If a VPB is found which matches these conditions, then the address of + the VCB for that VPB is returned via the pointer Vcb. + + Skip over the current Vcb. + +Arguments: + + Vcb - This is the Vcb we are checking for a remount. + + OldVcb - A pointer to the address to store the address for the Vcb + for the volume if this is a remount. (This is a pointer to + a pointer) + +Return Value: + + BOOLEAN - TRUE if this is in fact a remount, FALSE otherwise. + +--*/ + +{ + PLIST_ENTRY Link; + + PVPB Vpb = Vcb->Vpb; + PVPB OldVpb; + + BOOLEAN Remount = FALSE; + + PAGED_CODE(); + + // + // Check whether we are looking for a device only Mvcb. + // + + for (Link = CdData.VcbQueue.Flink; + Link != &CdData.VcbQueue; + Link = Link->Flink) { + + *OldVcb = CONTAINING_RECORD( Link, VCB, VcbLinks ); + + // + // Skip ourselves. + // + + if (Vcb == *OldVcb) { continue; } + + // + // Look at the Vpb and state of the previous Vcb. + // + + OldVpb = (*OldVcb)->Vpb; + + if ((OldVpb != Vpb) && + (OldVpb->RealDevice == Vpb->RealDevice) && + ((*OldVcb)->VcbCondition == VcbNotMounted)) { + + // + // If the current disk is a raw disk then it can match a previous music or + // raw disk. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + if (FlagOn( (*OldVcb)->VcbState, VCB_STATE_AUDIO_DISK )) { + + // + // If we have both TOC then fail the remount if the lengths + // are different or they don't match. + // + + if ((Vcb->TocLength != (*OldVcb)->TocLength) || + ((Vcb->TocLength != 0) && + !RtlEqualMemory( Vcb->CdromToc, + (*OldVcb)->CdromToc, + Vcb->TocLength ))) { + + continue; + } + + Remount = TRUE; + break; + } + + // + // The current disk is not a raw disk. Go ahead and compare + // serial numbers and volume label. + // + + } else if ((OldVpb->SerialNumber == Vpb->SerialNumber) && + (Vpb->VolumeLabelLength == OldVpb->VolumeLabelLength) && + (RtlEqualMemory( OldVpb->VolumeLabel, + Vpb->VolumeLabel, + Vpb->VolumeLabelLength ))) { + + // + // Remember the old mvcb. Then set the return value to + // TRUE and break. + // + + Remount = TRUE; + break; + } + } + } + + return Remount; +} + + +// +// Local support routine +// + +VOID +CdFindActiveVolDescriptor ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PCHAR RawIsoVd, + IN BOOLEAN VerifyVolume + ) + +/*++ + +Routine Description: + + This routine is called to search for a valid secondary volume descriptor that + we will support. Right now we only support Joliet escape sequences for + the secondary descriptor. + + If we don't find the secondary descriptor then we will reread the primary. + + This routine will update the serial number and volume label in the Vpb. + +Arguments: + + Vcb - This is the Vcb for the volume being mounted. + + RawIsoVd - Sector buffer used to read the volume descriptors from the disks, but + on input should contain the PVD (ISO) in the SECOND 'sector' of the + buffer. + + VerifyVolume - indicates we are being called by the verify path, and should + not modify the Vcb fields. + +Return Value: + + None + +--*/ + +{ + BOOLEAN FoundSecondaryVd = FALSE; + ULONG SectorOffset = FIRST_VD_SECTOR; + + ULONG Length; + + ULONG Index; + + PAGED_CODE(); + + // + // We only look for secondary volume descriptors on an Iso disk. + // + + if ((FlagOn( Vcb->VcbState, VCB_STATE_ISO) || VerifyVolume) && !CdNoJoliet) { + + // + // Scan the volume descriptors from the beginning looking for a valid + // secondary or a terminator. + // + + SectorOffset = FIRST_VD_SECTOR; + + while (TRUE) { + + // + // Read the next sector. We should never have an error in this + // path. + // + + CdReadSectors( IrpContext, + LlBytesFromSectors( Vcb->BaseSector + SectorOffset ), + SECTOR_SIZE, + FALSE, + RawIsoVd, + Vcb->TargetDeviceObject ); + + // + // Break out if the version number or standard Id is incorrect. + // Also break out if this is a terminator. + // + + if (!RtlEqualMemory( CdIsoId, CdRvdId( RawIsoVd, VCB_STATE_JOLIET ), VOL_ID_LEN ) || + (CdRvdVersion( RawIsoVd, VCB_STATE_JOLIET ) != VERSION_1) || + (CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_TERMINATOR)) { + + break; + } + + // + // We have a match if this is a secondary descriptor with a matching + // escape sequence. + // + + if ((CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_SECONDARY) && + (RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[0], + ESC_SEQ_LEN ) || + RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[1], + ESC_SEQ_LEN ) || + RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[2], + ESC_SEQ_LEN ))) { + + if (!VerifyVolume) { + + // + // Update the Vcb with the new volume descriptor. + // + + ClearFlag( Vcb->VcbState, VCB_STATE_ISO ); + SetFlag( Vcb->VcbState, VCB_STATE_JOLIET ); + + Vcb->VdSectorOffset = SectorOffset; + } + + FoundSecondaryVd = TRUE; + break; + } + + // + // Otherwise move on to the next sector. + // + + SectorOffset += 1; + } + + // + // If we didn't find the secondary then recover the original volume + // descriptor stored in the second half of the RawIsoVd. + // + + if (!FoundSecondaryVd) { + + RtlCopyMemory( RawIsoVd, + Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + SECTOR_SIZE ); + } + } + + // + // If we're in the verify path, our work is done, since we don't want + // to update any Vcb/Vpb values. + // + + if (VerifyVolume) { + + return; + } + + // + // Compute the serial number and volume label from the volume descriptor. + // + + Vcb->Vpb->SerialNumber = CdSerial32( RawIsoVd, SECTOR_SIZE ); + + // + // Make sure the CD label will fit in the Vpb. + // + + ASSERT( VOLUME_ID_LENGTH * sizeof( WCHAR ) <= MAXIMUM_VOLUME_LABEL_LENGTH ); + + // + // If this is not a Unicode label we must convert it to unicode. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) { + + // + // Convert the label to unicode. If we get any error then use a name + // length of zero. + // + + Vcb->Vpb->VolumeLabelLength = 0; + + if (NT_SUCCESS( RtlOemToUnicodeN( &Vcb->Vpb->VolumeLabel[0], + MAXIMUM_VOLUME_LABEL_LENGTH, + &Length, + CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH ))) { + + Vcb->Vpb->VolumeLabelLength = (USHORT) Length; + } + + // + // We need to convert from big-endian to little endian. + // + + } else { + + CdConvertBigToLittleEndian( IrpContext, + CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH, + (PCHAR) Vcb->Vpb->VolumeLabel ); + + Vcb->Vpb->VolumeLabelLength = VOLUME_ID_LENGTH * sizeof( WCHAR ); + } + + // + // Strip the trailing spaces or zeroes from the name. + // + + Index = Vcb->Vpb->VolumeLabelLength / sizeof( WCHAR ); + + while (Index > 0) { + + if ((Vcb->Vpb->VolumeLabel[ Index - 1 ] != L'\0') && + (Vcb->Vpb->VolumeLabel[ Index - 1 ] != L' ')) { + + break; + } + + Index -= 1; + } + + // + // Now set the final length for the name. + // + + Vcb->Vpb->VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR )); +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/fspdisp.c b/reactos/drivers/filesystems/cdfs_new/fspdisp.c new file mode 100755 index 00000000000..b7c4e80b62a --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/fspdisp.c @@ -0,0 +1,278 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FspDisp.c + +Abstract: + + This module implements the main dispatch procedure/thread for the Cdfs + Fsp + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FSPDISP) + + +VOID +CdFspDispatch ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This is the main FSP thread routine that is executed to receive + and dispatch IRP requests. Each FSP thread begins its execution here. + There is one thread created at system initialization time and subsequent + threads created as needed. + +Arguments: + + IrpContext - IrpContext for a request to process. + +Return Value: + + None + +--*/ + +{ + THREAD_CONTEXT ThreadContext; + NTSTATUS Status; + + PIRP Irp = IrpContext->Irp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVOLUME_DEVICE_OBJECT VolDo = NULL; + + // + // If this request has an associated volume device object, remember it. + // + + if (IrpSp->FileObject != NULL) { + + VolDo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + } + + // + // Now case on the function code. For each major function code, + // either call the appropriate worker routine. This routine that + // we call is responsible for completing the IRP, and not us. + // That way the routine can complete the IRP and then continue + // post processing as required. For example, a read can be + // satisfied right away and then read can be done. + // + // We'll do all of the work within an exception handler that + // will be invoked if ever some underlying operation gets into + // trouble. + // + + while ( TRUE ) { + + // + // Set all the flags indicating we are in the Fsp. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FSP_FLAGS ); + + FsRtlEnterFileSystem(); + + CdSetThreadContext( IrpContext, &ThreadContext ); + + while (TRUE) { + + try { + + // + // Reinitialize for the next try at completing this + // request. + // + + Status = + IrpContext->ExceptionStatus = STATUS_SUCCESS; + + // + // Initialize the Io status field in the Irp. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Case on the major irp code. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + CdCommonCreate( IrpContext, Irp ); + break; + + case IRP_MJ_CLOSE : + + ASSERT( FALSE ); + break; + + case IRP_MJ_READ : + + CdCommonRead( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_INFORMATION : + + CdCommonQueryInfo( IrpContext, Irp ); + break; + + case IRP_MJ_SET_INFORMATION : + + CdCommonSetInfo( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_VOLUME_INFORMATION : + + CdCommonQueryVolInfo( IrpContext, Irp ); + break; + + case IRP_MJ_DIRECTORY_CONTROL : + + CdCommonDirControl( IrpContext, Irp ); + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL : + + CdCommonFsControl( IrpContext, Irp ); + break; + + case IRP_MJ_DEVICE_CONTROL : + + CdCommonDevControl( IrpContext, Irp ); + break; + + case IRP_MJ_LOCK_CONTROL : + + CdCommonLockControl( IrpContext, Irp ); + break; + + case IRP_MJ_CLEANUP : + + CdCommonCleanup( IrpContext, Irp ); + break; + + case IRP_MJ_PNP : + + ASSERT( FALSE ); + CdCommonPnp( IrpContext, Irp ); + break; + + default : + + Status = STATUS_INVALID_DEVICE_REQUEST; + CdCompleteRequest( IrpContext, Irp, Status ); + } + + } except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + // + // Break out of the loop if we didn't get CANT_WAIT. + // + + if (Status != STATUS_CANT_WAIT) { break; } + + // + // We are retrying this request. Cleanup the IrpContext for the retry. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, FALSE ); + } + + FsRtlExitFileSystem(); + + // + // If there are any entries on this volume's overflow queue, service + // them. + // + + if (VolDo != NULL) { + + KIRQL SavedIrql; + PVOID Entry = NULL; + + // + // We have a volume device object so see if there is any work + // left to do in its overflow queue. + // + + KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql ); + + if (VolDo->OverflowQueueCount > 0) { + + // + // There is overflow work to do in this volume so we'll + // decrement the Overflow count, dequeue the IRP, and release + // the Event + // + + VolDo->OverflowQueueCount -= 1; + + Entry = RemoveHeadList( &VolDo->OverflowQueue ); + } + + KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql ); + + // + // There wasn't an entry, break out of the loop and return to + // the Ex Worker thread. + // + + if (Entry == NULL) { break; } + + // + // Extract the IrpContext , Irp, set wait to TRUE, and loop. + // + + IrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + continue; + } + + break; + } + + // + // Decrement the PostedRequestCount if there was a volume device object. + // + + if (VolDo) { + + InterlockedDecrement( &VolDo->PostedRequestCount ); + } + + return; +} + + + + diff --git a/reactos/drivers/filesystems/cdfs_new/lockctrl.c b/reactos/drivers/filesystems/cdfs_new/lockctrl.c new file mode 100755 index 00000000000..951ca15fe91 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/lockctrl.c @@ -0,0 +1,674 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + LockCtrl.c + +Abstract: + + This module implements the Lock Control routines for Cdfs called + by the Fsd/Fsp dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_LOCKCTRL) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonLockControl) +#pragma alloc_text(PAGE, CdFastLock) +#pragma alloc_text(PAGE, CdFastUnlockAll) +#pragma alloc_text(PAGE, CdFastUnlockAllByKey) +#pragma alloc_text(PAGE, CdFastUnlockSingle) +#endif + + +NTSTATUS +CdCommonLockControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for Lock Control called by both the fsd and fsp + threads. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Extract and decode the type of file object we're being asked to process + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // If the file is not a user file open then we reject the request + // as an invalid parameter + // + + if (TypeOfOpen != UserFileOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // We check whether we can proceed based on the state of the file oplocks. + // This call might post the irp for us. + // + + Status = FsRtlCheckOplock( &Fcb->Oplock, + Irp, + IrpContext, + CdOplockComplete, + NULL ); + + // + // If we don't get success then the oplock package completed the request. + // + + if (Status != STATUS_SUCCESS) { + + return Status; + } + + // + // Verify the Fcb. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // If we don't have a file lock, then get one now. + // + + if (Fcb->FileLock == NULL) { CdCreateFileLock( IrpContext, Fcb, TRUE ); } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + + Status = FsRtlProcessFileLock( Fcb->FileLock, Irp, NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + // + // Complete the request. + // + + CdCompleteRequest( IrpContext, NULL, Status ); + return Status; +} + + +BOOLEAN +CdFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + BOOLEAN ExclusiveLock, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast lock call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + FailImmediately - Indicates if the request should fail immediately + if the lock cannot be granted. + + ExclusiveLock - Indicates if this is a request for an exclusive or + shared lock + + IoStatus - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + + PFCB Fcb; + TYPE_OF_OPEN TypeOfOpen; + + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + FsRtlEnterFileSystem(); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to perform the lock request. + // + + if (Results = FsRtlFastLock( Fcb->FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + FailImmediately, + ExclusiveLock, + IoStatus, + NULL, + FALSE )) { + + // + // Set the flag indicating if Fast I/O is questionable. We + // only change this flag if the current state is possible. + // Retest again after synchronizing on the header. + // + + if (Fcb->IsFastIoPossible == FastIoIsPossible) { + + CdLockFcb( NULL, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( NULL, Fcb ); + } + } + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock single call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockSingle( Fcb->FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + NULL, + FALSE ); + + // + // Set the flag indicating if Fast I/O is possible. We are + // only concerned if there are no longer any filelocks on this + // file. + // + + if (!FsRtlAreThereCurrentFileLocks( Fcb->FileLock ) && + (Fcb->IsFastIoPossible != FastIoIsPossible)) { + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + } + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockAll ( + IN PFILE_OBJECT FileObject, + PEPROCESS ProcessId, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAll( Fcb->FileLock, + FileObject, + ProcessId, + NULL ); + + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all by key call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAllByKey( Fcb->FileLock, + FileObject, + ProcessId, + Key, + NULL ); + + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/namesup.c b/reactos/drivers/filesystems/cdfs_new/namesup.c new file mode 100755 index 00000000000..b07cbf5dcc7 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/namesup.c @@ -0,0 +1,1044 @@ +/*++ + +Copyright (c) 1991-2000 Microsoft Corporation + +Module Name: + + NameSup.c + +Abstract: + + This module implements the Cdfs Name support routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdConvertBigToLittleEndian) +#pragma alloc_text(PAGE, CdConvertNameToCdName) +#pragma alloc_text(PAGE, CdDissectName) +#pragma alloc_text(PAGE, CdGenerate8dot3Name) +#pragma alloc_text(PAGE, CdFullCompareNames) +#pragma alloc_text(PAGE, CdIs8dot3Name) +#pragma alloc_text(PAGE, CdIsNameInExpression) +#pragma alloc_text(PAGE, CdShortNameDirentOffset) +#pragma alloc_text(PAGE, CdUpcaseName) +#endif + + +VOID +CdConvertNameToCdName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PCD_NAME CdName + ) + +/*++ + +Routine Description: + + This routine is called to convert a string of bytes into a CdName. + + The full name is already in the CdName structure in the FileName field. + We split this into the filename and version strings. + +Arguments: + + CdName - Pointer to CdName structure to update. + +Return Value: + + None. + +--*/ + +{ + ULONG NameLength = 0; + PWCHAR CurrentCharacter = CdName->FileName.Buffer; + + PAGED_CODE(); + + // + // Look for a separator character. + // + + while ((NameLength < CdName->FileName.Length) && + (*CurrentCharacter != L';')) { + + CurrentCharacter += 1; + NameLength += 2; + } + + // + // If there is at least one more character after a possible separator then it + // and all following characters are part of the version string. + // + + CdName->VersionString.Length = 0; + if (NameLength + sizeof( WCHAR ) < CdName->FileName.Length) { + + CdName->VersionString.MaximumLength = + CdName->VersionString.Length = (USHORT) (CdName->FileName.Length - NameLength - sizeof( WCHAR )); + CdName->VersionString.Buffer = Add2Ptr( CdName->FileName.Buffer, + NameLength + sizeof( WCHAR ), + PWCHAR ); + } + + // + // Now update the filename portion of the name. + // + + CdName->FileName.Length = (USHORT) NameLength; + + return; +} + + +VOID +CdConvertBigToLittleEndian ( + IN PIRP_CONTEXT IrpContext, + IN PCHAR BigEndian, + IN ULONG ByteCount, + OUT PCHAR LittleEndian + ) + +/*++ + +Routine Description: + + This routine is called to convert a unicode string in big endian to + little endian. We start by copying all of the source bytes except + the first. This will put the low order bytes in the correct position. + We then copy each high order byte in its correct position. + +Arguments: + + BigEndian - Pointer to the string of big endian characters. + + ByteCount - Number of unicode characters in this string. + + LittleEndian - Pointer to array to store the little endian characters. + +Return Value: + + None. + +--*/ + +{ + ULONG RemainingByteCount = ByteCount; + + PCHAR Source = BigEndian; + PCHAR Destination = LittleEndian; + + PAGED_CODE(); + + // + // If the byte count isn't an even number then the disk is corrupt. + // + + if (FlagOn( ByteCount, 1 )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Start by copy the low-order bytes into the correct position. Do + // this by skipping the first byte in the BigEndian string. + // + + RtlCopyMemory( Destination, + Source + 1, + RemainingByteCount - 1 ); + + // + // Now move the high-order bytes into position. + // + + Destination += 1; + + while (RemainingByteCount != 0) { + + *Destination = *Source; + + Source += 2; + Destination += 2; + + RemainingByteCount -= 2; + } + + return; +} + + +VOID +CdUpcaseName ( + IN PIRP_CONTEXT IrpContext, + IN PCD_NAME Name, + IN OUT PCD_NAME UpcaseName + ) + +/*++ + +Routine Description: + + This routine is called to upcase a CdName structure. We will do both + the filename and version strings. + +Arguments: + + Name - This is the mixed case version of the name. + + UpcaseName - This is the destination for the upcase operation. + +Return Value: + + None. This routine will raise all errors. + +--*/ + +{ + NTSTATUS Status; + PVOID NewBuffer; + + PAGED_CODE(); + + // + // If the name structures are different then initialize the different components. + // + + if (Name != UpcaseName) { + + // + // Initialize the version string portion of the name. + // + + UpcaseName->VersionString.Length = 0; + + if (Name->VersionString.Length != 0) { + + UpcaseName->VersionString.MaximumLength = + UpcaseName->VersionString.Length = Name->VersionString.Length; + + // + // Initially set the buffer to point to where we need to insert + // the separator. + // + + UpcaseName->VersionString.Buffer = Add2Ptr( UpcaseName->FileName.Buffer, + Name->FileName.Length, + PWCHAR ); + + // + // We are currently pointing to the location to store the separator. + // Store the ';' and then move to the next character to + // copy the data. + // + + *(UpcaseName->VersionString.Buffer) = L';'; + + UpcaseName->VersionString.Buffer += 1; + } + } + + // + // Upcase the string using the correct upcase routine. + // + + Status = RtlUpcaseUnicodeString( &UpcaseName->FileName, + &Name->FileName, + FALSE ); + + // + // This should never fail. + // + + ASSERT( Status == STATUS_SUCCESS ); + + if (Name->VersionString.Length != 0) { + + Status = RtlUpcaseUnicodeString( &UpcaseName->VersionString, + &Name->VersionString, + FALSE ); + + // + // This should never fail. + // + + ASSERT( Status == STATUS_SUCCESS ); + } + + return; +} + + +VOID +CdDissectName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PUNICODE_STRING RemainingName, + OUT PUNICODE_STRING FinalName + ) + +/*++ + +Routine Description: + + This routine is called to strip off leading components of the name strings. We search + for either the end of the string or separating characters. The input remaining + name strings should have neither a trailing or leading backslash. + +Arguments: + + RemainingName - Remaining name. + + FinalName - Location to store next component of name. + +Return Value: + + None. + +--*/ + +{ + ULONG NameLength; + PWCHAR NextWchar; + + PAGED_CODE(); + + // + // Find the offset of the next component separators. + // + + for (NameLength = 0, NextWchar = RemainingName->Buffer; + (NameLength < RemainingName->Length) && (*NextWchar != L'\\'); + NameLength += sizeof( WCHAR) , NextWchar += 1); + + // + // Adjust all the strings by this amount. + // + + FinalName->Buffer = RemainingName->Buffer; + + FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength; + + // + // If this is the last component then set the RemainingName lengths to zero. + // + + if (NameLength == RemainingName->Length) { + + RemainingName->Length = 0; + + // + // Otherwise we adjust the string by this amount plus the separating character. + // + + } else { + + RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR )); + RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR )); + RemainingName->Buffer = Add2Ptr( RemainingName->Buffer, + NameLength + sizeof( WCHAR ), + PWCHAR ); + } + + return; +} + + +BOOLEAN +CdIs8dot3Name ( + IN PIRP_CONTEXT IrpContext, + IN UNICODE_STRING FileName + ) + +/*++ + +Routine Description: + + This routine checks if the name follows the 8.3 name conventions. We check for + the name length and whether the characters are valid. + +Arguments: + + FileName - String of bytes containing the name. + +Return Value: + + BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise. + +--*/ + +{ + CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ]; + STRING DbcsName; + + PWCHAR NextWchar; + ULONG Count; + + ULONG DotCount = 0; + BOOLEAN LastCharDot = FALSE; + + PAGED_CODE(); + + // + // The length must be less than 24 bytes. + // + + ASSERT( FileName.Length != 0 ); + if (FileName.Length > BYTE_COUNT_8_DOT_3) { + + return FALSE; + } + + // + // Walk though and check for a space character. + // + + NextWchar = FileName.Buffer; + Count = 0; + + do { + + // + // No spaces allowed. + // + + if (*NextWchar == L' ') { return FALSE; } + + if (*NextWchar == L'.') { + + // + // Not an 8.3 name if more than 1 dot or more than 8 characters + // remaining. (It is legal for the dot to be in the ninth + // position) + // + + if ((DotCount > 0) || + (Count > 8 * sizeof( WCHAR ))) { + + return FALSE; + } + + DotCount += 1; + LastCharDot = TRUE; + + } else { + + LastCharDot = FALSE; + } + + Count += 2; + NextWchar += 1; + + } while (Count < FileName.Length); + + // + // Go ahead and truncate the dot if at the end. + // + + if (LastCharDot) { + + FileName.Length -= sizeof( WCHAR ); + } + + // + // Create an Oem name to use to check for a valid short name. + // + + DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3; + DbcsName.Buffer = DbcsNameBuffer; + + if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName, + &FileName, + FALSE ))) { + + return FALSE; + } + + // + // We have now initialized the Oem string. Call the FsRtl package to check for a + // valid FAT name. + // + + return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE ); +} + + +VOID +CdGenerate8dot3Name ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING FileName, + IN ULONG DirentOffset, + OUT PWCHAR ShortFileName, + OUT PUSHORT ShortByteCount + ) + +/*++ + +Routine Description: + + This routine is called to generate a short name from the given long name. We will + generate a short name from the given long name. + + We go through the following steps to make this conversion. + + 1 - Generate the generic short name. This will also be in unicode format. + + 2 - Build the string representation of the dirent offset. + + 3 - Build the biased short name string by combining the generic short name with + the dirent offset string. + + 4 - Copy the final unicode string back to our caller's buffer. + +Arguments: + + FileName - String of bytes containing the name. + + DirentOffset - Offset in the directory for this filename. We incorporate the offset into + the short name by dividing this by 32 and prepending a tilde character to the + digit character. We then append this to the base of the generated short name. + + ShortFileName - Pointer to the buffer to store the short name into. + + ShortByteCount - Address to store the number of bytes in the short name. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING ShortName; + UNICODE_STRING BiasedShortName; + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; + WCHAR BiasedShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; + + GENERATE_NAME_CONTEXT NameContext; + + ULONG BiasedDirentOffset; + + ULONG MaximumBaseBytes; + ULONG BaseNameOffset; + + PWCHAR NextWchar; + WCHAR ThisWchar; + USHORT Length; + + BOOLEAN FoundTilde = FALSE; + + OEM_STRING OemName; + USHORT OemNameOffset = 0; + BOOLEAN OverflowBuffer = FALSE; + + PAGED_CODE(); + + // + // Initialize the short string to use the input buffer. + // + + ShortName.Buffer = ShortNameBuffer; + ShortName.MaximumLength = BYTE_COUNT_8_DOT_3; + + // + // Initialize the name context. + // + + RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT )); + + // + // We now have the unicode name for the input string. Go ahead and generate + // the short name. + // + + RtlGenerate8dot3Name( FileName, TRUE, &NameContext, &ShortName ); + + // + // We now have the generic short name. We want incorporate the dirent offset + // into the name in order to reduce the chance of name conflicts. We will use + // a tilde character followed by a character representation of the dirent offset. + // This will be the hexadecimal representation of the dirent offset in the directory. + // It is actuall this offset divided by 32 since we don't need the full + // granularity. + // + + BiasedDirentOffset = DirentOffset >> SHORT_NAME_SHIFT; + + // + // Point to a local buffer to store the offset string. We start + // at the end of the buffer and work backwards. + // + + NextWchar = Add2Ptr( BiasedShortNameBuffer, + BYTE_COUNT_8_DOT_3, + PWCHAR ); + + BiasedShortName.MaximumLength = BYTE_COUNT_8_DOT_3; + + // + // Generate an OEM version of the string so that we can check for double + // byte characters. + // + + Status = RtlUnicodeStringToOemString(&OemName, &ShortName, TRUE); + + if (!NT_SUCCESS( Status )) { + + CdRaiseStatus( IrpContext, Status ); + } + + Length = 0; + + // + // Now add the characters for the dirent offset. We need to start + // from the least significant digit and work backwards. + // + + do { + + NextWchar -= 1; + + ThisWchar = (WCHAR) (BiasedDirentOffset & 0x0000000f); + + // + // Store in the next character. Bias against either '0' or 'A' + // + + if (ThisWchar <= 9) { + + *NextWchar = ThisWchar + L'0'; + + } else { + + *NextWchar = ThisWchar + L'A' - 0xA; + } + + Length += sizeof( WCHAR ); + + // + // Shift out the low 4 bits of the offset. + // + + BiasedDirentOffset >>= 4; + + } while (BiasedDirentOffset != 0); + + // + // Now store in the tilde character. + // + + NextWchar -= 1; + *NextWchar = L'~'; + Length += sizeof( WCHAR ); + + // + // Set the length of this string. + // + + BiasedShortName.Length = Length; + BiasedShortName.Buffer = NextWchar; + + // + // Figure out the maximum number of characters we can copy of the base + // name. We subract the number of characters in the dirent string from 8. + // We will copy this many characters or stop when we reach a '.' character + // or a '~' character in the name. + // + + MaximumBaseBytes = 16 - Length; + + BaseNameOffset = 0; + + // + // Keep copying from the base name until we hit a '.', '~' or the end of + // the short name. + // + + NextWchar = ShortFileName; + Length = 0; + + while ((BaseNameOffset < ShortName.Length) && + (ShortName.Buffer[BaseNameOffset / 2] != L'.')) { + + // + // Remember if we found a tilde character in the short name, + // so we don't copy it or anything following it. + // + + if (ShortName.Buffer[BaseNameOffset / 2] == L'~') { + + FoundTilde = TRUE; + } + + // + // We need to consider the DBCS code page, because Unicode characters + // may use 2 bytes as DBCS characters. + // + + if (FsRtlIsLeadDbcsCharacter(OemName.Buffer[OemNameOffset])) { + + OemNameOffset += 2; + } + else { + + OemNameOffset++; + } + + if ((OemNameOffset + (BiasedShortName.Length / sizeof(WCHAR))) > 8) { + + OverflowBuffer = TRUE; + } + + // + // Only copy the bytes if we still have space for the dirent string. + // + + if (!FoundTilde && !OverflowBuffer && (BaseNameOffset < MaximumBaseBytes)) { + + *NextWchar = ShortName.Buffer[BaseNameOffset / 2]; + Length += sizeof( WCHAR ); + NextWchar += 1; + } + + BaseNameOffset += 2; + } + + RtlFreeOemString(&OemName); + + // + // Now copy the dirent string into the biased name buffer. + // + + RtlCopyMemory( NextWchar, + BiasedShortName.Buffer, + BiasedShortName.Length ); + + Length += BiasedShortName.Length; + NextWchar += (BiasedShortName.Length / sizeof( WCHAR )); + + // + // Now copy any remaining bytes over to the biased short name. + // + + if (BaseNameOffset != ShortName.Length) { + + RtlCopyMemory( NextWchar, + &ShortName.Buffer[BaseNameOffset / 2], + ShortName.Length - BaseNameOffset ); + + Length += (ShortName.Length - (USHORT) BaseNameOffset); + } + + // + // The final short name is stored in the user's buffer. + // + + *ShortByteCount = Length; + + return; +} + + +BOOLEAN +CdIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN PCD_NAME CurrentName, + IN PCD_NAME SearchExpression, + IN ULONG WildcardFlags, + IN BOOLEAN CheckVersion + ) + +/*++ + +Routine Description: + + This routine will compare two CdName strings. We assume that if this + is to be a case-insensitive search then they are already upcased. + + We compare the filename portions of the name and if they match we + compare the version strings if requested. + +Arguments: + + CurrentName - Filename from the disk. + + SearchExpression - Filename expression to use for match. + + WildcardFlags - Flags field which indicates which parts of the + search expression might have wildcards. These flags are the + same as in the Ccb flags field. + + CheckVersion - Indicates whether we should check both the name and the + version strings or just the name. + +Return Value: + + BOOLEAN - TRUE if the expressions match, FALSE otherwise. + +--*/ + +{ + BOOLEAN Match = TRUE; + PAGED_CODE(); + + // + // If there are wildcards in the expression then we call the + // appropriate FsRtlRoutine. + // + + if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD )) { + + Match = FsRtlIsNameInExpression( &SearchExpression->FileName, + &CurrentName->FileName, + FALSE, + NULL ); + + // + // Otherwise do a direct memory comparison for the name string. + // + + } else { + + if ((CurrentName->FileName.Length != SearchExpression->FileName.Length) || + (!RtlEqualMemory( CurrentName->FileName.Buffer, + SearchExpression->FileName.Buffer, + CurrentName->FileName.Length ))) { + + Match = FALSE; + } + } + + // + // Check the version numbers if requested by the user and we have a + // match on the name and the version number is present. + // + + if (Match && CheckVersion && SearchExpression->VersionString.Length && + !FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL )) { + + // + // If there are wildcards in the expression then call the + // appropriate search expression. + // + + if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD )) { + + Match = FsRtlIsNameInExpression( &SearchExpression->VersionString, + &CurrentName->VersionString, + FALSE, + NULL ); + + // + // Otherwise do a direct memory comparison for the name string. + // + + } else { + + if ((CurrentName->VersionString.Length != SearchExpression->VersionString.Length) || + (!RtlEqualMemory( CurrentName->VersionString.Buffer, + SearchExpression->VersionString.Buffer, + CurrentName->VersionString.Length ))) { + + Match = FALSE; + } + } + } + + return Match; +} + + +ULONG +CdShortNameDirentOffset ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This routine is called to examine a name to see if the dirent offset string is contained. + This consists of a tilde character followed by the offset represented as a hexadecimal + characters. We don't do any other checks to see if this is a short name. We + catch that later outside this routine. + +Arguments: + + Name - This is the CdName to examine. + +Return Value: + + ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the + convert the value to numeric form. + +--*/ + +{ + ULONG ResultOffset = MAXULONG; + ULONG RemainingByteCount = Name->Length; + + BOOLEAN FoundTilde = FALSE; + + PWCHAR NextWchar; + + PAGED_CODE(); + + // + // Walk through the name until we either reach the end of the name + // or find a tilde character. + // + + for (NextWchar = Name->Buffer; + RemainingByteCount != 0; + NextWchar += 1, RemainingByteCount -= sizeof( WCHAR )) { + + // + // Check if this is a dot. Stop constructing any string if + // we found a dot. + // + + if (*NextWchar == L'.') { + + break; + } + + // + // If we already found a tilde then check this character as a + // valid character. It must be a digit or A to F. + // + + if (FoundTilde) { + + if ((*NextWchar < L'0') || + (*NextWchar > L'F') || + ((*NextWchar > L'9') && (*NextWchar < 'A'))) { + + ResultOffset = MAXULONG; + break; + } + + // + // Shift the result by 4 bits and add in this new character. + // + + ResultOffset <<= 4; + + if (*NextWchar < L'A') { + + ResultOffset += *NextWchar - L'0'; + + } else { + + ResultOffset += (*NextWchar - L'A') + 10; + } + + continue; + } + + // + // If this is a tilde then start building the dirent string. + // + + if (*NextWchar == L'~') { + + FoundTilde = TRUE; + ResultOffset = 0; + } + } + + return ResultOffset; +} + + +// +// Local support routine +// + +FSRTL_COMPARISON_RESULT +CdFullCompareNames ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING NameA, + IN PUNICODE_STRING NameB + ) + +/*++ + +Routine Description: + + This function compares two names as fast as possible. Note that since + this comparison is case sensitive we can do a direct memory comparison. + +Arguments: + + NameA & NameB - The names to compare. + +Return Value: + + COMPARISON - returns + + LessThan if NameA < NameB lexicalgraphically, + GreaterThan if NameA > NameB lexicalgraphically, + EqualTo if NameA is equal to NameB + +--*/ + +{ + SIZE_T i; + ULONG MinLength = NameA->Length; + FSRTL_COMPARISON_RESULT Result = LessThan; + + PAGED_CODE(); + + // + // Figure out the minimum of the two lengths + // + + if (NameA->Length > NameB->Length) { + + MinLength = NameB->Length; + Result = GreaterThan; + + } else if (NameA->Length == NameB->Length) { + + Result = EqualTo; + } + + // + // Loop through looking at all of the characters in both strings + // testing for equalilty, less than, and greater than + // + + i = RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); + + if (i < MinLength) { + + // + // We know the offset of the first character which is different. + // + + return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ? + LessThan : + GreaterThan); + } + + // + // The names match up to the length of the shorter string. + // The shorter string lexically appears first. + // + + return Result; +} + + + diff --git a/reactos/drivers/filesystems/cdfs_new/nodetype.h b/reactos/drivers/filesystems/cdfs_new/nodetype.h new file mode 100755 index 00000000000..b227332b035 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/nodetype.h @@ -0,0 +1,103 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + NodeType.h + +Abstract: + + This module defines all of the node type codes used in this development + shell. Every major data structure in the file system is assigned a node + type code that is. This code is the first CSHORT in the structure and is + followed by a CSHORT containing the size, in bytes, of the structure. + + +--*/ + +#ifndef _CDNODETYPE_ +#define _CDNODETYPE_ + +typedef CSHORT NODE_TYPE_CODE; +typedef NODE_TYPE_CODE *PNODE_TYPE_CODE; + +#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000) + +#define CDFS_NTC_DATA_HEADER ((NODE_TYPE_CODE)0x0301) +#define CDFS_NTC_VCB ((NODE_TYPE_CODE)0x0302) +#define CDFS_NTC_FCB_PATH_TABLE ((NODE_TYPE_CODE)0x0303) +#define CDFS_NTC_FCB_INDEX ((NODE_TYPE_CODE)0x0304) +#define CDFS_NTC_FCB_DATA ((NODE_TYPE_CODE)0x0305) +#define CDFS_NTC_FCB_NONPAGED ((NODE_TYPE_CODE)0x0306) +#define CDFS_NTC_CCB ((NODE_TYPE_CODE)0x0307) +#define CDFS_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0308) +#define CDFS_NTC_IRP_CONTEXT_LITE ((NODE_TYPE_CODE)0x0309) + +typedef CSHORT NODE_BYTE_SIZE; + +// +// So all records start with +// +// typedef struct _RECORD_NAME { +// NODE_TYPE_CODE NodeTypeCode; +// NODE_BYTE_SIZE NodeByteSize; +// : +// } RECORD_NAME; +// typedef RECORD_NAME *PRECORD_NAME; +// + +#ifndef NodeType +#define NodeType(P) ((P) != NULL ? (*((PNODE_TYPE_CODE)(P))) : NTC_UNDEFINED) +#endif +#ifndef SafeNodeType +#define SafeNodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr))) +#endif + +// +// The following definitions are used to generate meaningful blue bugcheck +// screens. On a bugcheck the file system can output 4 ulongs of useful +// information. The first ulong will have encoded in it a source file id +// (in the high word) and the line number of the bugcheck (in the low word). +// The other values can be whatever the caller of the bugcheck routine deems +// necessary. +// +// Each individual file that calls bugcheck needs to have defined at the +// start of the file a constant called BugCheckFileId with one of the +// CDFS_BUG_CHECK_ values defined below and then use CdBugCheck to bugcheck +// the system. +// + +#define CDFS_BUG_CHECK_ACCHKSUP (0x00010000) +#define CDFS_BUG_CHECK_ALLOCSUP (0x00020000) +#define CDFS_BUG_CHECK_CACHESUP (0x00030000) +#define CDFS_BUG_CHECK_CDDATA (0x00040000) +#define CDFS_BUG_CHECK_CDINIT (0x00050000) +#define CDFS_BUG_CHECK_CLEANUP (0x00060000) +#define CDFS_BUG_CHECK_CLOSE (0x00070000) +#define CDFS_BUG_CHECK_CREATE (0x00080000) +#define CDFS_BUG_CHECK_DEVCTRL (0x00090000) +#define CDFS_BUG_CHECK_DEVIOSUP (0x000a0000) +#define CDFS_BUG_CHECK_DIRCTRL (0x000b0000) +#define CDFS_BUG_CHECK_DIRSUP (0x000c0000) +#define CDFS_BUG_CHECK_FILEINFO (0x000d0000) +#define CDFS_BUG_CHECK_FILOBSUP (0x000e0000) +#define CDFS_BUG_CHECK_FSCTRL (0x000f0000) +#define CDFS_BUG_CHECK_FSPDISP (0x00100000) +#define CDFS_BUG_CHECK_LOCKCTRL (0x00110000) +#define CDFS_BUG_CHECK_NAMESUP (0x00120000) +#define CDFS_BUG_CHECK_PATHSUP (0x00130000) +#define CDFS_BUG_CHECK_PNP (0x00140000) +#define CDFS_BUG_CHECK_PREFXSUP (0x00150000) +#define CDFS_BUG_CHECK_READ (0x00160000) +#define CDFS_BUG_CHECK_RESRCSUP (0x00170000) +#define CDFS_BUG_CHECK_STRUCSUP (0x00180000) +#define CDFS_BUG_CHECK_TIMESUP (0x00190000) +#define CDFS_BUG_CHECK_VERFYSUP (0x001a0000) +#define CDFS_BUG_CHECK_VOLINFO (0x001b0000) +#define CDFS_BUG_CHECK_WORKQUE (0x001c0000) + +#define CdBugCheck(A,B,C) { KeBugCheckEx(CDFS_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); } + +#endif // _NODETYPE_ + diff --git a/reactos/drivers/filesystems/cdfs_new/pathsup.c b/reactos/drivers/filesystems/cdfs_new/pathsup.c new file mode 100755 index 00000000000..66dbc5cd900 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/pathsup.c @@ -0,0 +1,997 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PathSup.c + +Abstract: + + This module implements the Path Table support routines for Cdfs. + + The path table on a CDROM is a condensed summary of the entire + directory structure. It is stored on a number of contiguous sectors + on the disk. Each directory on the disk has an entry in the path + table. The entries are aligned on USHORT boundaries and MAY span + sector boundaries. The entries are stored as a breadth-first search. + + The first entry in the table contains the entry for the root. The + next entries will consist of the contents of the root directory. The + next entries will consist of the all the directories at the next level + of the tree. The children of a given directory will be grouped together. + + The directories are assigned ordinal numbers based on their position in + the path table. The root dirctory is assigned ordinal value 1. + + Path table sectors: + + Ordinal 1 2 3 4 5 6 + +-----------+ + | Spanning | + | Sectors | + +----------------------------+ +------------------------+ + | | | | | | | | | + DirName | \ | a | b |c| | c | d | e | + | | | | | | | | | + Parent #| 1 | 1 | 1 | | | 2 | 2 | 3 | + +----------------------------+ +------------------------+ + + Directory Tree: + + \ (root) + + / \ + / \ + a b + + / \ \ + / \ \ + c d e + + Path Table Entries: + + - Position scan at known offset in the path table. Path Entry at + this offset must exist and is known to be valid. Used when + scanning for the children of a given directory. + + - Position scan at known offset in the path table. Path Entry is + known to start at this location but the bounds must be checked + for validity. + + - Move to next path entry in the table. + + - Update a common path entry structure with the details of the + on-disk structure. This is used to smooth out the differences + in the on-disk structures. + + - Update the filename in the in-memory path entry with the bytes + off the disk. For Joliet disks we will have + to convert to little endian. We assume that directories + don't have version numbers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PATHSUP) + +// +// Local macros +// + +// +// PRAW_PATH_ENTRY +// CdRawPathEntry ( +// IN PIRP_CONTEXT IrpContext, +// IN PPATH_ENUM_CONTEXT PathContext +// ); +// + +#define CdRawPathEntry(IC, PC) \ + Add2Ptr( (PC)->Data, (PC)->DataOffset, PRAW_PATH_ENTRY ) + +// +// Local support routines +// + +VOID +CdMapPathTableBlock ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG BaseOffset, + IN OUT PPATH_ENUM_CONTEXT PathContext + ); + +BOOLEAN +CdUpdatePathEntryFromRawPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN ULONG Ordinal, + IN BOOLEAN VerifyBounds, + IN PPATH_ENUM_CONTEXT PathContext, + OUT PPATH_ENTRY PathEntry + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFindPathEntry) +#pragma alloc_text(PAGE, CdLookupPathEntry) +#pragma alloc_text(PAGE, CdLookupNextPathEntry) +#pragma alloc_text(PAGE, CdMapPathTableBlock) +#pragma alloc_text(PAGE, CdUpdatePathEntryFromRawPathEntry) +#pragma alloc_text(PAGE, CdUpdatePathEntryName) +#endif + + +VOID +CdLookupPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN ULONG PathEntryOffset, + IN ULONG Ordinal, + IN BOOLEAN VerifyBounds, + IN OUT PCOMPOUND_PATH_ENTRY CompoundPathEntry + ) + +/*++ + +Routine Description: + + This routine is called to initiate a walk through a path table. We are + looking for a path table entry at location PathEntryOffset. + +Arguments: + + PathEntryOffset - This is our target point in the Path Table. We know that + a path entry must begin at this point although we may have to verify + the bounds. + + Ordinal - Ordinal number for the directory at the PathEntryOffset above. + + VerifyBounds - Indicates whether we need to check the validity of + this entry. + + CompoundPathEntry - PathEnumeration context and in-memory path entry. This + has been initialized outside of this call. + +Return Value: + + None. + +--*/ + +{ + PPATH_ENUM_CONTEXT PathContext = &CompoundPathEntry->PathContext; + LONGLONG CurrentBaseOffset; + + PAGED_CODE(); + + // + // Compute the starting base and starting path table offset. + // + + CurrentBaseOffset = SectorTruncate( PathEntryOffset ); + + // + // Map the next block in the Path Table. + // + + CdMapPathTableBlock( IrpContext, + IrpContext->Vcb->PathTableFcb, + CurrentBaseOffset, + PathContext ); + + // + // Set up our current offset into the Path Context. + // + + PathContext->DataOffset = PathEntryOffset - PathContext->BaseOffset; + + // + // Update the in-memory structure for this path entry. + // + + (VOID) CdUpdatePathEntryFromRawPathEntry( IrpContext, + Ordinal, + VerifyBounds, + &CompoundPathEntry->PathContext, + &CompoundPathEntry->PathEntry ); +} + + +BOOLEAN +CdLookupNextPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN OUT PPATH_ENUM_CONTEXT PathContext, + IN OUT PPATH_ENTRY PathEntry + ) + +/*++ + +Routine Description: + + This routine is called to move to the next path table entry. We know + the offset and the length of the current entry. We start by computing + the offset of the next entry and determine if it is contained in the + table. Then we check to see if we need to move to the next sector in + the path table. We always map two sectors at a time so we don't + have to deal with any path entries which span sectors. We move to + the next sector if we are in the second sector of the current mapped + data block. + + We look up the next entry and update the path entry structure with + the values out of the raw sector but don't update the CdName structure. + +Arguments: + + PathContext - Enumeration context for this scan of the path table. + + PathEntry - In-memory representation of the on-disk path table entry. + +Return Value: + + BOOLEAN - TRUE if another entry is found, FALSE otherwise. + This routine may raise on error. + +--*/ + +{ + LONGLONG CurrentBaseOffset; + + PAGED_CODE(); + + // + // Get the offset of the next path entry within the current + // data block. + // + + PathContext->DataOffset += PathEntry->PathEntryLength; + + // + // If we are in the last data block then check if we are beyond the + // end of the file. + // + + if (PathContext->LastDataBlock) { + + if (PathContext->DataOffset >= PathContext->DataLength) { + + return FALSE; + } + + // + // If we are not in the last data block of the path table and + // this offset is in the second sector then move to the next + // data block. + // + + } else if (PathContext->DataOffset >= SECTOR_SIZE) { + + CurrentBaseOffset = PathContext->BaseOffset + SECTOR_SIZE; + + CdMapPathTableBlock( IrpContext, + IrpContext->Vcb->PathTableFcb, + CurrentBaseOffset, + PathContext ); + + // + // Set up our current offset into the Path Context. + // + + PathContext->DataOffset -= SECTOR_SIZE; + } + + // + // Now update the path entry with the values from the on-disk + // structure. + // + + return CdUpdatePathEntryFromRawPathEntry( IrpContext, + PathEntry->Ordinal + 1, + TRUE, + PathContext, + PathEntry ); +} + + +BOOLEAN +CdFindPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN PFCB ParentFcb, + IN PCD_NAME DirName, + IN BOOLEAN IgnoreCase, + IN OUT PCOMPOUND_PATH_ENTRY CompoundPathEntry + ) + +/*++ + +Routine Description: + + This routine will walk through the path table looking for a matching entry for DirName + among the child directories of the ParentFcb. + +Arguments: + + ParentFcb - This is the directory we are examining. We know the ordinal and path table + offset for this directory in the path table. If this is the first scan for this + Fcb we will update the first child offset for this directory in the path table. + + DirName - This is the name we are searching for. This name will not contain wildcard + characters. The name will also not have a version string. + + IgnoreCase - Indicates if this search is exact or ignore case. + + CompoundPathEntry - Complete path table enumeration structure. We will have initialized + it for the search on entry. This will be positioned at the matching name if found. + +Return Value: + + BOOLEAN - TRUE if matching entry found, FALSE otherwise. + +--*/ + +{ + BOOLEAN Found = FALSE; + BOOLEAN UpdateChildOffset = TRUE; + + ULONG StartingOffset; + ULONG StartingOrdinal; + + PAGED_CODE(); + + // + // Position ourselves at either the first child or at the directory itself. + // Lock the Fcb to get this value and remember whether to update with the first + // child. + // + + StartingOffset = CdQueryFidPathTableOffset( ParentFcb->FileId ); + StartingOrdinal = ParentFcb->Ordinal; + + // + // ISO 9660 9.4.4 restricts the backpointer from child to parent in a + // pathtable entry to 16bits. Although we internally store ordinals + // as 32bit values, it is impossible to search for the children of a + // directory whose ordinal value is greater than MAXUSHORT. Media that + // could induce such a search is illegal. + // + // Note that it is not illegal to have more than MAXUSHORT directories. + // + + if (ParentFcb->Ordinal > MAXUSHORT) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + CdLockFcb( IrpContext, ParentFcb ); + + if (ParentFcb->ChildPathTableOffset != 0) { + + StartingOffset = ParentFcb->ChildPathTableOffset; + StartingOrdinal = ParentFcb->ChildOrdinal; + UpdateChildOffset = FALSE; + + } else if (ParentFcb == ParentFcb->Vcb->RootIndexFcb) { + + UpdateChildOffset = FALSE; + } + + CdUnlockFcb( IrpContext, ParentFcb ); + + CdLookupPathEntry( IrpContext, StartingOffset, StartingOrdinal, FALSE, CompoundPathEntry ); + + // + // Loop until we find a match or are beyond the children for this directory. + // + + do { + + // + // If we are beyond this directory then return FALSE. + // + + if (CompoundPathEntry->PathEntry.ParentOrdinal > ParentFcb->Ordinal) { + + // + // Update the Fcb with the offsets for the children in the path table. + // + + if (UpdateChildOffset) { + + CdLockFcb( IrpContext, ParentFcb ); + + ParentFcb->ChildPathTableOffset = StartingOffset; + ParentFcb->ChildOrdinal = StartingOrdinal; + + CdUnlockFcb( IrpContext, ParentFcb ); + } + + break; + } + + // + // If we are within the children of this directory then check for a match. + // + + if (CompoundPathEntry->PathEntry.ParentOrdinal == ParentFcb->Ordinal) { + + // + // Update the child offset if not yet done. + // + + if (UpdateChildOffset) { + + CdLockFcb( IrpContext, ParentFcb ); + + ParentFcb->ChildPathTableOffset = CompoundPathEntry->PathEntry.PathTableOffset; + ParentFcb->ChildOrdinal = CompoundPathEntry->PathEntry.Ordinal; + + CdUnlockFcb( IrpContext, ParentFcb ); + + UpdateChildOffset = FALSE; + } + + // + // Update the name in the path entry. + // + + CdUpdatePathEntryName( IrpContext, &CompoundPathEntry->PathEntry, IgnoreCase ); + + // + // Now compare the names for an exact match. + // + + if (CdIsNameInExpression( IrpContext, + &CompoundPathEntry->PathEntry.CdCaseDirName, + DirName, + 0, + FALSE )) { + + // + // Let our caller know we have a match. + // + + Found = TRUE; + break; + } + } + + // + // Go to the next entry in the path table. Remember the current position + // in the event we update the Fcb. + // + + StartingOffset = CompoundPathEntry->PathEntry.PathTableOffset; + StartingOrdinal = CompoundPathEntry->PathEntry.Ordinal; + + } while (CdLookupNextPathEntry( IrpContext, + &CompoundPathEntry->PathContext, + &CompoundPathEntry->PathEntry )); + + return Found; +} + + +// +// Local support routine +// + +VOID +CdMapPathTableBlock ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN LONGLONG BaseOffset, + IN OUT PPATH_ENUM_CONTEXT PathContext + ) + +/*++ + +Routine Description: + + This routine is called to map (or allocate and copy) the next + data block in the path table. We check if the next block will + span a view boundary and allocate an auxilary buffer in that case. + +Arguments: + + Fcb - This is the Fcb for the Path Table. + + BaseOffset - Offset of the first sector to map. This will be on a + sector boundary. + + PathContext - Enumeration context to update in this routine. + +Return Value: + + None. + +--*/ + +{ + ULONG CurrentLength; + ULONG SectorSize; + ULONG DataOffset; + ULONG PassCount; + PVOID Sector; + + PAGED_CODE(); + + // + // Map the new block and set the enumeration context to this + // point. Allocate an auxilary buffer if necessary. + // + + CurrentLength = 2 * SECTOR_SIZE; + + if (CurrentLength >= (ULONG) (Fcb->FileSize.QuadPart - BaseOffset)) { + + CurrentLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); + + // + // We know this is the last data block for this + // path table. + // + + PathContext->LastDataBlock = TRUE; + } + + // + // Set context values. + // + + PathContext->BaseOffset = (ULONG) BaseOffset; + PathContext->DataLength = CurrentLength; + + // + // Drop the previous sector's mapping + // + + CdUnpinData( IrpContext, &PathContext->Bcb ); + + // + // Check if spanning a view section. The following must + // be true before we take this step. + // + // Data length is more than one sector. + // Starting offset must be one sector before the + // cache manager VACB boundary. + // + + if ((CurrentLength > SECTOR_SIZE) && + (FlagOn( ((ULONG) BaseOffset), VACB_MAPPING_MASK ) == LAST_VACB_SECTOR_OFFSET )) { + + // + // Map each sector individually and store into an auxilary + // buffer. + // + + SectorSize = SECTOR_SIZE; + DataOffset = 0; + PassCount = 2; + + PathContext->Data = FsRtlAllocatePoolWithTag( CdPagedPool, + CurrentLength, + TAG_SPANNING_PATH_TABLE ); + PathContext->AllocatedData = TRUE; + + while (PassCount--) { + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + SectorSize, + TRUE, + &PathContext->Bcb, + &Sector ); + + RtlCopyMemory( Add2Ptr( PathContext->Data, DataOffset, PVOID ), + Sector, + SectorSize ); + + CdUnpinData( IrpContext, &PathContext->Bcb ); + + BaseOffset += SECTOR_SIZE; + SectorSize = CurrentLength - SECTOR_SIZE; + DataOffset = SECTOR_SIZE; + } + + // + // Otherwise we can just map the data into the cache. + // + + } else { + + // + // There is a slight chance that we have allocated an + // auxilary buffer on the previous sector. + // + + if (PathContext->AllocatedData) { + + CdFreePool( &PathContext->Data ); + PathContext->AllocatedData = FALSE; + } + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + CurrentLength, + TRUE, + &PathContext->Bcb, + &PathContext->Data ); + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdUpdatePathEntryFromRawPathEntry ( + IN PIRP_CONTEXT IrpContext, + IN ULONG Ordinal, + IN BOOLEAN VerifyBounds, + IN PPATH_ENUM_CONTEXT PathContext, + OUT PPATH_ENTRY PathEntry + ) + +/*++ + +Routine Description: + + This routine is called to update the in-memory Path Entry from the on-disk + path entry. We also do a careful check of the bounds if requested and we + are in the last data block of the path table. + +Arguments: + + Ordinal - Ordinal number for this directory. + + VerifyBounds - Check that the current raw Path Entry actually fits + within the data block. + + PathContext - Current path table enumeration context. + + PathEntry - Pointer to the in-memory path entry structure. + +Return Value: + + TRUE if updated ok, + FALSE if we've hit the end of the pathtable - zero name length && PT size is a multiple + of blocksize. This is a workaround for some Video CDs. Win 9x works around this. + + This routine may raise. + +--*/ + +{ + PRAW_PATH_ENTRY RawPathEntry = CdRawPathEntry( IrpContext, PathContext ); + ULONG RemainingDataLength; + + PAGED_CODE(); + + // + // Check for a name length of zero. This is the first byte of the record, + // and there must be at least one byte remaining in the buffer else we + // wouldn't be here (caller would have spotted buffer end). + // + + PathEntry->DirNameLen = CdRawPathIdLen( IrpContext, RawPathEntry ); + + if (0 == PathEntry->DirNameLen) { + + // + // If we are in the last block, and the path table size (ie last block) is a + // multiple of block size, then we will consider this the end of the path table + // rather than raising an error. Workaround for NTI Cd Maker video CDs which + // round path table length to blocksize multiple. In all other cases we consider + // a zero length name to be corruption. + // + + if ( PathContext->LastDataBlock && + (0 == BlockOffset( IrpContext->Vcb, PathContext->DataLength))) { + + return FALSE; + } + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Check if we should verify the path entry. If we are not in the last + // data block then there is nothing to check. + // + + if (PathContext->LastDataBlock && VerifyBounds) { + + // + // Quick check to see if the maximum size is still available. This + // will handle most cases and we don't need to access any of the + // fields. + // + + RemainingDataLength = PathContext->DataLength - PathContext->DataOffset; + + if (RemainingDataLength < sizeof( RAW_PATH_ENTRY )) { + + // + // Make sure the remaining bytes hold the path table entries. + // Do the following checks. + // + // - A minimal path table entry will fit (and then check) + // - This path table entry (with dir name) will fit. + // + + if ((RemainingDataLength < MIN_RAW_PATH_ENTRY_LEN) || + (RemainingDataLength < (ULONG) (CdRawPathIdLen( IrpContext, RawPathEntry ) + MIN_RAW_PATH_ENTRY_LEN - 1))) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + } + } + + // + // The ordinal number of this directory is passed in. + // Compute the path table offset of this entry. + // + + PathEntry->Ordinal = Ordinal; + PathEntry->PathTableOffset = PathContext->BaseOffset + PathContext->DataOffset; + + // + // We know we can safely access all of the fields of the raw path table at + // this point. + + // + // Bias the disk offset by the number of logical blocks + // + + CopyUchar4( &PathEntry->DiskOffset, CdRawPathLoc( IrpContext, RawPathEntry )); + + PathEntry->DiskOffset += CdRawPathXar( IrpContext, RawPathEntry ); + + CopyUchar2( &PathEntry->ParentOrdinal, &RawPathEntry->ParentNum ); + + PathEntry->PathEntryLength = PathEntry->DirNameLen + MIN_RAW_PATH_ENTRY_LEN - 1; + + // + // Align the path entry length on a ushort boundary. + // + + PathEntry->PathEntryLength = WordAlign( PathEntry->PathEntryLength ); + + PathEntry->DirName = RawPathEntry->DirId; + + return TRUE; +} + + +// +// Local support routine +// + +VOID +CdUpdatePathEntryName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PPATH_ENTRY PathEntry, + IN BOOLEAN IgnoreCase + ) + +/*++ + +Routine Description: + + This routine will store the directory name into the CdName in the + path entry. If this is a Joliet name then we will make sure we have + an allocated buffer and need to convert from big endian to little + endian. We also correctly update the case name. If this operation is ignore + case then we need an auxilary buffer for the name. + + For an Ansi disk we can use the name from the disk for the exact case. We only + need to allocate a buffer for the ignore case name. The on-disk representation of + a Unicode name is useless for us. In this case we will need a name buffer for + both names. We store a buffer in the PathEntry which can hold two 8.3 unicode + names. This means we will almost never need to allocate a buffer in the Ansi case + (we only need one buffer and already have 48 characters). + +Arguments: + + PathEntry - Pointer to a path entry structure. We have already updated + this path entry with the values from the raw path entry. + +Return Value: + + None. + +--*/ + +{ + ULONG Length; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check if this is a self entry. We use a fixed string for this. + // + // Self-Entry - Length is 1, value is 0. + // + + if ((*PathEntry->DirName == 0) && + (PathEntry->DirNameLen == 1)) { + + // + // There should be no allocated buffers. + // + + ASSERT( !FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )); + + // + // Now use one of the hard coded directory names. + // + + PathEntry->CdDirName.FileName = CdUnicodeDirectoryNames[0]; + + // + // Show that there is no version number. + // + + PathEntry->CdDirName.VersionString.Length = 0; + + // + // The case name is identical. + // + + PathEntry->CdCaseDirName = PathEntry->CdDirName; + + // + // Return now. + // + + return; + } + + // + // Compute how large a buffer we will need. If this is an ignore + // case operation then we will want a double size buffer. If the disk is not + // a Joliet disk then we might need two bytes for each byte in the name. + // + + Length = PathEntry->DirNameLen; + + if (IgnoreCase) { + + Length *= 2; + } + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Length *= sizeof( WCHAR ); + } + + // + // Now decide if we need to allocate a new buffer. We will if + // this name won't fit in the embedded name buffer and it is + // larger than the current allocated buffer. We always use the + // allocated buffer if present. + // + // If we haven't allocated a buffer then use the embedded buffer if the data + // will fit. This is the typical case. + // + + if (!FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ) && + (Length <= sizeof( PathEntry->NameBuffer ))) { + + PathEntry->CdDirName.FileName.MaximumLength = sizeof( PathEntry->NameBuffer ); + PathEntry->CdDirName.FileName.Buffer = PathEntry->NameBuffer; + + } else { + + // + // We need to use an allocated buffer. Check if the current buffer + // is large enough. + // + + if (Length > PathEntry->CdDirName.FileName.MaximumLength) { + + // + // Free any allocated buffer. + // + + if (FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) { + + CdFreePool( &PathEntry->CdDirName.FileName.Buffer ); + ClearFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); + } + + PathEntry->CdDirName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + Length, + TAG_PATH_ENTRY_NAME ); + + SetFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); + + PathEntry->CdDirName.FileName.MaximumLength = (USHORT) Length; + } + } + + // + // We now have a buffer for the name. We need to either convert the on-disk bigendian + // to little endian or covert the name to Unicode. + // + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Status = RtlOemToUnicodeN( PathEntry->CdDirName.FileName.Buffer, + PathEntry->CdDirName.FileName.MaximumLength, + &Length, + PathEntry->DirName, + PathEntry->DirNameLen ); + + ASSERT( Status == STATUS_SUCCESS ); + PathEntry->CdDirName.FileName.Length = (USHORT) Length; + + } else { + + // + // Convert this string to little endian. + // + + CdConvertBigToLittleEndian( IrpContext, + PathEntry->DirName, + PathEntry->DirNameLen, + (PCHAR) PathEntry->CdDirName.FileName.Buffer ); + + PathEntry->CdDirName.FileName.Length = (USHORT) PathEntry->DirNameLen; + } + + // + // There is no version string. + // + + PathEntry->CdDirName.VersionString.Length = + PathEntry->CdCaseDirName.VersionString.Length = 0; + + // + // If the name string ends with a period then knock off the last + // character. + // + + if (PathEntry->CdDirName.FileName.Buffer[(PathEntry->CdDirName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { + + // + // Shrink the filename length. + // + + PathEntry->CdDirName.FileName.Length -= sizeof( WCHAR ); + } + + // + // Update the case name buffer if necessary. If this is an exact case + // operation then just copy the exact case string. + // + + if (IgnoreCase) { + + PathEntry->CdCaseDirName.FileName.Buffer = Add2Ptr( PathEntry->CdDirName.FileName.Buffer, + PathEntry->CdDirName.FileName.MaximumLength / 2, + PWCHAR); + + PathEntry->CdCaseDirName.FileName.MaximumLength = PathEntry->CdDirName.FileName.MaximumLength / 2; + + CdUpcaseName( IrpContext, + &PathEntry->CdDirName, + &PathEntry->CdCaseDirName ); + + } else { + + PathEntry->CdCaseDirName = PathEntry->CdDirName; + } + + return; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/pnp.c b/reactos/drivers/filesystems/cdfs_new/pnp.c new file mode 100755 index 00000000000..48f8dc4e1f7 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/pnp.c @@ -0,0 +1,793 @@ +/*++ + +Copyright (c) 1997-2000 Microsoft Corporation + +Module Name: + + Pnp.c + +Abstract: + + This module implements the Plug and Play routines for CDFS called by + the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PNP) + +NTSTATUS +CdPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +CdPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +CdPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +CdPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +CdPnpCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonPnp) +#pragma alloc_text(PAGE, CdPnpCancelRemove) +#pragma alloc_text(PAGE, CdPnpQueryRemove) +#pragma alloc_text(PAGE, CdPnpRemove) +#pragma alloc_text(PAGE, CdPnpSurpriseRemove) +#endif + + +NTSTATUS +CdCommonPnp ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing PnP operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + BOOLEAN PassThrough = FALSE; + + PIO_STACK_LOCATION IrpSp; + + PVOLUME_DEVICE_OBJECT OurDeviceObject; + PVCB Vcb; + + // + // Get the current Irp stack location. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Find our Vcb. This is tricky since we have no file object in the Irp. + // + + OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject; + + // + // IO holds a handle reference on our VDO and holds the device lock, which + // syncs us against mounts/verifies. However we hold no reference on the + // volume, which may already have been torn down (and the Vpb freed), for + // example by a force dismount. Check for this condition. We must hold this + // lock until the pnp worker functions take additional locks/refs on the Vcb. + // + + CdAcquireCdData( IrpContext); + + // + // Make sure this device object really is big enough to be a volume device + // object. If it isn't, we need to get out before we try to reference some + // field that takes us past the end of an ordinary device object. + // + + if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || + NodeType( &OurDeviceObject->Vcb ) != CDFS_NTC_VCB) { + + // + // We were called with something we don't understand. + // + + Status = STATUS_INVALID_PARAMETER; + CdReleaseCdData( IrpContext); + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Force all PnP operations to be synchronous. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + Vcb = &OurDeviceObject->Vcb; + + // + // Check that the Vcb hasn't already been deleted. If so, just pass the + // request through to the driver below, we don't need to do anything. + // + + if (NULL == Vcb->Vpb) { + + PassThrough = TRUE; + } + else { + + // + // Case on the minor code. + // + + switch ( IrpSp->MinorFunction ) { + + case IRP_MN_QUERY_REMOVE_DEVICE: + + Status = CdPnpQueryRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_SURPRISE_REMOVAL: + + Status = CdPnpSurpriseRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_REMOVE_DEVICE: + + Status = CdPnpRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + Status = CdPnpCancelRemove( IrpContext, Irp, Vcb ); + break; + + default: + + PassThrough = TRUE; + break; + } + } + + if (PassThrough) { + + CdReleaseCdData( IrpContext); + + // + // Just pass the IRP on. As we do not need to be in the + // way on return, ellide ourselves out of the stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + // + // Cleanup our Irp Context. The driver has completed the Irp. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + } + + return Status; +} + + +NTSTATUS +CdPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP query remove operation. The filesystem + is responsible for answering whether there are any reasons it sees + that the volume can not go away (and the device removed). Initiation + of the dismount begins when we answer yes to this question. + + Query will be followed by a Cancel or Remove. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being queried. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + ASSERT_EXCLUSIVE_CDDATA; + + // + // Having said yes to a QUERY, any communication with the + // underlying storage stack is undefined (and may block) + // until the bounding CANCEL or REMOVE is sent. + // + // Acquire the global resource so that we can try to vaporize the volume, + // and the vcb resource itself. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Drop a reference on the Vcb to keep it around after we drop the locks. + // + + CdLockVcb( IrpContext, Vcb); + Vcb->VcbReference += 1; + CdUnlockVcb( IrpContext, Vcb); + + CdReleaseCdData( IrpContext); + + Status = CdLockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // Reacquire the global lock, which means dropping the Vcb resource. + // + + CdReleaseVcb( IrpContext, Vcb ); + + CdAcquireCdData( IrpContext ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Remove our extra reference. + // + + CdLockVcb( IrpContext, Vcb); + Vcb->VcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb); + + if (NT_SUCCESS( Status )) { + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now if no one below us failed already, initiate the dismount + // on this volume, make it go away. PnP needs to see our internal + // streams close and drop their references to the target device. + // + // Since we were able to lock the volume, we are guaranteed to + // move this volume into dismount state and disconnect it from + // the underlying storage stack. The force on our part is actually + // unnecesary, though complete. + // + // What is not strictly guaranteed, though, is that the closes + // for the metadata streams take effect synchronously underneath + // of this call. This would leave references on the target device + // even though we are disconnected! + // + + if (NT_SUCCESS( Status )) { + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + ASSERT( !VcbPresent || Vcb->VcbCondition == VcbDismountInProgress ); + } + + // + // Note: Normally everything will complete and the internal streams will + // vaporise. However there is some code in the system which drops additional + // references on fileobjects, including our internal stream file objects, + // for (WMI) tracing purposes. If that happens to run concurrently with our + // teardown, our internal streams will not vaporise until those references + // are removed. So it's possible that the volume still remains at this + // point. The pnp query remove will fail due to our references on the device. + // To be cleaner we will return an error here. We could pend the pnp + // IRP until the volume goes away, but since we don't know when that will + // be, and this is a very rare case, we'll just fail the query. + // + // The reason this is the case is that handles/fileobjects place a reference + // on the device objects they overly. In the filesystem case, these references + // are on our target devices. PnP correcly thinks that if references remain + // on the device objects in the stack that someone has a handle, and that this + // counts as a reason to not succeed the query - even though every interrogated + // driver thinks that it is OK. + // + + if (NT_SUCCESS( Status) && VcbPresent && (Vcb->VcbReference != 0)) { + + Status = STATUS_DEVICE_BUSY; + } + } + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP if neccesary. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +CdPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP remove operation. This is our notification + that the underlying storage device for the volume we have is gone, and + an excellent indication that the volume will never reappear. The filesystem + is responsible for initiation or completion the dismount. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + ASSERT_EXCLUSIVE_CDDATA; + + // + // REMOVE - a storage device is now gone. We either got + // QUERY'd and said yes OR got a SURPRISE OR a storage + // stack failed to spin back up from a sleep/stop state + // (the only case in which this will be the first warning). + // + // Note that it is entirely unlikely that we will be around + // for a REMOVE in the first two cases, as we try to intiate + // dismount. + // + + // + // Acquire the global resource so that we can try to vaporize + // the volume, and the vcb resource itself. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // The device will be going away. Remove our lock and find + // out if we ever had one in the first place. + // + + Status = CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // If the volume had not been locked, we must invalidate the + // volume to ensure it goes away properly. The remove will + // succeed. + // + + if (!NT_SUCCESS( Status )) { + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + Status = STATUS_SUCCESS; + } + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding if we were not preceeded by a QUERY. + // + // PnP will take care of disconnecting this stack if we + // couldn't get off of it immediately. + // + + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +CdPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP surprise remove operation. This is another + type of notification that the underlying storage device for the volume we + have is gone, and is excellent indication that the volume will never reappear. + The filesystem is responsible for initiation or completion the dismount. + + For the most part, only "real" drivers care about the distinction of a + surprise remove, which is a result of our noticing that a user (usually) + physically reached into the machine and pulled something out. + + Surprise will be followed by a Remove when all references have been shut down. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + ASSERT_EXCLUSIVE_CDDATA; + + // + // SURPRISE - a device was physically yanked away without + // any warning. This means external forces. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Invalidate the volume right now. + // + // The intent here is to make every subsequent operation + // on the volume fail and grease the rails toward dismount. + // By definition there is no going back from a SURPRISE. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding since this is an out of band notification. + // + + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +CdPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP cancel remove operation. This is our + notification that a previously proposed remove (query) was eventually + vetoed by a component. The filesystem is responsible for cleaning up + and getting ready for more IO. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + ASSERT_EXCLUSIVE_CDDATA; + + // + // CANCEL - a previous QUERY has been rescinded as a result + // of someone vetoing. Since PnP cannot figure out who may + // have gotten the QUERY (think about it: stacked drivers), + // we must expect to deal with getting a CANCEL without having + // seen the QUERY. + // + // For CDFS, this is quite easy. In fact, we can't get a + // CANCEL if the underlying drivers succeeded the QUERY since + // we disconnect the Vpb on our dismount initiation. This is + // actually pretty important because if PnP could get to us + // after the disconnect we'd be thoroughly unsynchronized + // with respect to the Vcb getting torn apart - merely referencing + // the volume device object is insufficient to keep us intact. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + CdReleaseCdData( IrpContext); + + // + // Unlock the volume. This is benign if we never had seen + // a QUERY. + // + + (VOID) CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Send the request. The underlying driver will complete the + // IRP. Since we don't need to be in the way, simply ellide + // ourselves out of the IRP stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdPnpCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) +{ + PKEVENT Event = (PKEVENT) Contxt; + + KeSetEvent( Event, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/prefxsup.c b/reactos/drivers/filesystems/cdfs_new/prefxsup.c new file mode 100755 index 00000000000..c00b98e50e9 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/prefxsup.c @@ -0,0 +1,711 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PrefxSup.c + +Abstract: + + This module implements the Cdfs Prefix support routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PREFXSUP) + +// +// Local support routines. +// + +PNAME_LINK +CdFindNameLink ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PUNICODE_STRING Name + ); + +BOOLEAN +CdInsertNameLink ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PNAME_LINK NameLink + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFindNameLink) +#pragma alloc_text(PAGE, CdFindPrefix) +#pragma alloc_text(PAGE, CdInsertNameLink) +#pragma alloc_text(PAGE, CdInsertPrefix) +#pragma alloc_text(PAGE, CdRemovePrefix) +#endif + + +VOID +CdInsertPrefix ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN BOOLEAN ShortNameMatch, + IN PFCB ParentFcb + ) + +/*++ + +Routine Description: + + This routine inserts the names in the given Lcb into the links for the + parent. + +Arguments: + + Fcb - This is the Fcb whose name is being inserted into the tree. + + Name - This is the name for the component. The IgnoreCase flag tells + us which entry this belongs to. + + IgnoreCase - Indicates if we should insert the case-insensitive name. + + ShortNameMatch - Indicates if this is the short name. + + ParentFcb - This is the ParentFcb. The prefix tree is attached to this. + +Return Value: + + None. + +--*/ + +{ + ULONG PrefixFlags; + PNAME_LINK NameLink; + PPREFIX_ENTRY PrefixEntry; + PRTL_SPLAY_LINKS *TreeRoot; + + PWCHAR NameBuffer; + + PAGED_CODE(); + + // + // Check if we need to allocate a prefix entry for the short name. + // If we can't allocate one then fail quietly. We don't have to + // insert the name. + // + + PrefixEntry = &Fcb->FileNamePrefix; + + if (ShortNameMatch) { + + if (Fcb->ShortNamePrefix == NULL) { + + Fcb->ShortNamePrefix = ExAllocatePoolWithTag( CdPagedPool, + sizeof( PREFIX_ENTRY ), + TAG_PREFIX_ENTRY ); + + if (Fcb->ShortNamePrefix == NULL) { return; } + + RtlZeroMemory( Fcb->ShortNamePrefix, sizeof( PREFIX_ENTRY )); + } + + PrefixEntry = Fcb->ShortNamePrefix; + } + + // + // Capture the local variables for the separate cases. + // + + if (IgnoreCase) { + + PrefixFlags = PREFIX_FLAG_IGNORE_CASE_IN_TREE; + NameLink = &PrefixEntry->IgnoreCaseName; + TreeRoot = &ParentFcb->IgnoreCaseRoot; + + } else { + + PrefixFlags = PREFIX_FLAG_EXACT_CASE_IN_TREE; + NameLink = &PrefixEntry->ExactCaseName; + TreeRoot = &ParentFcb->ExactCaseRoot; + } + + // + // If neither name is in the tree then check whether we have a buffer for this + // name + // + + if (!FlagOn( PrefixEntry->PrefixFlags, + PREFIX_FLAG_EXACT_CASE_IN_TREE | PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + // + // Allocate a new buffer if the embedded buffer is too small. + // + + NameBuffer = PrefixEntry->FileNameBuffer; + + if (Name->FileName.Length > BYTE_COUNT_EMBEDDED_NAME) { + + NameBuffer = ExAllocatePoolWithTag( CdPagedPool, + Name->FileName.Length * 2, + TAG_PREFIX_NAME ); + + // + // Exit if no name buffer. + // + + if (NameBuffer == NULL) { return; } + } + + // + // Split the buffer and fill in the separate components. + // + + PrefixEntry->ExactCaseName.FileName.Buffer = NameBuffer; + PrefixEntry->IgnoreCaseName.FileName.Buffer = Add2Ptr( NameBuffer, + Name->FileName.Length, + PWCHAR ); + + PrefixEntry->IgnoreCaseName.FileName.MaximumLength = + PrefixEntry->IgnoreCaseName.FileName.Length = + PrefixEntry->ExactCaseName.FileName.MaximumLength = + PrefixEntry->ExactCaseName.FileName.Length = Name->FileName.Length; + } + + // + // Only insert the name if not already present. + // + + if (!FlagOn( PrefixEntry->PrefixFlags, PrefixFlags )) { + + // + // Initialize the name in the prefix entry. + // + + RtlCopyMemory( NameLink->FileName.Buffer, + Name->FileName.Buffer, + Name->FileName.Length ); + + CdInsertNameLink( IrpContext, + TreeRoot, + NameLink ); + + PrefixEntry->Fcb = Fcb; + SetFlag( PrefixEntry->PrefixFlags, PrefixFlags ); + } + + return; +} + + +VOID +CdRemovePrefix ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to remove all of the previx entries of a + given Fcb from its parent Fcb. + +Arguments: + + Fcb - Fcb whose entries are to be removed. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Start with the short name prefix entry. + // + + if (Fcb->ShortNamePrefix != NULL) { + + if (FlagOn( Fcb->ShortNamePrefix->PrefixFlags, PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + Fcb->ParentFcb->IgnoreCaseRoot = RtlDelete( &Fcb->ShortNamePrefix->IgnoreCaseName.Links ); + } + + if (FlagOn( Fcb->ShortNamePrefix->PrefixFlags, PREFIX_FLAG_EXACT_CASE_IN_TREE )) { + + Fcb->ParentFcb->ExactCaseRoot = RtlDelete( &Fcb->ShortNamePrefix->ExactCaseName.Links ); + } + + ClearFlag( Fcb->ShortNamePrefix->PrefixFlags, + PREFIX_FLAG_IGNORE_CASE_IN_TREE | PREFIX_FLAG_EXACT_CASE_IN_TREE ); + } + + // + // Now do the long name prefix entries. + // + + if (FlagOn( Fcb->FileNamePrefix.PrefixFlags, PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + Fcb->ParentFcb->IgnoreCaseRoot = RtlDelete( &Fcb->FileNamePrefix.IgnoreCaseName.Links ); + } + + if (FlagOn( Fcb->FileNamePrefix.PrefixFlags, PREFIX_FLAG_EXACT_CASE_IN_TREE )) { + + Fcb->ParentFcb->ExactCaseRoot = RtlDelete( &Fcb->FileNamePrefix.ExactCaseName.Links ); + } + + ClearFlag( Fcb->FileNamePrefix.PrefixFlags, + PREFIX_FLAG_IGNORE_CASE_IN_TREE | PREFIX_FLAG_EXACT_CASE_IN_TREE ); + + // + // Deallocate any buffer we may have allocated. + // + + if ((Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != (PWCHAR) &Fcb->FileNamePrefix.FileNameBuffer) && + (Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != NULL)) { + + CdFreePool( &Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer ); + Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer = NULL; + } + + return; +} + + +VOID +CdFindPrefix ( + IN PIRP_CONTEXT IrpContext, + IN OUT PFCB *CurrentFcb, + IN OUT PUNICODE_STRING RemainingName, + IN BOOLEAN IgnoreCase + ) + +/*++ + +Routine Description: + + This routine begins from the given CurrentFcb and walks through all of + components of the name looking for the longest match in the prefix + splay trees. The search is relative to the starting Fcb so the + full name may not begin with a '\'. On return this routine will + update Current Fcb with the lowest point it has travelled in the + tree. It will also hold only that resource on return and it must + hold that resource. + +Arguments: + + CurrentFcb - Address to store the lowest Fcb we find on this search. + On return we will have acquired this Fcb. On entry this is the + Fcb to examine. + + RemainingName - Supplies a buffer to store the exact case of the name being + searched for. Initially will contain the upcase name based on the + IgnoreCase flag. + + IgnoreCase - Indicates if we are doing a case-insensitive compare. + +Return Value: + + None + +--*/ + +{ + UNICODE_STRING LocalRemainingName; + + UNICODE_STRING FinalName; + + PNAME_LINK NameLink; + PPREFIX_ENTRY PrefixEntry; + + PAGED_CODE(); + + // + // Make a local copy of the input strings. + // + + LocalRemainingName = *RemainingName; + + // + // Loop until we find the longest matching prefix. + // + + while (TRUE) { + + // + // If there are no characters left or we are not at an IndexFcb then + // return immediately. + // + + if ((LocalRemainingName.Length == 0) || + (SafeNodeType( *CurrentFcb ) != CDFS_NTC_FCB_INDEX)) { + + return; + } + + // + // Split off the next component from the name. + // + + CdDissectName( IrpContext, + &LocalRemainingName, + &FinalName ); + + // + // Check if this name is in the splay tree for this Scb. + // + + if (IgnoreCase) { + + NameLink = CdFindNameLink( IrpContext, + &(*CurrentFcb)->IgnoreCaseRoot, + &FinalName ); + + // + // Get the prefix entry from this NameLink. Don't access any + // fields within it until we verify we have a name link. + // + + PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink, + PREFIX_ENTRY, + IgnoreCaseName ); + + } else { + + NameLink = CdFindNameLink( IrpContext, + &(*CurrentFcb)->ExactCaseRoot, + &FinalName ); + + PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink, + PREFIX_ENTRY, + ExactCaseName ); + } + + // + // If we didn't find a match then exit. + // + + if (NameLink == NULL) { return; } + + // + // If this is a case-insensitive match then copy the exact case of the name into + // the input buffer. + // + + if (IgnoreCase) { + + RtlCopyMemory( FinalName.Buffer, + PrefixEntry->ExactCaseName.FileName.Buffer, + PrefixEntry->ExactCaseName.FileName.Length ); + } + + // + // Update the caller's remaining name string to reflect the fact that we found + // a match. + // + + *RemainingName = LocalRemainingName; + + // + // Move down to the next component in the tree. Acquire without waiting. + // If this fails then lock the Fcb to reference this Fcb and then drop + // the parent and acquire the child. + // + + if (!CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, TRUE )) { + + // + // If we can't wait then raise CANT_WAIT. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + CdLockVcb( IrpContext, IrpContext->Vcb ); + PrefixEntry->Fcb->FcbReference += 1; + CdUnlockVcb( IrpContext, IrpContext->Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, FALSE ); + + CdLockVcb( IrpContext, IrpContext->Vcb ); + PrefixEntry->Fcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, IrpContext->Vcb ); + + } else { + + CdReleaseFcb( IrpContext, *CurrentFcb ); + } + + *CurrentFcb = PrefixEntry->Fcb; + } +} + + +// +// Local support routine +// + +PNAME_LINK +CdFindNameLink ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This routine searches through a splay link tree looking for a match for the + input name. If we find the corresponding name we will rebalance the + tree. + +Arguments: + + RootNode - Supplies the parent to search. + + Name - This is the name to search for. Note if we are doing a case + insensitive search the name would have been upcased already. + +Return Value: + + PNAME_LINK - The name link found or NULL if there is no match. + +--*/ + +{ + FSRTL_COMPARISON_RESULT Comparison; + PNAME_LINK Node; + PRTL_SPLAY_LINKS Links; + + PAGED_CODE(); + + Links = *RootNode; + + while (Links != NULL) { + + Node = CONTAINING_RECORD( Links, NAME_LINK, Links ); + + // + // Compare the prefix in the tree with the full name + // + + Comparison = CdFullCompareNames( IrpContext, &Node->FileName, Name ); + + // + // See if they don't match + // + + if (Comparison == GreaterThan) { + + // + // The prefix is greater than the full name + // so we go down the left child + // + + Links = RtlLeftChild( Links ); + + // + // And continue searching down this tree + // + + } else if (Comparison == LessThan) { + + // + // The prefix is less than the full name + // so we go down the right child + // + + Links = RtlRightChild( Links ); + + // + // And continue searching down this tree + // + + } else { + + // + // We found it. + // + // Splay the tree and save the new root. + // + + *RootNode = RtlSplay( Links ); + + return Node; + } + } + + // + // We didn't find the Link. + // + + return NULL; +} + + +// +// Local support routine +// + +BOOLEAN +CdInsertNameLink ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PNAME_LINK NameLink + ) + +/*++ + +Routine Description: + + This routine will insert a name in the splay tree pointed to + by RootNode. + + The name could already exist in this tree for a case-insensitive tree. + In that case we simply return FALSE and do nothing. + +Arguments: + + RootNode - Supplies a pointer to the table. + + NameLink - Contains the new link to enter. + +Return Value: + + BOOLEAN - TRUE if the name is inserted, FALSE otherwise. + +--*/ + +{ + FSRTL_COMPARISON_RESULT Comparison; + PNAME_LINK Node; + + PAGED_CODE(); + + RtlInitializeSplayLinks( &NameLink->Links ); + + // + // If we are the first entry in the tree, just become the root. + // + + if (*RootNode == NULL) { + + *RootNode = &NameLink->Links; + + return TRUE; + } + + Node = CONTAINING_RECORD( *RootNode, NAME_LINK, Links ); + + while (TRUE) { + + // + // Compare the prefix in the tree with the prefix we want + // to insert. + // + + Comparison = CdFullCompareNames( IrpContext, &Node->FileName, &NameLink->FileName ); + + // + // If we found the entry, return immediately. + // + + if (Comparison == EqualTo) { return FALSE; } + + // + // If the tree prefix is greater than the new prefix then + // we go down the left subtree + // + + if (Comparison == GreaterThan) { + + // + // We want to go down the left subtree, first check to see + // if we have a left subtree + // + + if (RtlLeftChild( &Node->Links ) == NULL) { + + // + // there isn't a left child so we insert ourselves as the + // new left child + // + + RtlInsertAsLeftChild( &Node->Links, &NameLink->Links ); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a left child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlLeftChild( &Node->Links ), + NAME_LINK, + Links ); + } + + } else { + + // + // The tree prefix is either less than or a proper prefix + // of the new string. We treat both cases as less than when + // we do insert. So we want to go down the right subtree, + // first check to see if we have a right subtree + // + + if (RtlRightChild( &Node->Links ) == NULL) { + + // + // These isn't a right child so we insert ourselves as the + // new right child + // + + RtlInsertAsRightChild( &Node->Links, &NameLink->Links ); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a right child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlRightChild( &Node->Links ), + NAME_LINK, + Links ); + } + } + } + + return TRUE; +} + + + + + diff --git a/reactos/drivers/filesystems/cdfs_new/read.c b/reactos/drivers/filesystems/cdfs_new/read.c new file mode 100755 index 00000000000..aeb44e2d56b --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/read.c @@ -0,0 +1,550 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Read.c + +Abstract: + + This module implements the File Read routine for Read called by the + Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_READ) + +// +// VOID +// SafeZeroMemory ( +// IN PUCHAR At, +// IN ULONG ByteCount +// ); +// + +// +// This macro just puts a nice little try-except around RtlZeroMemory +// + +#define SafeZeroMemory(IC,AT,BYTE_COUNT) { \ + try { \ + RtlZeroMemory( (AT), (BYTE_COUNT) ); \ + } except( EXCEPTION_EXECUTE_HANDLER ) { \ + CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER ); \ + } \ +} + +// +// Read ahead amount used for normal data files +// + +#define READ_AHEAD_GRANULARITY (0x10000) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonRead) +#endif + + +NTSTATUS +CdCommonRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common entry point for NtReadFile calls. For synchronous requests, + CommonRead will complete the request in the current thread. If not + synchronous the request will be passed to the Fsp if there is a need to + block. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The result of this operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN Wait; + ULONG PagingIo; + ULONG SynchronousIo; + ULONG NonCachedIo; + PVOID UserBuffer; + + LONGLONG StartingOffset; + LONGLONG ByteRange; + ULONG ByteCount; + ULONG ReadByteCount; + ULONG OriginalByteCount; + + PVOID SystemBuffer; + + BOOLEAN ReleaseFile = TRUE; + + CD_IO_CONTEXT LocalIoContext; + + PAGED_CODE(); + + // + // If this is a zero length read then return SUCCESS immediately. + // + + if (IrpSp->Parameters.Read.Length == 0) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Decode the file object and verify we support read on this. It + // must be a user file, stream file or volume file (for a data disk). + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if ((TypeOfOpen == UnopenedFileObject) || + (TypeOfOpen == UserDirectoryOpen)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Examine our input parameters to determine if this is noncached and/or + // a paging io operation. + // + + Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + PagingIo = FlagOn( Irp->Flags, IRP_PAGING_IO ); + NonCachedIo = FlagOn( Irp->Flags, IRP_NOCACHE ); + SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO ); + + + // + // Extract the range of the Io. + // + + StartingOffset = IrpSp->Parameters.Read.ByteOffset.QuadPart; + OriginalByteCount = ByteCount = IrpSp->Parameters.Read.Length; + + ByteRange = StartingOffset + ByteCount; + + // + // Make sure that Dasd access is always non-cached. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + // + // Acquire the file shared to perform the read. If we are doing paging IO, + // it may be the case that we would have a deadlock imminent because we may + // block on shared access, so starve out any exclusive waiters. This requires + // a degree of caution - we believe that any paging IO bursts will recede and + // allow the exclusive waiter in. + // + + if (PagingIo) { + + CdAcquireFileSharedStarveExclusive( IrpContext, Fcb ); + + } else { + + CdAcquireFileShared( IrpContext, Fcb ); + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Fcb. Allow reads if this is a DASD handle that is + // dismounting the volume. + // + + if ((TypeOfOpen != UserVolumeOpen) || (NULL == Ccb) || + !FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE)) { + + CdVerifyFcbOperation( IrpContext, Fcb ); + } + + // + // If this is a non-cached then check whether we need to post this + // request if this thread can't block. + // + + if (!Wait && NonCachedIo) { + + // + // XA requests must always be waitable. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + try_return( Status = STATUS_CANT_WAIT ); + } + } + + // + // If this is a user request then verify the oplock and filelock state. + // + + if (TypeOfOpen == UserFileOpen) { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + Status = FsRtlCheckOplock( &Fcb->Oplock, + Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + // + // If the result is not STATUS_SUCCESS then the Irp was completed + // elsewhere. + // + + if (Status != STATUS_SUCCESS) { + + Irp = NULL; + IrpContext = NULL; + + try_return( NOTHING ); + } + + if (!PagingIo && + (Fcb->FileLock != NULL) && + !FsRtlCheckLockForReadAccess( Fcb->FileLock, Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Complete the request if it begins beyond the end of file. + // + + if (StartingOffset >= Fcb->FileSize.QuadPart) { + + try_return( Status = STATUS_END_OF_FILE ); + } + + // + // Truncate the read if it extends beyond the end of the file. + // + + if (ByteRange > Fcb->FileSize.QuadPart) { + + ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset); + ByteRange = Fcb->FileSize.QuadPart; + } + + // + // Handle the non-cached read first. + // + + if (NonCachedIo) { + + // + // If we have an unaligned transfer then post this request if + // we can't wait. Unaligned means that the starting offset + // is not on a sector boundary or the read is not integral + // sectors. + // + + ReadByteCount = BlockAlign( Fcb->Vcb, ByteCount ); + + if (SectorOffset( StartingOffset ) || + SectorOffset( ReadByteCount ) || + (ReadByteCount > OriginalByteCount)) { + + if (!Wait) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make sure we don't overwrite the buffer. + // + + ReadByteCount = ByteCount; + } + + // + // Initialize the IoContext for the read. + // If there is a context pointer, we need to make sure it was + // allocated and not a stale stack pointer. + // + + if (IrpContext->IoContext == NULL || + !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) { + + // + // If we can wait, use the context on the stack. Otherwise + // we need to allocate one. + // + + if (Wait) { + + IrpContext->IoContext = &LocalIoContext; + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + } else { + + IrpContext->IoContext = CdAllocateIoContext(); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + } + } + + RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT )); + + // + // Store whether we allocated this context structure in the structure + // itself. + // + + IrpContext->IoContext->AllocatedContext = + BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->IoContext->SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread(); + IrpContext->IoContext->Resource = Fcb->Resource; + IrpContext->IoContext->RequestedByteCount = ByteCount; + } + + Irp->IoStatus.Information = ReadByteCount; + + // + // Call one of the NonCacheIo routines to perform the actual + // read. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) { + + Status = CdNonCachedXARead( IrpContext, Fcb, StartingOffset, ReadByteCount ); + + } else { + + Status = CdNonCachedRead( IrpContext, Fcb, StartingOffset, ReadByteCount ); + } + + // + // Don't complete this request now if STATUS_PENDING was returned. + // + + if (Status == STATUS_PENDING) { + + Irp = NULL; + ReleaseFile = FALSE; + + // + // Test is we should zero part of the buffer or update the + // synchronous file position. + // + + } else { + + // + // Convert any unknown error code to IO_ERROR. + // + + if (!NT_SUCCESS( Status )) { + + // + // Set the information field to zero. + // + + Irp->IoStatus.Information = 0; + + // + // Raise if this is a user induced error. + // + + if (IoIsErrorUserInduced( Status )) { + + CdRaiseStatus( IrpContext, Status ); + } + + Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR ); + + // + // Check if there is any portion of the user's buffer to zero. + // + + } else if (ReadByteCount != ByteCount) { + + CdMapUserBuffer( IrpContext, &UserBuffer); + + SafeZeroMemory( IrpContext, + Add2Ptr( UserBuffer, + ByteCount, + PVOID ), + ReadByteCount - ByteCount ); + + Irp->IoStatus.Information = ByteCount; + } + + // + // Update the file position if this is a synchronous request. + // + + if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) { + + IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; + } + } + + try_return( NOTHING ); + } + + // + // Handle the cached case. Start by initializing the private + // cache map. + // + + if (IrpSp->FileObject->PrivateCacheMap == NULL) { + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( IrpSp->FileObject, + (PCC_FILE_SIZES) &Fcb->AllocationSize, + FALSE, + &CdData.CacheManagerCallbacks, + Fcb ); + + CcSetReadAheadGranularity( IrpSp->FileObject, READ_AHEAD_GRANULARITY ); + } + + // + // Read from the cache if this is not an Mdl read. + // + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + // + // If we are in the Fsp now because we had to wait earlier, + // we must map the user buffer, otherwise we can use the + // user's buffer directly. + // + + CdMapUserBuffer( IrpContext, &SystemBuffer ); + + // + // Now try to do the copy. + // + + if (!CcCopyRead( IrpSp->FileObject, + (PLARGE_INTEGER) &StartingOffset, + ByteCount, + Wait, + SystemBuffer, + &Irp->IoStatus )) { + + try_return( Status = STATUS_CANT_WAIT ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + CdNormalizeAndRaiseStatus( IrpContext, Irp->IoStatus.Status ); + } + + // + // Otherwise perform the MdlRead operation. + // + + } else { + + CcMdlRead( IrpSp->FileObject, + (PLARGE_INTEGER) &StartingOffset, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + } + + // + // Update the current file position in the user file object. + // + + if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) { + + IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; + } + + try_exit: NOTHING; + } finally { + + // + // Release the Fcb. + // + + if (ReleaseFile) { + + CdReleaseFile( IrpContext, Fcb ); + } + } + + // + // Post the request if we got CANT_WAIT. + // + + if (Status == STATUS_CANT_WAIT) { + + Status = CdFsdPostRequest( IrpContext, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + CdCompleteRequest( IrpContext, Irp, Status ); + } + + return Status; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/resrcsup.c b/reactos/drivers/filesystems/cdfs_new/resrcsup.c new file mode 100755 index 00000000000..03d47cade1c --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/resrcsup.c @@ -0,0 +1,332 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + ResrcSup.c + +Abstract: + + This module implements the Cdfs Resource acquisition routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_RESRCSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdAcquireForCache) +#pragma alloc_text(PAGE, CdAcquireForCreateSection) +#pragma alloc_text(PAGE, CdAcquireResource) +#pragma alloc_text(PAGE, CdNoopAcquire) +#pragma alloc_text(PAGE, CdNoopRelease) +#pragma alloc_text(PAGE, CdReleaseForCreateSection) +#pragma alloc_text(PAGE, CdReleaseFromCache) +#endif + + +BOOLEAN +CdAcquireResource ( + IN PIRP_CONTEXT IrpContext, + IN PERESOURCE Resource, + IN BOOLEAN IgnoreWait, + IN TYPE_OF_ACQUIRE Type + ) + +/*++ + +Routine Description: + + This is the single routine used to acquire file system resources. It + looks at the IgnoreWait flag to determine whether to try to acquire the + resource without waiting. Returning TRUE/FALSE to indicate success or + failure. Otherwise it is driven by the WAIT flag in the IrpContext and + will raise CANT_WAIT on a failure. + +Arguments: + + Resource - This is the resource to try and acquire. + + IgnoreWait - If TRUE then this routine will not wait to acquire the + resource and will return a boolean indicating whether the resource was + acquired. Otherwise we use the flag in the IrpContext and raise + if the resource is not acquired. + + Type - Indicates how we should try to get the resource. + +Return Value: + + BOOLEAN - TRUE if the resource is acquired. FALSE if not acquired and + IgnoreWait is specified. Otherwise we raise CANT_WAIT. + +--*/ + +{ + BOOLEAN Wait = FALSE; + BOOLEAN Acquired; + PAGED_CODE(); + + // + // We look first at the IgnoreWait flag, next at the flag in the Irp + // Context to decide how to acquire this resource. + // + + if (!IgnoreWait && FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + Wait = TRUE; + } + + // + // Attempt to acquire the resource either shared or exclusively. + // + + switch (Type) { + case AcquireExclusive: + + Acquired = ExAcquireResourceExclusiveLite( Resource, Wait ); + break; + + case AcquireShared: + + Acquired = ExAcquireResourceSharedLite( Resource, Wait ); + break; + + case AcquireSharedStarveExclusive: + + Acquired = ExAcquireSharedStarveExclusive( Resource, Wait ); + break; + + default: + Acquired = FALSE; + ASSERT( FALSE ); + } + + // + // If not acquired and the user didn't specifiy IgnoreWait then + // raise CANT_WAIT. + // + + if (!Acquired && !IgnoreWait) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + return Acquired; +} + + +BOOLEAN +CdAcquireForCache ( + IN PFCB Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer for synchronization. + +Arguments: + + Fcb - The pointer supplied as context to the cache initialization + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + ASSERT(IoGetTopLevelIrp() == NULL); + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return ExAcquireResourceSharedLite( Fcb->Resource, Wait ); +} + + +VOID +CdReleaseFromCache ( + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a virtual file. It is subsequently called by the Lazy Writer to release + a resource acquired above. + +Arguments: + + Fcb - The pointer supplied as context to the cache initialization + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + IoSetTopLevelIrp( NULL ); + + ExReleaseResourceLite( Fcb->Resource ); +} + + +BOOLEAN +CdNoopAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Vcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + TRUE + +--*/ + +{ + PAGED_CODE(); + return TRUE; +} + + +VOID +CdNoopRelease ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Vcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); +} + + +VOID +CdAcquireForCreateSection ( + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This is the callback routine for MM to use to acquire the file exclusively. + +Arguments: + + FileObject - File object for a Cdfs stream. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + + // + // Get the Fcb resource exclusively. + // + + ExAcquireResourceExclusiveLite( &((PFCB) FileObject->FsContext)->FcbNonpaged->FcbResource, + TRUE ); + + // + // Take the File resource shared. We need this later on when MM calls + // QueryStandardInfo to get the file size. + // + // If we don't use StarveExclusive, then we can get wedged behind an + // exclusive waiter who is waiting on someone else holding it shared in the + // read->initializecachemap path (which calls createsection) who is in turn + // waiting on us to finish the create section. + // + + ExAcquireSharedStarveExclusive( ((PFCB) FileObject->FsContext)->Resource, + TRUE ); +} + + +VOID +CdReleaseForCreateSection ( + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This is the callback routine for MM to use to release a file acquired with + the AcquireForCreateSection call above. + +Arguments: + + FileObject - File object for a Cdfs stream. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Release the resources. + // + + ExReleaseResourceLite( &((PFCB) FileObject->FsContext)->FcbNonpaged->FcbResource ); + ExReleaseResourceLite( ((PFCB) FileObject->FsContext)->Resource); +} + diff --git a/reactos/drivers/filesystems/cdfs_new/strucsup.c b/reactos/drivers/filesystems/cdfs_new/strucsup.c new file mode 100755 index 00000000000..0d157e13b38 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/strucsup.c @@ -0,0 +1,2766 @@ +/*++ + +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, +// 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) \ + 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 + ); + +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 ); + + // + // 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 ); + + // + // 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) { + + CdFreePool( &Vcb->SwapVpb ); + } + + // + // If there is a Vpb then we must delete it ourselves. + // + + if (Vcb->Vpb != NULL) { + + CdFreePool( &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) { + + CdFreePool( &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) { + + CdFreePool( &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) { + + CdFreePool( &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) { + + CdFreePool( &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. + // + + 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; +} + + +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)) { + + 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: + + 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(); + + CdFreePool( &Buffer ); + + 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; +} + + diff --git a/reactos/drivers/filesystems/cdfs_new/verfysup.c b/reactos/drivers/filesystems/cdfs_new/verfysup.c new file mode 100755 index 00000000000..2a23d364c29 --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/verfysup.c @@ -0,0 +1,954 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VerfySup.c + +Abstract: + + This module implements the Cdfs Verification routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_VERFYSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdVerifyFcbOperation) +#pragma alloc_text(PAGE, CdVerifyVcb) +#endif + + +NTSTATUS +CdPerformVerify ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT DeviceToVerify + ) + +/*++ + +Routine Description: + + This routines performs an IoVerifyVolume operation and takes the + appropriate action. If the verify is successful then we send the originating + Irp off to an Ex Worker Thread. This routine is called from the exception handler. + + No file system resources are held when this routine is called. + +Arguments: + + Irp - The irp to send off after all is well and done. + + Device - The real device needing verification. + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Check if this Irp has a status of Verify required and if it does + // then call the I/O system to do a verify. + // + // Skip the IoVerifyVolume if this is a mount or verify request + // itself. Trying a recursive mount will cause a deadlock with + // the DeviceObject->DeviceLock. + // + + if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || + (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { + + return CdFsdPostRequest( IrpContext, Irp ); + } + + // + // Extract a pointer to the Vcb from the VolumeDeviceObject. + // Note that since we have specifically excluded mount, + // requests, we know that IrpSp->DeviceObject is indeed a + // volume device object. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject )->Vcb; + try { + + // + // Send down the verify FSCTL. Note that this is sent to the + // currently mounted volume, which may not be this one. + // + // We will allow Raw to mount this volume if we were doing a + // an absolute DASD open. + // + + Status = IoVerifyVolume( DeviceToVerify, CdOperationIsDasdOpen( IrpContext)); + + // + // Acquire the Vcb so we're working with a stable VcbCondition. + // + + CdAcquireVcbShared( IrpContext, Vcb, FALSE); + + // + // If the verify operation completed it will return + // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. + // + // If CdVerifyVolume encountered an error during + // processing, it will return that error. If we got + // STATUS_WRONG_VOLUME from the verify, and our volume + // is now mounted, commute the status to STATUS_SUCCESS. + // + + if ((Status == STATUS_WRONG_VOLUME) && + (Vcb->VcbCondition == VcbMounted)) { + + Status = STATUS_SUCCESS; + } + else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbMounted)) { + + // + // If the verify succeeded, but our volume is not mounted, + // then some other volume is on the device. + // + + Status = STATUS_WRONG_VOLUME; + } + + // + // Do a quick unprotected check here. The routine will do + // a safe check. After here we can release the resource. + // Note that if the volume really went away, we will be taking + // the Reparse path. + // + + // + // If the device might need to go away then call our dismount routine. + // + + if (((Vcb->VcbCondition == VcbNotMounted) || + (Vcb->VcbCondition == VcbInvalid) || + (Vcb->VcbCondition == VcbDismountInProgress)) && + (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE)) { + + CdReleaseVcb( IrpContext, Vcb); + + CdAcquireCdData( IrpContext ); + CdCheckForDismount( IrpContext, Vcb, FALSE ); + CdReleaseCdData( IrpContext ); + } + else { + + CdReleaseVcb( IrpContext, Vcb); + } + + // + // If this is a create and the verify succeeded then complete the + // request with a REPARSE status. + // + + if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->RelatedFileObject == NULL) && + ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) { + + Irp->IoStatus.Information = IO_REMOUNT; + + CdCompleteRequest( IrpContext, Irp, STATUS_REPARSE ); + Status = STATUS_REPARSE; + Irp = NULL; + IrpContext = NULL; + + // + // If there is still an error to process then call the Io system + // for a popup. + // + + } else if ((Irp != NULL) && !NT_SUCCESS( Status )) { + + // + // Fill in the device object if required. + // + + if (IoIsErrorUserInduced( Status ) ) { + + IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); + } + + CdNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // If there is still an Irp, send it off to an Ex Worker thread. + // + + if (IrpContext != NULL) { + + Status = CdFsdPostRequest( IrpContext, Irp ); + } + + } except(CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the verify or raised + // an error ourselves. So we'll abort the I/O request with + // the error status that we get back from the execption code. + // + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + return Status; +} + + +BOOLEAN +CdCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN Force + ) + +/*++ + +Routine Description: + + This routine is called to check if a volume is ready for dismount. This + occurs when only file system references are left on the volume. + + If the dismount is not currently underway and the user reference count + has gone to zero then we can begin the dismount. + + If the dismount is in progress and there are no references left on the + volume (we check the Vpb for outstanding references as well to catch + any create calls dispatched to the file system) then we can delete + the Vcb. + +Arguments: + + Vcb - Vcb for the volume to try to dismount. + + Force - Whether we will force this volume to be dismounted. + +Return Value: + + BOOLEAN - True if the Vcb was not gone by the time this function finished, + False if it was deleted. + + This is only a trustworthy indication to the caller if it had the vcb + exclusive itself. + +--*/ + +{ + BOOLEAN UnlockVcb = TRUE; + BOOLEAN VcbPresent = TRUE; + KIRQL SavedIrql; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_VCB( Vcb ); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // Acquire and lock this Vcb to check the dismount state. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Lets get rid of any pending closes for this volume. + // + + CdFspClose( Vcb ); + + CdLockVcb( IrpContext, Vcb ); + + // + // If the dismount is not already underway then check if the + // user reference count has gone to zero or we are being forced + // to disconnect. If so start the teardown on the Vcb. + // + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + if (Vcb->VcbUserReference <= CDFS_RESIDUAL_USER_REFERENCE || Force) { + + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + VcbPresent = CdDismountVcb( IrpContext, Vcb ); + } + + // + // If the teardown is underway and there are absolutely no references + // remaining then delete the Vcb. References here include the + // references in the Vcb and Vpb. + // + + } else if (Vcb->VcbReference == 0) { + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // If there are no file objects and no reference counts in the + // Vpb we can delete the Vcb. Don't forget that we have the + // last reference in the Vpb. + // + + if (Vcb->Vpb->ReferenceCount == 1) { + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + } + } + + // + // Unlock the Vcb if still held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release any resources still acquired. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + return VcbPresent; +} + + +BOOLEAN +CdMarkDevForVerifyIfVcbMounted( + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine checks to see if the specified Vcb is currently mounted on + the device or not. If it is, it sets the verify flag on the device, if + not then the state is noted in the Vcb. + +Arguments: + + Vcb - This is the volume to check. + +Return Value: + + TRUE if the device has been marked for verify here, FALSE otherwise. + +--*/ + +{ + BOOLEAN Marked = FALSE; + KIRQL SavedIrql; + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) { + + CdMarkRealDevForVerify( Vcb->Vpb->RealDevice); + Marked = TRUE; + } + else { + + // + // Flag this to avoid the VPB spinlock in future passes. + // + + SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Marked; +} + + +VOID +CdVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine checks that the current Vcb is valid and currently mounted + on the device. It will raise on an error condition. + + We check whether the volume needs verification and the current state + of the Vcb. + +Arguments: + + Vcb - This is the volume to verify. + +Return Value: + + None + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + IO_STATUS_BLOCK Iosb; + ULONG MediaChangeCount = 0; + BOOLEAN ForceVerify = FALSE; + BOOLEAN DevMarkedForVerify; + KIRQL SavedIrql; + + PAGED_CODE(); + + // + // Fail immediately if the volume is in the progress of being dismounted + // or has been marked invalid. + // + + if ((Vcb->VcbCondition == VcbInvalid) || + ((Vcb->VcbCondition == VcbDismountInProgress) && + (IrpContext->MajorFunction != IRP_MJ_CREATE))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA )) { + + // + // Capture the real device verify state. + // + + DevMarkedForVerify = CdRealDevNeedsVerify( Vcb->Vpb->RealDevice); + + // + // If the media is removable and the verify volume flag in the + // device object is not set then we want to ping the device + // to see if it needs to be verified. + // + + if (Vcb->VcbCondition != VcbMountInProgress) { + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + FALSE, + &Iosb ); + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // There are four cases when we want to do a verify. These are the + // first three. + // + // 1. We are mounted, and the device has become empty + // 2. The device has returned verify required (=> DO_VERIFY_VOL flag is + // set, but could be due to hardware condition) + // 3. Media change count doesn't match the one in the Vcb + // + + if (((Vcb->VcbCondition == VcbMounted) && + CdIsRawDevice( IrpContext, Status )) + || + (Status == STATUS_VERIFY_REQUIRED) + || + (NT_SUCCESS(Status) && + (Vcb->MediaChangeCount != MediaChangeCount))) { + + // + // If we are currently the volume on the device then it is our + // responsibility to set the verify flag. If we're not on the device, + // then we shouldn't touch the flag. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) && + !DevMarkedForVerify) { + + DevMarkedForVerify = CdMarkDevForVerifyIfVcbMounted( Vcb); + } + + ForceVerify = TRUE; + + // + // NOTE that we no longer update the media change count here. We + // do so only when we've actually completed a verify at a particular + // change count value. + // + } + } + + // + // This is the 4th verify case. + // + // We ALWAYS force CREATE requests on unmounted volumes through the + // verify path. These requests could have been in limbo between + // IoCheckMountedVpb and us when a verify/mount took place and caused + // a completely different fs/volume to be mounted. In this case the + // checks above may not have caught the condition, since we may already + // have verified (wrong volume) and decided that we have nothing to do. + // We want the requests to be re routed to the currently mounted volume, + // since they were directed at the 'drive', not our volume. + // + + if (NT_SUCCESS( Status) && !ForceVerify && + (IrpContext->MajorFunction == IRP_MJ_CREATE)) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp); + + ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) && + ((Vcb->VcbCondition == VcbDismountInProgress) || + (Vcb->VcbCondition == VcbNotMounted)); + + // + // Note that we don't touch the device verify flag here. It required + // it would have been caught and set by the first set of checks. + // + } + + // + // Raise the verify / error if neccessary. + // + + if (ForceVerify || !NT_SUCCESS( Status)) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, + Vcb->Vpb->RealDevice ); + + CdRaiseStatus( IrpContext, ForceVerify ? STATUS_VERIFY_REQUIRED : Status); + } + } + + // + // Based on the condition of the Vcb we'll either return to our + // caller or raise an error condition + // + + switch (Vcb->VcbCondition) { + + case VcbNotMounted: + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); + + CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + break; + + case VcbInvalid: + case VcbDismountInProgress : + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + break; + } +} + + +BOOLEAN +CdVerifyFcbOperation ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to verify that the state of the Fcb is valid + to allow the current operation to continue. We use the state of the + Vcb, target device and type of operation to determine this. + +Arguments: + + IrpContext - IrpContext for the request. If not present then we + were called from the fast IO path. + + Fcb - Fcb to perform the request on. + +Return Value: + + BOOLEAN - TRUE if the request can continue, FALSE otherwise. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PVCB Vcb = Fcb->Vcb; + PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice; + PIRP Irp; + + PAGED_CODE(); + + // + // Check that the fileobject has not been cleaned up. + // + + if ( ARGUMENT_PRESENT( IrpContext )) { + + PFILE_OBJECT FileObject; + + Irp = IrpContext->Irp; + FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject; + + if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE)) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Following FAT, we allow certain operations even on cleaned up + // file objects. Everything else, we fail. + // + + if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) || + (IrpSp->MajorFunction == IRP_MJ_CLOSE ) || + (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || + ( (IrpSp->MajorFunction == IRP_MJ_READ) && + FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) { + + NOTHING; + + } else { + + CdRaiseStatus( IrpContext, STATUS_FILE_CLOSED ); + } + } + } + + // + // Fail immediately if the volume is in the progress of being dismounted + // or has been marked invalid. + // + + if ((Vcb->VcbCondition == VcbInvalid) || + (Vcb->VcbCondition == VcbDismountInProgress)) { + + if (ARGUMENT_PRESENT( IrpContext )) { + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + return FALSE; + } + + // + // Always fail if the volume needs to be verified. + // + + if (CdRealDevNeedsVerify( RealDevice)) { + + if (ARGUMENT_PRESENT( IrpContext )) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, + RealDevice ); + + CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); + } + + return FALSE; + + // + // + // All operations are allowed on mounted. + // + + } else if ((Vcb->VcbCondition == VcbMounted) || + (Vcb->VcbCondition == VcbMountInProgress)) { + + return TRUE; + + // + // Fail all requests for fast Io on other Vcb conditions. + // + + } else if (!ARGUMENT_PRESENT( IrpContext )) { + + return FALSE; + + // + // The remaining case is VcbNotMounted. + // Mark the device to be verified and raise WRONG_VOLUME. + // + + } else if (Vcb->VcbCondition == VcbNotMounted) { + + if (ARGUMENT_PRESENT( IrpContext )) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); + CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + } + + return FALSE; + } + + return TRUE; +} + + +BOOLEAN +CdDismountVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine is called when all of the user references to a volume are + gone. We will initiate all of the teardown any system resources. + + If all of the references to this volume are gone at the end of this routine + then we will complete the teardown of this Vcb and mark the current Vpb + as not mounted. Otherwise we will allocated a new Vpb for this device + and keep the current Vpb attached to the Vcb. + +Arguments: + + Vcb - Vcb for the volume to dismount. + +Return Value: + + BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise. + +--*/ + +{ + PVPB OldVpb; + BOOLEAN VcbPresent = TRUE; + KIRQL SavedIrql; + + BOOLEAN FinalReference; + + ASSERT_EXCLUSIVE_CDDATA; + ASSERT_EXCLUSIVE_VCB( Vcb ); + + CdLockVcb( IrpContext, Vcb ); + + // + // We should only take this path once. + // + + ASSERT( Vcb->VcbCondition != VcbDismountInProgress ); + + // + // Mark the Vcb as DismountInProgress. + // + + Vcb->VcbCondition = VcbDismountInProgress; + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + Vcb->XASector = 0; + Vcb->XADiskOffset = 0; + } + + // + // Remove our reference to the internal Fcb's. The Fcb's will then + // be removed in the purge path below. + // + + if (Vcb->RootIndexFcb != NULL) { + + Vcb->RootIndexFcb->FcbReference -= 1; + Vcb->RootIndexFcb->FcbUserReference -= 1; + } + + if (Vcb->PathTableFcb != NULL) { + + Vcb->PathTableFcb->FcbReference -= 1; + Vcb->PathTableFcb->FcbUserReference -= 1; + } + + if (Vcb->VolumeDasdFcb != NULL) { + + Vcb->VolumeDasdFcb->FcbReference -= 1; + Vcb->VolumeDasdFcb->FcbUserReference -= 1; + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // Purge the volume. + // + + CdPurgeVolume( IrpContext, Vcb, TRUE ); + + // + // Empty the delayed and async close queues. + // + + CdFspClose( Vcb ); + + OldVpb = Vcb->Vpb; + + // + // Remove the mount volume reference. + // + + CdLockVcb( IrpContext, Vcb ); + Vcb->VcbReference -= 1; + + // + // Acquire the Vpb spinlock to check for Vpb references. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // Remember if this is the last reference on this Vcb. We incremented + // the count on the Vpb earlier so we get one last crack it. If our + // reference has gone to zero but the vpb reference count is greater + // than zero then the Io system will be responsible for deleting the + // Vpb. + // + + FinalReference = (BOOLEAN) ((Vcb->VcbReference == 0) && + (OldVpb->ReferenceCount == 1)); + + // + // There is a reference count in the Vpb and in the Vcb. We have + // incremented the reference count in the Vpb to make sure that + // we have last crack at it. If this is a failed mount then we + // want to return the Vpb to the IO system to use for the next + // mount request. + // + + if (OldVpb->RealDevice->Vpb == OldVpb) { + + // + // If not the final reference then swap out the Vpb. We must + // preserve the REMOVE_PENDING flag so that the device is + // not remounted in the middle of a PnP remove operation. + // + + if (!FinalReference) { + + ASSERT( Vcb->SwapVpb != NULL ); + + Vcb->SwapVpb->Type = IO_TYPE_VPB; + Vcb->SwapVpb->Size = sizeof( VPB ); + Vcb->SwapVpb->RealDevice = OldVpb->RealDevice; + + Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb; + + Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING ); + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // Indicate we used up the swap. + // + + Vcb->SwapVpb = NULL; + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We want to leave the Vpb for the IO system. Mark it + // as being not mounted. Go ahead and delete the Vcb as + // well. + // + + } else { + + // + // Make sure to remove the last reference on the Vpb. + // + + OldVpb->ReferenceCount -= 1; + + OldVpb->DeviceObject = NULL; + ClearFlag( Vcb->Vpb->Flags, VPB_MOUNTED ); + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED ); + + // + // Clear the Vpb flag so we know not to delete it. + // + + Vcb->Vpb = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + } + + // + // Someone has already swapped in a new Vpb. If this is the final reference + // then the file system is responsible for deleting the Vpb. + // + + } else if (FinalReference) { + + // + // Make sure to remove the last reference on the Vpb. + // + + OldVpb->ReferenceCount -= 1; + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + + // + // The current Vpb is no longer the Vpb for the device (the IO system + // has already allocated a new one). We leave our reference in the + // Vpb and will be responsible for deleting it at a later time. + // + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Let our caller know whether the Vcb is still present. + // + + return VcbPresent; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/volinfo.c b/reactos/drivers/filesystems/cdfs_new/volinfo.c new file mode 100755 index 00000000000..801e5df78cc --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/volinfo.c @@ -0,0 +1,502 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VolInfo.c + +Abstract: + + This module implements the volume information routines for Cdfs called by + the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_VOLINFO) + +// +// Local support routines +// + +NTSTATUS +CdQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +CdQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +CdQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +CdQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonQueryVolInfo) +#pragma alloc_text(PAGE, CdQueryFsAttributeInfo) +#pragma alloc_text(PAGE, CdQueryFsDeviceInfo) +#pragma alloc_text(PAGE, CdQueryFsSizeInfo) +#pragma alloc_text(PAGE, CdQueryFsVolumeInfo) +#endif + + +NTSTATUS +CdCommonQueryVolInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying volume information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + ULONG Length; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryVolume.Length; + + // + // Decode the file object and fail if this an unopened file object. + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (TypeOfOpen == UnopenedFileObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire the Vcb for this volume. + // + + CdAcquireVcbShared( IrpContext, Fcb->Vcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Based on the information class we'll do different actions. Each + // of the procedures that we're calling fills up the output buffer + // if possible and returns true if it successfully filled the buffer + // and false if it couldn't wait for any I/O to complete. + // + + switch (IrpSp->Parameters.QueryVolume.FsInformationClass) { + + case FileFsSizeInformation: + + Status = CdQueryFsSizeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsVolumeInformation: + + Status = CdQueryFsVolumeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsDeviceInformation: + + Status = CdQueryFsDeviceInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsAttributeInformation: + + Status = CdQueryFsAttributeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + } + + // + // Set the information field to the number of bytes actually filled in + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Fcb->Vcb ); + } + + // + // Complete the request if we didn't raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume info call + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Fill in the data from the Vcb. + // + + Buffer->VolumeCreationTime = *((PLARGE_INTEGER) &Vcb->VolumeDasdFcb->CreationTime); + Buffer->VolumeSerialNumber = Vcb->Vpb->SerialNumber; + + Buffer->SupportsObjects = FALSE; + + *Length -= FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel[0] ); + + // + // Check if the buffer we're given is long enough + // + + if (*Length >= (ULONG) Vcb->Vpb->VolumeLabelLength) { + + BytesToCopy = Vcb->Vpb->VolumeLabelLength; + + } else { + + BytesToCopy = *Length; + + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Copy over what we can of the volume label, and adjust *Length + // + + Buffer->VolumeLabelLength = BytesToCopy; + + if (BytesToCopy) { + + RtlCopyMemory( &Buffer->VolumeLabel[0], + &Vcb->Vpb->VolumeLabel[0], + BytesToCopy ); + } + + *Length -= BytesToCopy; + + // + // Set our status and return to our caller + // + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume size call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + // + // Fill in the output buffer. + // + + Buffer->TotalAllocationUnits.QuadPart = LlSectorsFromBytes( Vcb->VolumeDasdFcb->AllocationSize.QuadPart ); + + Buffer->AvailableAllocationUnits.QuadPart = 0; + Buffer->SectorsPerAllocationUnit = 1; + Buffer->BytesPerSector = SECTOR_SIZE; + + // + // Adjust the length variable + // + + *Length -= sizeof( FILE_FS_SIZE_INFORMATION ); + + // + // And return success to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume device call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + // + // Update the output buffer. + // + + Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics; + Buffer->DeviceType = FILE_DEVICE_CD_ROM; + + // + // Adjust the length variable + // + + *Length -= sizeof( FILE_FS_DEVICE_INFORMATION ); + + // + // And return success to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume attribute call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Fill out the fixed portion of the buffer. + // + + Buffer->FileSystemAttributes = FILE_CASE_SENSITIVE_SEARCH | + FILE_READ_ONLY_VOLUME; + + if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + SetFlag( Buffer->FileSystemAttributes, FILE_UNICODE_ON_DISK ); + + Buffer->MaximumComponentNameLength = 110; + + } else { + + Buffer->MaximumComponentNameLength = 221; + } + + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ); + + // + // Make sure we can copy full unicode characters. + // + + ClearFlag( *Length, 1 ); + + // + // Determine how much of the file system name will fit. + // + + if (*Length >= 8) { + + BytesToCopy = 8; + + } else { + + BytesToCopy = *Length; + Status = STATUS_BUFFER_OVERFLOW; + } + + *Length -= BytesToCopy; + + // + // Do the file system name. + // + + Buffer->FileSystemNameLength = BytesToCopy; + + RtlCopyMemory( &Buffer->FileSystemName[0], L"CDFS", BytesToCopy ); + + // + // And return to our caller + // + + return Status; +} + diff --git a/reactos/drivers/filesystems/cdfs_new/workque.c b/reactos/drivers/filesystems/cdfs_new/workque.c new file mode 100755 index 00000000000..d32a014900f --- /dev/null +++ b/reactos/drivers/filesystems/cdfs_new/workque.c @@ -0,0 +1,398 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + WorkQue.c + +Abstract: + + This module implements the Work queue routines for the Cdfs File + system. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_WORKQUE) + +// +// The following constant is the maximum number of ExWorkerThreads that we +// will allow to be servicing a particular target device at any one time. +// + +#define FSP_PER_DEVICE_THRESHOLD (2) + +// +// Local support routines +// + +VOID +CdAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFsdPostRequest) +#pragma alloc_text(PAGE, CdOplockComplete) +#pragma alloc_text(PAGE, CdPrePostIrp) +#endif + + +NTSTATUS +CdFsdPostRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine enqueues the request packet specified by IrpContext to the + work queue associated with the FileSystemDeviceObject. This is a FSD + routine. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp. + + Irp - I/O Request Packet. + +Return Value: + + STATUS_PENDING + +--*/ + +{ + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Posting is a three step operation. First lock down any buffers + // in the Irp. Next cleanup the IrpContext for the post and finally + // add this to a workque. + // + + CdPrePostIrp( IrpContext, Irp ); + + CdAddToWorkque( IrpContext, Irp ); + + // + // And return to our caller + // + + return STATUS_PENDING; +} + + +VOID +CdPrePostIrp ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs any neccessary work before STATUS_PENDING is + returned with the Fsd thread. This routine is called within the + filesystem and by the oplock package. + +Arguments: + + Context - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Case on the type of the operation. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + // + // If called from the oplock package then there is an + // Fcb to possibly teardown. We will call the teardown + // routine and release the Fcb if still present. The cleanup + // code in create will know not to release this Fcb because + // we will clear the pointer. + // + + if ((IrpContext->TeardownFcb != NULL) && + *(IrpContext->TeardownFcb) != NULL) { + + CdTeardownStructures( IrpContext, *(IrpContext->TeardownFcb), &RemovedFcb ); + + if (!RemovedFcb) { + + CdReleaseFcb( IrpContext, *(IrpContext->TeardownFcb) ); + } + + *(IrpContext->TeardownFcb) = NULL; + IrpContext->TeardownFcb = NULL; + } + + break; + + // + // We need to lock the user's buffer, unless this is an MDL-read, + // in which case there is no user buffer. + // + + case IRP_MJ_READ : + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + CdLockUserBuffer( IrpContext, IrpSp->Parameters.Read.Length ); + } + + break; + + // + // We also need to check whether this is a query file operation. + // + + case IRP_MJ_DIRECTORY_CONTROL : + + if (IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) { + + CdLockUserBuffer( IrpContext, IrpSp->Parameters.QueryDirectory.Length ); + } + + break; + } + + // + // Cleanup the IrpContext for the post. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, TRUE ); + + // + // Mark the Irp to show that we've already returned pending to the user. + // + + IoMarkIrpPending( Irp ); + + return; +} + + +VOID +CdOplockComplete ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the oplock package when an oplock break has + completed, allowing an Irp to resume execution. If the status in + the Irp is STATUS_SUCCESS, then we queue the Irp to the Fsp queue. + Otherwise we complete the Irp with the status in the Irp. + + If we are completing due to an error then check if there is any + cleanup to do. + +Arguments: + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + // + // Check on the return value in the Irp. If success then we + // are to post this request. + // + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + // + // Check if there is any cleanup work to do. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + // + // If called from the oplock package then there is an + // Fcb to possibly teardown. We will call the teardown + // routine and release the Fcb if still present. The cleanup + // code in create will know not to release this Fcb because + // we will clear the pointer. + // + + if (IrpContext->TeardownFcb != NULL) { + + CdTeardownStructures( IrpContext, *(IrpContext->TeardownFcb), &RemovedFcb ); + + if (!RemovedFcb) { + + CdReleaseFcb( IrpContext, *(IrpContext->TeardownFcb) ); + } + + *(IrpContext->TeardownFcb) = NULL; + IrpContext->TeardownFcb = NULL; + } + + break; + } + + // + // Insert the Irp context in the workqueue. + // + + CdAddToWorkque( IrpContext, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + CdCompleteRequest( IrpContext, Irp, Irp->IoStatus.Status ); + } + + return; +} + + +// +// Local support routine +// + +VOID +CdAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called to acually store the posted Irp to the Fsp + workque. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PVOLUME_DEVICE_OBJECT Vdo; + KIRQL SavedIrql; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Check if this request has an associated file object, and thus volume + // device object. + // + + if (IrpSp->FileObject != NULL) { + + + Vdo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + + // + // Check to see if this request should be sent to the overflow + // queue. If not, then send it off to an exworker thread. + // + + KeAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql ); + + if (Vdo->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) { + + // + // We cannot currently respond to this IRP so we'll just enqueue it + // to the overflow queue on the volume. + // + + InsertTailList( &Vdo->OverflowQueue, + &IrpContext->WorkQueueItem.List ); + + Vdo->OverflowQueueCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + + return; + + } else { + + // + // We are going to send this Irp to an ex worker thread so up + // the count. + // + + Vdo->PostedRequestCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + } + } + + // + // Send it off..... + // + + ExInitializeWorkItem( &IrpContext->WorkQueueItem, + CdFspDispatch, + IrpContext ); + + ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue ); + + return; +} + + +