mirror of
https://github.com/reactos/reactos.git
synced 2024-08-29 22:58:34 +00:00
1244 lines
33 KiB
C
1244 lines
33 KiB
C
|
|
/*++
|
|
|
|
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( <CD Audio Raw 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 // <CD Audio Raw Data> 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 ( <CDXA Raw 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 // <CD-XA Raw Sectors> ADJUST
|
|
};
|
|
|
|
#ifdef CDFS_TELEMETRY_DATA
|
|
|
|
//
|
|
// Telemetry Data for reporting
|
|
//
|
|
|
|
CDFS_TELEMETRY_DATA_CONTEXT CdTelemetryData;
|
|
|
|
#endif // CDFS_TELEMETRY_DATA
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, CdFastIoCheckIfPossible)
|
|
#pragma alloc_text(PAGE, CdSerial32)
|
|
#pragma alloc_text(PAGE, CdSetThreadContext)
|
|
#endif
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
__drv_dispatchType(DRIVER_DISPATCH)
|
|
__drv_dispatchType(IRP_MJ_CREATE)
|
|
__drv_dispatchType(IRP_MJ_CLOSE)
|
|
__drv_dispatchType(IRP_MJ_READ)
|
|
__drv_dispatchType(IRP_MJ_WRITE)
|
|
__drv_dispatchType(IRP_MJ_QUERY_INFORMATION)
|
|
__drv_dispatchType(IRP_MJ_SET_INFORMATION)
|
|
__drv_dispatchType(IRP_MJ_QUERY_VOLUME_INFORMATION)
|
|
__drv_dispatchType(IRP_MJ_DIRECTORY_CONTROL)
|
|
__drv_dispatchType(IRP_MJ_FILE_SYSTEM_CONTROL)
|
|
__drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
|
|
__drv_dispatchType(IRP_MJ_LOCK_CONTROL)
|
|
__drv_dispatchType(IRP_MJ_CLEANUP)
|
|
__drv_dispatchType(IRP_MJ_PNP)
|
|
__drv_dispatchType(IRP_MJ_SHUTDOWN)
|
|
NTSTATUS
|
|
NTAPI
|
|
CdFsdDispatch (
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_Inout_ 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:
|
|
|
|
DeviceObject - 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 = {0};
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
BOOLEAN Wait;
|
|
|
|
#ifdef CD_SANITY
|
|
PVOID PreviousTopLevel;
|
|
#endif
|
|
|
|
NTSTATUS Status;
|
|
|
|
#if DBG
|
|
|
|
KIRQL SaveIrql = KeGetCurrentIrql();
|
|
|
|
#endif
|
|
|
|
ASSERT_OPTIONAL_IRP( Irp );
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
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.
|
|
//
|
|
|
|
_SEH2_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
|
|
NT_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_WRITE :
|
|
|
|
Status = CdCommonWrite( 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;
|
|
|
|
case IRP_MJ_SHUTDOWN :
|
|
|
|
Status = CdCommonShutdown( IrpContext, Irp );
|
|
break;
|
|
|
|
default :
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
CdCompleteRequest( IrpContext, Irp, Status );
|
|
}
|
|
|
|
} _SEH2_EXCEPT( CdExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
Status = CdProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
|
|
} _SEH2_END;
|
|
|
|
} while (Status == STATUS_CANT_WAIT);
|
|
|
|
#ifdef CD_SANITY
|
|
NT_ASSERT( !CdTestTopLevel ||
|
|
(PreviousTopLevel == IoGetTopLevelIrp()) );
|
|
#endif
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
NT_ASSERT( SaveIrql == KeGetCurrentIrql( ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
#ifdef CD_SANITY
|
|
|
|
VOID
|
|
CdRaiseStatusEx (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ NTSTATUS Status,
|
|
_In_ BOOLEAN NormalizeStatus,
|
|
_In_opt_ ULONG FileId,
|
|
_In_opt_ 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);
|
|
|
|
NT_ASSERT(FALSE);
|
|
}
|
|
|
|
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 (
|
|
_Inout_ 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 )) {
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress: __WARNING_USE_OTHER_FUNCTION, "We're corrupted." )
|
|
#endif
|
|
CdBugCheck( (ULONG_PTR) ExceptionPointer->ExceptionRecord,
|
|
(ULONG_PTR) ExceptionPointer->ContextRecord,
|
|
(ULONG_PTR) ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
|
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdProcessException (
|
|
_In_opt_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ 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 = NULL;
|
|
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;
|
|
|
|
//
|
|
// 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, or within a guarded region. 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).
|
|
//
|
|
|
|
_SEH2_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) &&
|
|
FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) &&
|
|
KeAreAllApcsDisabled()) {
|
|
|
|
ExceptionCode = CdFsdPostRequest( IrpContext, Irp );
|
|
}
|
|
}
|
|
_SEH2_EXCEPT( CdExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
ExceptionCode = _SEH2_GetExceptionCode();
|
|
} _SEH2_END;
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
|
|
NT_ASSERT( Device != NULL );
|
|
|
|
}
|
|
|
|
//
|
|
// It turns out some storage drivers really do set invalid non-NULL device
|
|
// objects to verify.
|
|
//
|
|
// To work around this, completely ignore the device to verify in the thread,
|
|
// and just use our real device object instead.
|
|
//
|
|
|
|
if (IrpContext->Vcb) {
|
|
|
|
Device = IrpContext->Vcb->Vpb->RealDevice;
|
|
}
|
|
|
|
//
|
|
// Let's not BugCheck just because the device to verify is somehow still NULL.
|
|
//
|
|
|
|
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 );
|
|
|
|
NT_ASSERT( Device != NULL );
|
|
}
|
|
|
|
//
|
|
// It turns out some storage drivers really do set invalid non-NULL device
|
|
// objects to verify.
|
|
//
|
|
// To work around this, completely ignore the device to verify in the thread,
|
|
// and just use our real device object instead.
|
|
//
|
|
|
|
if (IrpContext->Vcb) {
|
|
|
|
Device = IrpContext->Vcb->Vpb->RealDevice;
|
|
}
|
|
|
|
//
|
|
// Let's not BugCheck just because the device to verify is somehow still NULL.
|
|
//
|
|
|
|
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 (
|
|
_Inout_opt_ PIRP_CONTEXT IrpContext,
|
|
_Inout_opt_ PIRP Irp,
|
|
_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 (
|
|
_Inout_ 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;
|
|
#ifdef __REACTOS__
|
|
ULONG_PTR StackTop;
|
|
ULONG_PTR StackBottom;
|
|
#endif
|
|
|
|
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.
|
|
//
|
|
|
|
#ifdef __REACTOS__
|
|
IoGetStackLimits( &StackTop, &StackBottom);
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress: 6011) // Bug in PREFast around bitflag operations
|
|
#endif
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) ||
|
|
#ifndef __REACTOS__
|
|
(!IoWithinStackLimits( (ULONG_PTR)CurrentThreadContext, sizeof( THREAD_CONTEXT ) ) ||
|
|
#else
|
|
(((ULONG_PTR) CurrentThreadContext > StackBottom - sizeof( THREAD_CONTEXT )) ||
|
|
((ULONG_PTR) CurrentThreadContext <= StackTop) ||
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
|
|
_Function_class_(FAST_IO_CHECK_IF_POSSIBLE)
|
|
_IRQL_requires_same_
|
|
_Success_(return != FALSE)
|
|
BOOLEAN
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdFastIoCheckIfPossible (
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ BOOLEAN Wait,
|
|
_In_ ULONG LockKey,
|
|
_In_ BOOLEAN CheckForReadOperation,
|
|
_Pre_notnull_
|
|
_When_(return != FALSE, _Post_equal_to_(_Old_(IoStatus)))
|
|
_When_(return == FALSE, _Post_valid_)
|
|
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();
|
|
|
|
UNREFERENCED_PARAMETER( Wait );
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
//
|
|
// 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_reads_bytes_(ByteCount) 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;
|
|
}
|
|
|
|
|