mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
4195 lines
114 KiB
C
4195 lines
114 KiB
C
/*++
|
|
|
|
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
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
BOOLEAN
|
|
CdPrepareBuffers (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIRP Irp,
|
|
_In_ PFCB Fcb,
|
|
_In_reads_bytes_(ByteCount) PVOID UserBuffer,
|
|
_In_ ULONG UserBufferOffset,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount,
|
|
_Out_ PIO_RUN IoRuns,
|
|
_Out_ PULONG RunCount,
|
|
_Out_ PULONG ThisByteCount
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdPrepareXABuffers (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIRP Irp,
|
|
_In_ PFCB Fcb,
|
|
_In_reads_bytes_(ByteCount) PVOID UserBuffer,
|
|
_In_ ULONG UserBufferOffset,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount,
|
|
_Out_ PIO_RUN IoRuns,
|
|
_Out_ PULONG RunCount,
|
|
_Out_ PULONG ThisByteCount
|
|
);
|
|
|
|
BOOLEAN
|
|
CdFinishBuffers (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIO_RUN IoRuns,
|
|
_In_ ULONG RunCount,
|
|
_In_ BOOLEAN FinalCleanup,
|
|
_In_ BOOLEAN SaveXABuffer
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdMultipleAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PFCB Fcb,
|
|
_In_ ULONG RunCount,
|
|
_Inout_ PIO_RUN IoRuns
|
|
);
|
|
|
|
VOID
|
|
CdMultipleXAAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ ULONG RunCount,
|
|
_Inout_ PIO_RUN IoRuns,
|
|
_In_ PRAW_READ_INFO RawReads,
|
|
_In_ TRACK_MODE_TYPE TrackMode
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdSingleAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_RUN Run,
|
|
_In_ PFCB Fcb
|
|
);
|
|
|
|
VOID
|
|
CdWaitSync (
|
|
_In_ PIRP_CONTEXT IrpContext
|
|
);
|
|
|
|
// Tell prefast this is a completion routine.
|
|
IO_COMPLETION_ROUTINE CdMultiSyncCompletionRoutine;
|
|
|
|
// Tell prefast this is a completion routine
|
|
IO_COMPLETION_ROUTINE CdMultiAsyncCompletionRoutine;
|
|
|
|
// Tell prefast this is a completion routine
|
|
IO_COMPLETION_ROUTINE CdSingleSyncCompletionRoutine;
|
|
|
|
// Tell prefast this is a completion routine
|
|
IO_COMPLETION_ROUTINE CdSingleAsyncCompletionRoutine;
|
|
|
|
_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset == 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + sizeof(RAW_DIRENT))))
|
|
_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset != 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + SECTOR_SIZE)))
|
|
VOID
|
|
CdReadAudioSystemFile (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PFCB Fcb,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ _In_range_(>=, CdAudioDirentSize) ULONG ByteCount,
|
|
_Out_writes_bytes_(ByteCount) PVOID SystemBuffer
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
BOOLEAN
|
|
CdReadDirDataThroughCache (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_RUN Run
|
|
);
|
|
|
|
#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, CdVolumeDasdWrite)
|
|
#pragma alloc_text(PAGE, CdFinishBuffers)
|
|
#pragma alloc_text(PAGE, CdPerformDevIoCtrl)
|
|
#pragma alloc_text(PAGE, CdPerformDevIoCtrlEx)
|
|
#pragma alloc_text(PAGE, CdPrepareBuffers)
|
|
#pragma alloc_text(PAGE, CdPrepareXABuffers)
|
|
#pragma alloc_text(PAGE, CdReadAudioSystemFile)
|
|
#pragma alloc_text(PAGE, CdReadSectors)
|
|
#pragma alloc_text(PAGE, CdSingleAsync)
|
|
#pragma alloc_text(PAGE, CdWaitSync)
|
|
#pragma alloc_text(PAGE, CdReadDirDataThroughCache)
|
|
#pragma alloc_text(PAGE, CdFreeDirCache)
|
|
#pragma alloc_text(PAGE, CdLbnToMmSsFf)
|
|
#pragma alloc_text(PAGE, CdHijackIrpAndFlushDevice)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
CdLbnToMmSsFf (
|
|
_In_ ULONG Blocks,
|
|
_Out_writes_(3) PUCHAR Msf
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert Lbn to MSF format.
|
|
|
|
Arguments:
|
|
|
|
Msf - on output, set to 0xMmSsFf representation of blocks.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
Blocks += 150; // Lbn 0 == 00:02:00, 1sec == 75 frames.
|
|
|
|
Msf[0] = (UCHAR)(Blocks % 75); // Frames
|
|
Blocks /= 75; // -> Seconds
|
|
Msf[1] = (UCHAR)(Blocks % 60); // Seconds
|
|
Blocks /= 60; // -> Minutes
|
|
Msf[2] = (UCHAR)Blocks; // Minutes
|
|
}
|
|
|
|
|
|
__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.
|
|
|
|
--*/
|
|
{
|
|
NT_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;
|
|
}
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
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, IoWriteAccess );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// If we're going to use the sector cache for this request, then
|
|
// mark the request waitable.
|
|
//
|
|
|
|
if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) &&
|
|
(NULL != Fcb->Vcb->SectorCacheBuffer) &&
|
|
(VcbMounted == IrpContext->Vcb->VcbCondition)) {
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
|
|
|
KeInitializeEvent( &IrpContext->IoContext->SyncEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to perform the final cleanup.
|
|
//
|
|
|
|
_SEH2_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], Fcb );
|
|
|
|
//
|
|
// 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, Fcb, 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;
|
|
} _SEH2_FINALLY {
|
|
|
|
//
|
|
// Perform final cleanup on the IoRuns if necessary.
|
|
//
|
|
|
|
if (CleanupRunCount != 0) {
|
|
|
|
CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE );
|
|
}
|
|
} _SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
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 = 0;
|
|
ULONG Address = 0;
|
|
|
|
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, IoWriteAccess );
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
_SEH2_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;
|
|
|
|
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;
|
|
|
|
//
|
|
// One frame == One sector.
|
|
// One second == 75 frames (winds up being a 44.1khz sample)
|
|
//
|
|
// Note: LBN 0 == 0:2:0 (MSF)
|
|
//
|
|
|
|
//
|
|
// Fill in the address (both MSF and Lbn format) and length fields.
|
|
//
|
|
|
|
SwapCopyUchar4( &Address, TrackData->Address);
|
|
CdLbnToMmSsFf( Address, AudioPlayHeader->TrackAddress);
|
|
|
|
SwapCopyUchar4( &AudioPlayHeader->StartingSector, TrackData->Address);
|
|
|
|
//
|
|
// Go to the next track and find the starting point.
|
|
//
|
|
|
|
TrackData = &Fcb->Vcb->CdromToc->TrackData[Fcb->XAFileNumber + 1];
|
|
|
|
SwapCopyUchar4( &AudioPlayHeader->SectorCount, TrackData->Address);
|
|
|
|
//
|
|
// 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. Bias by 150 to make
|
|
// it an "lbn" since the conversion routine corrects for Lbn 0 == 0:2:0;
|
|
//
|
|
|
|
Address = AudioPlayHeader->SectorCount - 150;
|
|
CdLbnToMmSsFf( Address, AudioPlayHeader->TrackLength);
|
|
|
|
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 {
|
|
|
|
NT_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;
|
|
} _SEH2_FINALLY {
|
|
|
|
//
|
|
// Perform final cleanup on the IoRuns if necessary.
|
|
//
|
|
|
|
if (CleanupRunCount != 0) {
|
|
|
|
CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE );
|
|
}
|
|
} _SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdVolumeDasdWrite (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PFCB Fcb,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the non-cached writes to 'cooked' sectors (2048 bytes
|
|
per sector). This is done by filling the IoRun for the desired request
|
|
and send it down to the device.
|
|
|
|
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;
|
|
IO_RUN IoRun;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We want to make sure the user's buffer is locked in all cases.
|
|
//
|
|
|
|
CdLockUserBuffer( IrpContext, ByteCount, IoReadAccess );
|
|
|
|
//
|
|
// The entire Io can be contained in a single run, just pass
|
|
// the Io down to the driver. Send the driver down
|
|
// and wait on the result if this is synchronous.
|
|
//
|
|
|
|
RtlZeroMemory( &IoRun, sizeof( IoRun ) );
|
|
|
|
IoRun.DiskOffset = StartingOffset;
|
|
IoRun.DiskByteCount = ByteCount;
|
|
|
|
CdSingleAsync( IrpContext, &IoRun, Fcb );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
CdReadSectors (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount,
|
|
_In_ BOOLEAN ReturnError,
|
|
_Out_writes_bytes_(ByteCount) 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,
|
|
_In_ LOCK_OPERATION Operation
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
Operation - IoWriteAccess or IoReadAccess
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status from this routine. Error status only returned if
|
|
RaiseOnError is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER( Operation );
|
|
UNREFERENCED_PARAMETER( IrpContext );
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_IRP( IrpContext->Irp );
|
|
NT_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.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
MmProbeAndLockPages( Mdl, IrpContext->Irp->RequestorMode, IoWriteAccess );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress: 6320)
|
|
#endif
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
IoFreeMdl( Mdl );
|
|
IrpContext->Irp->MdlAddress = NULL;
|
|
|
|
if (!FsRtlIsNtstatusExpected( Status )) {
|
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
} _SEH2_END;
|
|
}
|
|
|
|
//
|
|
// Check if we are to raise or return
|
|
//
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
if (RaiseOnError) {
|
|
|
|
CdRaiseStatus( IrpContext, Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the status code.
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdPerformDevIoCtrlEx (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ ULONG IoControlCode,
|
|
_In_ PDEVICE_OBJECT Device,
|
|
_In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
|
|
_In_ ULONG InputBufferLength,
|
|
_Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
|
|
_In_ ULONG OutputBufferLength,
|
|
_In_ BOOLEAN InternalDeviceIoControl,
|
|
_In_ BOOLEAN OverrideVerify,
|
|
_Out_opt_ PIO_STATUS_BLOCK Iosb
|
|
)
|
|
|
|
/*++
|
|
|
|
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();
|
|
|
|
UNREFERENCED_PARAMETER( IrpContext );
|
|
|
|
//
|
|
// 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,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
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;
|
|
}
|
|
|
|
NT_ASSERT( !(OverrideVerify && (STATUS_VERIFY_REQUIRED == Status)));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
CdPerformDevIoCtrl (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ ULONG IoControlCode,
|
|
_In_ PDEVICE_OBJECT Device,
|
|
_Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
|
|
_In_ ULONG OutputBufferLength,
|
|
_In_ BOOLEAN InternalDeviceIoControl,
|
|
_In_ BOOLEAN OverrideVerify,
|
|
_Out_opt_ PIO_STATUS_BLOCK Iosb
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return CdPerformDevIoCtrlEx( IrpContext,
|
|
IoControlCode,
|
|
Device,
|
|
NULL,
|
|
0,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
InternalDeviceIoControl,
|
|
OverrideVerify,
|
|
Iosb);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
BOOLEAN
|
|
CdPrepareBuffers (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIRP Irp,
|
|
_In_ PFCB Fcb,
|
|
_In_reads_bytes_(ByteCount) PVOID UserBuffer,
|
|
_In_ ULONG UserBufferOffset,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount,
|
|
_Out_ PIO_RUN IoRuns,
|
|
_Out_ PULONG RunCount,
|
|
_Out_ 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;
|
|
|
|
//
|
|
// The following is the next contiguous bytes on the disk to
|
|
// transfer. Read from the allocation package.
|
|
//
|
|
|
|
LONGLONG DiskOffset = 0;
|
|
ULONG CurrentByteCount = RemainingByteCount;
|
|
|
|
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)
|
|
//
|
|
// 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( (ULONG) CurrentByteCount, SECTOR_MASK ) &&
|
|
(CurrentByteCount < SECTOR_SIZE))) {
|
|
|
|
NT_ASSERT( SafeNodeType(Fcb) != CDFS_NTC_FCB_INDEX);
|
|
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// We need to allocate an auxilary buffer for the next sector.
|
|
// 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 );
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdPrepareXABuffers (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIRP Irp,
|
|
_In_ PFCB Fcb,
|
|
_In_reads_bytes_(ByteCount) PVOID UserBuffer,
|
|
_In_ ULONG UserBufferOffset,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ ULONG ByteCount,
|
|
_Out_ PIO_RUN IoRuns,
|
|
_Out_ PULONG RunCount,
|
|
_Out_ 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;
|
|
|
|
//
|
|
// 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 = 0;
|
|
ULONG CurrentCookedByteCount = 0;
|
|
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);
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress: __WARNING_RESULTOFSHIFTCASTTOLARGERSIZE, "This is fine beacuse raw sector size > sector shift" )
|
|
#endif
|
|
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;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
PerformedCopy = TRUE;
|
|
|
|
CurrentCookedByteCount = SECTOR_SIZE;
|
|
|
|
} 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) &&
|
|
(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(CurrentUserBuffer, 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) > 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);
|
|
|
|
//
|
|
// Point to the user's buffer and Mdl for this transfer.
|
|
//
|
|
|
|
ThisIoRun->TransferBuffer = CurrentUserBuffer;
|
|
ThisIoRun->TransferMdl = Irp->MdlAddress;
|
|
ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer,
|
|
CurrentUserBufferOffset,
|
|
PVOID);
|
|
|
|
} 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,
|
|
_Inout_ 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) {
|
|
|
|
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;
|
|
}
|
|
|
|
// Tell prefast this is a completion routine.
|
|
IO_COMPLETION_ROUTINE CdSyncCompletionRoutine;
|
|
|
|
NTSTATUS
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdSyncCompletionRoutine (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Contxt
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for synchronizing back to dispatch.
|
|
|
|
Arguments:
|
|
|
|
Contxt - pointer to KEVENT.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEVENT Event = (PKEVENT)Contxt;
|
|
_Analysis_assume_(Contxt != NULL);
|
|
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
KeSetEvent( Event, 0, FALSE );
|
|
|
|
//
|
|
// We don't want IO to get our IRP and free it.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdFreeDirCache (
|
|
_In_ PIRP_CONTEXT IrpContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Safely frees the sector cache buffer.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (NULL != IrpContext->Vcb->SectorCacheBuffer) {
|
|
|
|
CdAcquireCacheForUpdate( IrpContext);
|
|
CdFreePool( &IrpContext->Vcb->SectorCacheBuffer);
|
|
CdReleaseCache( IrpContext);
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
BOOLEAN
|
|
CdReadDirDataThroughCache (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_RUN Run
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads blocks through the sector cache. If the data is present, then it
|
|
is copied from memory. If not present, one of the cache chunks will be
|
|
replaced with a chunk containing the requested region, and the data
|
|
copied from there.
|
|
|
|
Only intended for reading *directory* blocks, for the purpose of pre-caching
|
|
directory information, by reading a chunk of blocks which hopefully contains
|
|
other directory blocks, rather than just the (usually) single block requested.
|
|
|
|
Arguments:
|
|
|
|
Run - description of extent required, and buffer to read into.
|
|
|
|
Return Value:
|
|
|
|
None. Raises on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVCB Vcb = IrpContext->Vcb;
|
|
ULONG Lbn = SectorsFromLlBytes( Run->DiskOffset);
|
|
ULONG Remaining = SectorsFromBytes( Run->DiskByteCount);
|
|
PUCHAR UserBuffer = Run->TransferBuffer;
|
|
|
|
NTSTATUS Status;
|
|
ULONG Found;
|
|
ULONG BufferSectorOffset;
|
|
ULONG StartBlock;
|
|
ULONG EndBlock;
|
|
ULONG Blocks;
|
|
|
|
PIO_STACK_LOCATION IrpSp;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
PTRACK_DATA TrackData;
|
|
|
|
#if DBG
|
|
BOOLEAN JustRead = FALSE;
|
|
#endif
|
|
|
|
ULONG Index;
|
|
PCD_SECTOR_CACHE_CHUNK Buffer;
|
|
BOOLEAN Result = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
CdAcquireCacheForRead( IrpContext);
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Check the cache hasn't gone away due to volume verify failure (which
|
|
// is the *only* reason it'll go away). If this is the case we raise
|
|
// the same error any I/O would return if the cache weren't here.
|
|
//
|
|
|
|
if (NULL == Vcb->SectorCacheBuffer) {
|
|
|
|
CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED);
|
|
}
|
|
|
|
while (Remaining) {
|
|
|
|
Buffer = NULL;
|
|
|
|
//
|
|
// Look to see if any portion is currently cached.
|
|
//
|
|
|
|
for (Index = 0; Index < CD_SEC_CACHE_CHUNKS; Index++) {
|
|
|
|
if ((Vcb->SecCacheChunks[ Index].BaseLbn != -1) &&
|
|
(Vcb->SecCacheChunks[ Index].BaseLbn <= Lbn) &&
|
|
((Vcb->SecCacheChunks[ Index].BaseLbn + CD_SEC_CHUNK_BLOCKS) > Lbn)) {
|
|
|
|
Buffer = &Vcb->SecCacheChunks[ Index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found any, copy it out and continue.
|
|
//
|
|
|
|
if (NULL != Buffer) {
|
|
|
|
BufferSectorOffset = Lbn - Buffer->BaseLbn;
|
|
Found = Min( CD_SEC_CHUNK_BLOCKS - BufferSectorOffset, Remaining);
|
|
|
|
RtlCopyMemory( UserBuffer,
|
|
Buffer->Buffer + BytesFromSectors( BufferSectorOffset),
|
|
BytesFromSectors( Found));
|
|
|
|
Remaining -= Found;
|
|
UserBuffer += BytesFromSectors( Found);
|
|
Lbn += Found;
|
|
#if DBG
|
|
//
|
|
// Update stats. Don't count a hit if we've just read the data in.
|
|
//
|
|
|
|
if (!JustRead) {
|
|
|
|
InterlockedIncrement( (LONG*)&Vcb->SecCacheHits);
|
|
}
|
|
|
|
JustRead = FALSE;
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Missed the cache, so we need to read a new chunk. Take the cache
|
|
// resource exclusive while we do so.
|
|
//
|
|
|
|
CdReleaseCache( IrpContext);
|
|
CdAcquireCacheForUpdate( IrpContext);
|
|
#if DBG
|
|
Vcb->SecCacheMisses += 1;
|
|
#endif
|
|
//
|
|
// Select the chunk to replace and calculate the start block of the
|
|
// chunk to cache. We cache blocks which start on Lbns aligned on
|
|
// multiples of chunk size, treating block 16 (VRS start) as block
|
|
// zero.
|
|
//
|
|
|
|
Buffer = &Vcb->SecCacheChunks[ Vcb->SecCacheLRUChunkIndex];
|
|
|
|
StartBlock = Lbn - ((Lbn - 16) % CD_SEC_CHUNK_BLOCKS);
|
|
|
|
//
|
|
// Make sure we don't try and read past end of the last track.
|
|
//
|
|
|
|
#ifdef __REACTOS__
|
|
if (Vcb->CdromToc) {
|
|
#endif
|
|
TrackData = &Vcb->CdromToc->TrackData[(Vcb->CdromToc->LastTrack - Vcb->CdromToc->FirstTrack + 1)];
|
|
|
|
SwapCopyUchar4( &EndBlock, &TrackData->Address );
|
|
|
|
Blocks = EndBlock - StartBlock;
|
|
|
|
if (Blocks > CD_SEC_CHUNK_BLOCKS) {
|
|
|
|
Blocks = CD_SEC_CHUNK_BLOCKS;
|
|
}
|
|
#ifdef __REACTOS__
|
|
} else {
|
|
// HACK!!!!!!!! Might cause reads to overrun the end of the partition, no idea what consequences that can have.
|
|
Blocks = CD_SEC_CHUNK_BLOCKS;
|
|
}
|
|
#endif
|
|
|
|
if ((0 == Blocks) || (Lbn < 16)) {
|
|
|
|
CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Now build / send the read request.
|
|
//
|
|
|
|
IoReuseIrp( Vcb->SectorCacheIrp, STATUS_SUCCESS);
|
|
|
|
KeClearEvent( &Vcb->SectorCacheEvent);
|
|
Vcb->SectorCacheIrp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
//
|
|
// Get a pointer to the stack location of the first driver which will be
|
|
// invoked. This is where the function codes and the parameters are set.
|
|
//
|
|
|
|
IrpSp = IoGetNextIrpStackLocation( Vcb->SectorCacheIrp);
|
|
IrpSp->MajorFunction = (UCHAR) IRP_MJ_READ;
|
|
|
|
//
|
|
// Build an MDL to describe the buffer.
|
|
//
|
|
|
|
IoAllocateMdl( Buffer->Buffer,
|
|
BytesFromSectors( Blocks),
|
|
FALSE,
|
|
FALSE,
|
|
Vcb->SectorCacheIrp);
|
|
|
|
if (NULL == Vcb->SectorCacheIrp->MdlAddress) {
|
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// We're reading/writing into the block cache (paged pool). Lock the
|
|
// pages and update the MDL with physical page information.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
MmProbeAndLockPages( Vcb->SectorCacheIrp->MdlAddress,
|
|
KernelMode,
|
|
(LOCK_OPERATION) IoWriteAccess );
|
|
}
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress: 6320)
|
|
#endif
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
IoFreeMdl( Vcb->SectorCacheIrp->MdlAddress );
|
|
Vcb->SectorCacheIrp->MdlAddress = NULL;
|
|
} _SEH2_END;
|
|
|
|
if (NULL == Vcb->SectorCacheIrp->MdlAddress) {
|
|
|
|
CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
//
|
|
// Reset the BaseLbn as we can't trust this Buffer's data until the request
|
|
// is successfully completed.
|
|
//
|
|
|
|
Buffer->BaseLbn = (ULONG)-1;
|
|
|
|
IrpSp->Parameters.Read.Length = BytesFromSectors( Blocks);
|
|
IrpSp->Parameters.Read.ByteOffset.QuadPart = LlBytesFromSectors( StartBlock);
|
|
|
|
IoSetCompletionRoutine( Vcb->SectorCacheIrp,
|
|
CdSyncCompletionRoutine,
|
|
&Vcb->SectorCacheEvent,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
Vcb->SectorCacheIrp->UserIosb = &Iosb;
|
|
|
|
Status = IoCallDriver( Vcb->TargetDeviceObject, Vcb->SectorCacheIrp );
|
|
|
|
if (STATUS_PENDING == Status) {
|
|
|
|
|
|
(VOID)KeWaitForSingleObject( &Vcb->SectorCacheEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
Status = Vcb->SectorCacheIrp->IoStatus.Status;
|
|
}
|
|
|
|
Vcb->SectorCacheIrp->UserIosb = NULL;
|
|
|
|
//
|
|
// Unlock the pages and free the MDL.
|
|
//
|
|
|
|
MmUnlockPages( Vcb->SectorCacheIrp->MdlAddress );
|
|
IoFreeMdl( Vcb->SectorCacheIrp->MdlAddress );
|
|
Vcb->SectorCacheIrp->MdlAddress = NULL;
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
try_leave( Status );
|
|
}
|
|
|
|
//
|
|
// Update the buffer information, and drop the cache resource to shared
|
|
// to allow in reads.
|
|
//
|
|
|
|
Buffer->BaseLbn = StartBlock;
|
|
Vcb->SecCacheLRUChunkIndex = (Vcb->SecCacheLRUChunkIndex + 1) % CD_SEC_CACHE_CHUNKS;
|
|
|
|
CdConvertCacheToShared( IrpContext);
|
|
#if DBG
|
|
JustRead = TRUE;
|
|
#endif
|
|
}
|
|
|
|
Result = TRUE;
|
|
}
|
|
_SEH2_FINALLY {
|
|
|
|
CdReleaseCache( IrpContext);
|
|
} _SEH2_END;
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdMultipleAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PFCB Fcb,
|
|
_In_ ULONG RunCount,
|
|
_Inout_ 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;
|
|
BOOLEAN UseSectorCache;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up things according to whether this is truely async.
|
|
//
|
|
|
|
CompletionRoutine = CdMultiSyncCompletionRoutine;
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
|
|
|
CompletionRoutine = CdMultiAsyncCompletionRoutine;
|
|
}
|
|
|
|
//
|
|
// For directories, use the sector cache.
|
|
//
|
|
|
|
if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) &&
|
|
(NULL != Fcb->Vcb->SectorCacheBuffer) &&
|
|
(VcbMounted == IrpContext->Vcb->VcbCondition)) {
|
|
|
|
UseSectorCache = TRUE;
|
|
}
|
|
else {
|
|
|
|
UseSectorCache = FALSE;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
if (UseSectorCache) {
|
|
|
|
if (!CdReadDirDataThroughCache( IrpContext, &IoRuns[ UnwindRunCount])) {
|
|
|
|
//
|
|
// Turn off using directory cache and restart all over again.
|
|
//
|
|
|
|
UseSectorCache = FALSE;
|
|
UnwindRunCount = 0;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// If we used the cache, we're done.
|
|
//
|
|
|
|
if (UseSectorCache) {
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
|
|
|
IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
KeSetEvent( &IrpContext->IoContext->SyncEvent, 0, FALSE );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// If we (FS) acquired locks, transition the lock owners to an object, since
|
|
// when we return this thread could go away before request completion, and
|
|
// the resource package may otherwise try to boost priority, etc.
|
|
//
|
|
|
|
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ) &&
|
|
FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) {
|
|
|
|
NT_ASSERT( IrpContext->IoContext->ResourceThreadId == (ERESOURCE_THREAD)PsGetCurrentThread() );
|
|
|
|
IrpContext->IoContext->ResourceThreadId = ((ULONG_PTR)IrpContext->IoContext) | 3;
|
|
|
|
ExSetResourceOwnerPointer( IrpContext->IoContext->Resource,
|
|
(PVOID)IrpContext->IoContext->ResourceThreadId );
|
|
}
|
|
|
|
//
|
|
// 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 (NULL != Irp) {
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
CdMultipleXAAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ ULONG RunCount,
|
|
_Inout_ 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
|
|
//
|
|
|
|
NT_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
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
CdSingleAsync (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_RUN Run,
|
|
_In_ PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
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();
|
|
|
|
//
|
|
// For directories, look in the sector cache,
|
|
//
|
|
|
|
if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) &&
|
|
(NULL != Fcb->Vcb->SectorCacheBuffer) &&
|
|
(VcbMounted == IrpContext->Vcb->VcbCondition)) {
|
|
|
|
if (CdReadDirDataThroughCache( IrpContext, Run )) {
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
|
|
|
IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
KeSetEvent( &IrpContext->IoContext->SyncEvent, 0, FALSE );
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set up things according to whether this is truely async.
|
|
//
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
|
|
|
CompletionRoutine = CdSingleSyncCompletionRoutine;
|
|
|
|
} else {
|
|
|
|
CompletionRoutine = CdSingleAsyncCompletionRoutine;
|
|
|
|
//
|
|
// If we (FS) acquired locks, transition the lock owners to an object, since
|
|
// when we return this thread could go away before request completion, and
|
|
// the resource package may otherwise try to boost priority, etc.
|
|
//
|
|
|
|
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) {
|
|
|
|
NT_ASSERT( IrpContext->IoContext->ResourceThreadId == (ERESOURCE_THREAD)PsGetCurrentThread() );
|
|
|
|
IrpContext->IoContext->ResourceThreadId = ((ULONG_PTR)IrpContext->IoContext) | 3;
|
|
|
|
ExSetResourceOwnerPointer( IrpContext->IoContext->Resource,
|
|
(PVOID)IrpContext->IoContext->ResourceThreadId );
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 = IrpContext->MajorFunction;
|
|
IrpSp->Parameters.Read.Length = Run->DiskByteCount;
|
|
IrpSp->Parameters.Read.ByteOffset.QuadPart = Run->DiskOffset;
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
|
|
|
|
//
|
|
// 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();
|
|
|
|
|
|
(VOID)KeWaitForSingleObject( &IrpContext->IoContext->SyncEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
KeClearEvent( &IrpContext->IoContext->SyncEvent );
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdMultiSyncCompletionRoutine (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
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;
|
|
_Analysis_assume_(Context != NULL);
|
|
|
|
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
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdMultiAsyncCompletionRoutine (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
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;
|
|
_Analysis_assume_(Context != NULL);
|
|
AssertVerifyDeviceIrp( Irp );
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
_Analysis_assume_lock_held_(*IoContext->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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdSingleSyncCompletionRoutine (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
_Analysis_assume_(Context != NULL);
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
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
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
CdSingleAsyncCompletionRoutine (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_IO_CONTEXT IoContext = Context;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
_Analysis_assume_(IoContext != NULL);
|
|
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 = IoContext->RequestedByteCount;
|
|
}
|
|
|
|
//
|
|
// Mark the Irp pending
|
|
//
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// Now release the resource
|
|
//
|
|
|
|
_Analysis_assume_lock_held_(*IoContext->Resource);
|
|
ExReleaseResourceForThreadLite( IoContext->Resource, IoContext->ResourceThreadId );
|
|
|
|
//
|
|
// and finally, free the context record.
|
|
//
|
|
|
|
CdFreeIoContext( IoContext );
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset == 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + sizeof(RAW_DIRENT))))
|
|
_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset != 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + SECTOR_SIZE)))
|
|
VOID
|
|
CdReadAudioSystemFile (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PFCB Fcb,
|
|
_In_ LONGLONG StartingOffset,
|
|
_In_ _In_range_(>=, CdAudioDirentSize) ULONG ByteCount,
|
|
_Out_writes_bytes_(ByteCount) 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.
|
|
//
|
|
|
|
NT_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 00:00
|
|
//
|
|
|
|
RawDirent->RecordTime[0] = 95;
|
|
RawDirent->RecordTime[1] = 1;
|
|
RawDirent->RecordTime[2] = 1;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdHijackIrpAndFlushDevice (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp,
|
|
_In_ PDEVICE_OBJECT TargetDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we need to send a flush to a device but
|
|
we don't have a flush Irp. What this routine does is make a copy
|
|
of its current Irp stack location, but changes the Irp Major code
|
|
to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at
|
|
the knees in the completion routine, fix it up and return to the
|
|
user as if nothing had happened.
|
|
|
|
Arguments:
|
|
|
|
Irp - The Irp to hijack
|
|
|
|
TargetDeviceObject - The device to send the request to.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Status from the flush in case anybody cares.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION NextIrpSp;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER( IrpContext );
|
|
|
|
//
|
|
// Get the next stack location, and copy over the stack location
|
|
//
|
|
|
|
NextIrpSp = IoGetNextIrpStackLocation( Irp );
|
|
|
|
*NextIrpSp = *IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
|
|
NextIrpSp->MinorFunction = 0;
|
|
|
|
//
|
|
// Set up the completion routine
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
CdSyncCompletionRoutine,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Send the request.
|
|
//
|
|
|
|
Status = IoCallDriver( TargetDeviceObject, Irp );
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// If the driver doesn't support flushes, return SUCCESS.
|
|
//
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Status = 0;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|