xbox-kernel/private/ntos/fatx/readwrit.c.bak
2020-09-30 17:17:25 +02:00

1673 lines
49 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
readwrit.c
Abstract:
This module implements routines related to handling IRP_MJ_READ and
IRP_MJ_WRITE.
--*/
#include "fatx.h"
//
// Local support.
//
VOID
FatxStartNextAsynchronousIoEntry(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PFAT_ASYNC_IO_DESCRIPTOR AsyncIoDescriptor
);
NTSTATUS
FatxVolumeIoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is called to process the completion of a volume I/O transfer.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Context - Specifies the context that was supplied to IoSetCompletionRoutine.
Return Value:
Status of operation.
--*/
{
PFAT_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
//
// Propagate the pending flag up the IRP stack.
//
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// Check if this is a read or write completion. This completion routine is
// shared by other IRP dispatch routines.
//
if ((IrpSp->MajorFunction == IRP_MJ_READ) ||
(IrpSp->MajorFunction == IRP_MJ_WRITE)) {
if (NT_SUCCESS(Irp->IoStatus.Status)) {
ASSERT(Irp->IoStatus.Information > 0);
ASSERT(FatxIsSectorAligned(VolumeExtension, Irp->IoStatus.Information));
//
// If the file is open for synchronous I/O, then we need to update
// the current file position.
//
FileObject = IrpSp->FileObject;
if (FatxIsFlagSet(FileObject->Flags, FO_SYNCHRONOUS_IO)) {
FileObject->CurrentByteOffset.QuadPart =
IrpSp->Parameters.Read.ByteOffset.QuadPart +
Irp->IoStatus.Information;
}
}
}
FatxDpcReleaseVolumeMutex(VolumeExtension);
return STATUS_SUCCESS;
}
NTSTATUS
FatxSignalIoEventCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is called to signal an event when a thread is blocked on an
I/O operation inside an IRP dispatch routine.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Context - Specifies the context that was supplied to IoSetCompletionRoutine.
Return Value:
Status of operation.
--*/
{
KeSetEvent((PKEVENT)Context, IO_DISK_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
FatxSynchronousIoTail(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG FileByteOffset,
IN ULONG IoLength
)
/*++
Routine Description:
This routine is called at the tail of the non-cached and cached synchronous
I/O routines to update the state in the file control block and file objects.
This routine is only called on success from these other routines.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
MajorFunction - Specifies the function to be performed; either IRP_MJ_READ
or IRP_MJ_WRITE.
FileObject - Specifies the file object that the I/O request is for.
FileByteOffset - Specifies the file byte offset where the transfer started
from.
IoLength - Specifies the number of bytes that were transferred.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_FCB FileFcb;
ULONG EndingByteOffset;
ULONG OriginalFileSize;
FileFcb = (PFAT_FCB)FileObject->FsContext;
EndingByteOffset = FileByteOffset + IoLength;
//
// If we've written to the file, then update the last write time. The last
// write time is lazily flushed to the directory entry. We need to acquire
// the volume mutex for exclusive access to synchronize with
// FatxFsdSetInformation and to write out any directory changes below.
//
if (MajorFunction == IRP_MJ_WRITE) {
FatxAcquireVolumeMutexExclusive(VolumeExtension);
if (FatxIsFlagClear(FileFcb->Flags, FAT_FCB_DISABLE_LAST_WRITE_TIME)) {
KeQuerySystemTime(&FileFcb->LastWriteTime);
FileFcb->Flags |= FAT_FCB_UPDATE_DIRECTORY_ENTRY;
}
//
// If the ending byte offset is beyond the current size of the file,
// then we've extended the file. Change the file size to the new size
// and commit the directory change.
//
if (EndingByteOffset > FileFcb->FileSize) {
OriginalFileSize = FileFcb->FileSize;
FileFcb->FileSize = EndingByteOffset;
status = FatxUpdateDirectoryEntry(VolumeExtension, Irp, FileFcb);
if (!NT_SUCCESS(status)) {
FileFcb->FileSize = OriginalFileSize;
FatxReleaseVolumeMutex(VolumeExtension);
return status;
}
}
FatxReleaseVolumeMutex(VolumeExtension);
}
//
// If the file is open for synchronous I/O, then we need to update the
// current file position.
//
if (FatxIsFlagSet(FileObject->Flags, FO_SYNCHRONOUS_IO)) {
FileObject->CurrentByteOffset.LowPart = EndingByteOffset;
FileObject->CurrentByteOffset.HighPart = 0;
}
//
// Fill in the number of bytes of transferred. This number may be less than
// the actual number of modified bytes in the buffer if we're at the end of
// file.
//
Irp->IoStatus.Information = IoLength;
return STATUS_SUCCESS;
}
NTSTATUS
FatxNonCachedSynchronousIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG FileByteOffset,
IN ULONG BufferByteOffset,
IN ULONG IoLength,
IN BOOLEAN PartialTransfer
)
/*++
Routine Description:
This routine is called to synchronously read or write a sector aligned
buffer from or to the target device.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
MajorFunction - Specifies the function to be performed; either IRP_MJ_READ
or IRP_MJ_WRITE.
FileObject - Specifies the file object that the I/O request is for.
FileByteOffset - Specifies the file byte offset to start the transfer from.
BufferByteOffset - Specifies the buffer byte offset ot start the transfer
from.
IoLength - Specifies the number of bytes to transfer.
PartialTransfer - TRUE if called from FatxPartiallyCachedSynchronousIo, else
FALSE.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PFAT_FCB Fcb;
PIO_STACK_LOCATION NextIrpSp;
ULONG OriginalFileByteOffset;
ULONG IoLengthRemaining;
ULONGLONG PhysicalByteOffset;
ULONG PhysicalRunLength;
ULONGLONG StartingPhysicalByteOffset;
ULONG PhysicalIoLength;
ULONGLONG AdjacentPhysicalByteOffset;
KEVENT IoEvent;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
Fcb = (PFAT_FCB)FileObject->FsContext;
NextIrpSp = IoGetNextIrpStackLocation(Irp);
OriginalFileByteOffset = FileByteOffset;
IoLengthRemaining = FatxRoundToSectors(VolumeExtension, IoLength);
ASSERT(IoLengthRemaining > 0);
ASSERT(FatxIsSectorAligned(VolumeExtension, FileByteOffset));
//
// Get the physical byte offset corresponding to the starting byte offset.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension, Irp, Fcb,
FileByteOffset, TRUE, &PhysicalByteOffset, &PhysicalRunLength);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Do the transfer.
//
do {
StartingPhysicalByteOffset = PhysicalByteOffset;
//
// Compute how many physical bytes we can process in this pass. The
// cluster cache doesn't follow adjacent clusters that cross cache pages
// so we handle that logic here by increasing the physical I/O length
// until we find a non-adjacent cluster run. This non-adjacent cluster
// run is the starting cluster for the next iteration of the outer loop.
//
PhysicalIoLength = 0;
do {
//
// Limit the number of bytes in the physical run to the number of
// bytes left to process.
//
if (PhysicalRunLength > IoLengthRemaining) {
PhysicalRunLength = IoLengthRemaining;
IoLengthRemaining = 0;
} else {
IoLengthRemaining -= PhysicalRunLength;
}
//
// Update the number of physical bytes we can process and the offset
// into the file.
//
PhysicalIoLength += PhysicalRunLength;
FileByteOffset += PhysicalRunLength;
if (IoLengthRemaining == 0) {
break;
}
//
// Compute the physical byte offset for the adjacent run.
//
AdjacentPhysicalByteOffset = PhysicalByteOffset + PhysicalRunLength;
//
// Obtain the next physical run.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension,
Irp, Fcb, FileByteOffset, TRUE, &PhysicalByteOffset,
&PhysicalRunLength);
if (!NT_SUCCESS(status)) {
return status;
}
//
// If the next physical byte offset isn't adjacent to the last
// physical run, then we're done.
//
} while (PhysicalByteOffset == AdjacentPhysicalByteOffset);
//
// Invalidate any file system cache buffers for this byte range if this
// is a write operation.
//
if (MajorFunction == IRP_MJ_WRITE) {
FscInvalidateByteRange(&VolumeExtension->CacheExtension,
StartingPhysicalByteOffset, PhysicalIoLength);
}
//
// Initialize (or reinitialize) the synchronization event we'll use to
// block for the port driver to handle the I/O.
//
KeInitializeEvent(&IoEvent, SynchronizationEvent, FALSE);
//
// Fill in the starting physical byte offset and the number of bytes to
// transfer.
//
NextIrpSp->Parameters.Read.ByteOffset.QuadPart =
StartingPhysicalByteOffset;
NextIrpSp->Parameters.Read.Length = PhysicalIoLength;
//
// Fill in the offset into the buffer to start the transfer and update
// that offset by the number of bytes we'll physically transfer.
//
NextIrpSp->Parameters.Read.BufferOffset = BufferByteOffset;
BufferByteOffset += PhysicalIoLength;
//
// Fill in the header for the stack location. This clears out the
// MinorFunction, Control, and Flags field as well.
//
*((PULONG)&NextIrpSp->MajorFunction) = MajorFunction;
//
// Set the completion routine that will signal our synchronization
// event.
//
IoSetCompletionRoutine(Irp, FatxSignalIoEventCompletion, &IoEvent, TRUE,
TRUE, TRUE);
//
// Call down to the target device and block for the I/O to complete.
//
status = IoCallDriver(VolumeExtension->TargetDeviceObject, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&IoEvent, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// Assert that the device handled as many bytes as we programmed it to.
//
ASSERT(Irp->IoStatus.Information == PhysicalIoLength);
} while (IoLengthRemaining > 0);
if (!PartialTransfer) {
status = FatxSynchronousIoTail(VolumeExtension, Irp, MajorFunction,
FileObject, OriginalFileByteOffset, IoLength);
} else {
status = STATUS_SUCCESS;
}
return status;
}
NTSTATUS
FatxNonCachedAsynchronousIoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is called to process the completion of a non-cached
asynchronous I/O transfer.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Context - Specifies the context that was supplied to IoSetCompletionRoutine.
Return Value:
Status of operation.
--*/
{
PFAT_VOLUME_EXTENSION VolumeExtension;
PFAT_ASYNC_IO_DESCRIPTOR AsyncIoDescriptor;
PFAT_FCB FileFcb;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
AsyncIoDescriptor = (PFAT_ASYNC_IO_DESCRIPTOR)Context;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
//
// If there are still more bytes to transfer, then start the entry and
// bail out.
//
if (AsyncIoDescriptor->IoLengthRemaining > 0) {
FatxStartNextAsynchronousIoEntry(VolumeExtension, Irp,
AsyncIoDescriptor);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// If this was a file write, then we need to update the last write time.
//
if (AsyncIoDescriptor->MajorFunction == IRP_MJ_WRITE) {
FileFcb = AsyncIoDescriptor->FileFcb;
if (FatxIsFlagClear(FileFcb->Flags, FAT_FCB_DISABLE_LAST_WRITE_TIME)) {
KeQuerySystemTime(&FileFcb->LastWriteTime);
FileFcb->Flags |= FAT_FCB_UPDATE_DIRECTORY_ENTRY;
}
}
//
// Fill in the number of bytes of transferred. This number may be less
// than the actual number of modified bytes in the buffer if we're at
// the end of file.
//
Irp->IoStatus.Information = AsyncIoDescriptor->IoLength;
}
FatxDecrementDismountBlockCount(VolumeExtension);
FatxDpcReleaseFileMutex(AsyncIoDescriptor->FileFcb);
ExFreePool(AsyncIoDescriptor);
return STATUS_SUCCESS;
}
VOID
FatxStartNextAsynchronousIoEntry(
IN PFAT_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PFAT_ASYNC_IO_DESCRIPTOR AsyncIoDescriptor
)
/*++
Routine Description:
This routine is called to start the next read or write from the supplied
asynchronous I/O descriptor.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
AsyncIoDescriptor - Specifies the asynchronous I/O descriptor that indicates
the next starting physical sector and transfer length.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION NextIrpSp;
PFAT_ASYNC_IO_ENTRY AsyncIoEntry;
ULONG PhysicalIoLength;
NextIrpSp = IoGetNextIrpStackLocation(Irp);
AsyncIoEntry = &AsyncIoDescriptor->Entries[AsyncIoDescriptor->NextAsyncIoEntry++];
PhysicalIoLength = AsyncIoEntry->PhysicalIoLength;
//
// Adjust the number of bytes remaining in the transfer.
//
AsyncIoDescriptor->IoLengthRemaining -= PhysicalIoLength;
//
// Fill in the starting physical byte offset and the number of bytes to
// transfer.
//
NextIrpSp->Parameters.Read.ByteOffset.QuadPart =
(ULONGLONG)AsyncIoEntry->PhysicalSector << VolumeExtension->SectorShift;
NextIrpSp->Parameters.Read.Length = PhysicalIoLength;
//
// Fill in the offset into the buffer to start the transfer and update
// that offset by the number of bytes we'll physically transfer.
//
NextIrpSp->Parameters.Read.BufferOffset = AsyncIoDescriptor->BufferOffset;
AsyncIoDescriptor->BufferOffset += PhysicalIoLength;
//
// Fill in the header for the stack location. This clears out the
// MinorFunction, Control, and Flags field as well.
//
*((PULONG)&NextIrpSp->MajorFunction) = AsyncIoDescriptor->MajorFunction;
//
// Set the completion routine that will start the next entry or finish the
// transfer.
//
IoSetCompletionRoutine(Irp, FatxNonCachedAsynchronousIoCompletion,
AsyncIoDescriptor, TRUE, TRUE, TRUE);
//
// Call down to the target device.
//
IoCallDriver(VolumeExtension->TargetDeviceObject, Irp);
}
NTSTATUS
FatxNonCachedAsynchronousIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG FileByteOffset,
IN ULONG IoLength
)
/*++
Routine Description:
This routine is called to asynchronously read or write a sector aligned
buffer from or to the target device.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
MajorFunction - Specifies the function to be performed; either IRP_MJ_READ
or IRP_MJ_WRITE.
FileObject - Specifies the file object that the I/O request is for.
FileByteOffset - Specifies the file byte offset to start the transfer from.
IoLength - Specifies the number of bytes to transfer.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PFAT_FCB Fcb;
ULONG IoLengthRemaining;
SIZE_T AsyncIoDescriptorSize;
PFAT_ASYNC_IO_DESCRIPTOR AsyncIoDescriptor;
ULONGLONG PhysicalByteOffset;
ULONG PhysicalRunLength;
PFAT_ASYNC_IO_ENTRY AsyncIoEntry;
ULONGLONG StartingPhysicalByteOffset;
ULONG PhysicalIoLength;
ULONGLONG AdjacentPhysicalByteOffset;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
Fcb = (PFAT_FCB)FileObject->FsContext;
IoLengthRemaining = FatxRoundToSectors(VolumeExtension, IoLength);
ASSERT(IoLengthRemaining > 0);
ASSERT(FatxIsSectorAligned(VolumeExtension, FileByteOffset));
//
// Always assume the worst case for the state of the file. If the file is
// highly fragmented, then we'll need one entry per cluster. If the file
// isn't fragmented, then we'll end up wasting memory, but the application
// will always be charged a consistent number of bytes per transfer.
//
// #####: bug fix
// If the area to write wraps a cluster, allocate space for one more
// entry. XDKs starting with 4627 patch this bug in older kernels.
// They patch by always adding 8 extra (sizeof(FAT_ASYNC_IO_ENTRY)).
// Note the 2 below - that used to be a 1.
//
AsyncIoDescriptorSize = sizeof(FAT_ASYNC_IO_DESCRIPTOR) +
((IoLengthRemaining >> VolumeExtension->ClusterShift) + 2) *
sizeof(FAT_ASYNC_IO_ENTRY);
AsyncIoDescriptor = ExAllocatePoolWithTag(AsyncIoDescriptorSize, 'dAtF');
if (AsyncIoDescriptor == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the asynchronous I/O descriptor.
//
AsyncIoDescriptor->MajorFunction = MajorFunction;
AsyncIoDescriptor->IoLength = IoLength;
AsyncIoDescriptor->IoLengthRemaining = IoLengthRemaining;
AsyncIoDescriptor->BufferOffset = 0;
AsyncIoDescriptor->NextAsyncIoEntry = 0;
AsyncIoDescriptor->FileFcb = Fcb;
//
// Get the physical byte offset corresponding to the starting byte offset.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension, Irp, Fcb,
FileByteOffset, TRUE, &PhysicalByteOffset, &PhysicalRunLength);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Fill in the entries of the asynchronous I/O descriptor.
//
AsyncIoEntry = AsyncIoDescriptor->Entries;
do {
StartingPhysicalByteOffset = PhysicalByteOffset;
//
// Compute how many physical bytes we can process in this pass. The
// cluster cache doesn't follow adjacent clusters that cross cache pages
// so we handle that logic here by increasing the physical I/O length
// until we find a non-adjacent cluster run. This non-adjacent cluster
// run is the starting cluster for the next iteration of the outer loop.
//
PhysicalIoLength = 0;
do {
//
// Limit the number of bytes in the physical run to the number of
// bytes left to process.
//
if (PhysicalRunLength > IoLengthRemaining) {
PhysicalRunLength = IoLengthRemaining;
IoLengthRemaining = 0;
} else {
IoLengthRemaining -= PhysicalRunLength;
}
//
// Update the number of physical bytes we can process and the offset
// into the file.
//
PhysicalIoLength += PhysicalRunLength;
FileByteOffset += PhysicalRunLength;
if (IoLengthRemaining == 0) {
break;
}
//
// Compute the physical byte offset for the adjacent run.
//
AdjacentPhysicalByteOffset = PhysicalByteOffset + PhysicalRunLength;
//
// Obtain the next physical run.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension,
Irp, Fcb, FileByteOffset, TRUE, &PhysicalByteOffset,
&PhysicalRunLength);
if (!NT_SUCCESS(status)) {
ExFreePool(AsyncIoDescriptor);
return status;
}
//
// If the next physical byte offset isn't adjacent to the last
// physical run, then we're done.
//
} while (PhysicalByteOffset == AdjacentPhysicalByteOffset);
ASSERT(FatxIsSectorAligned(VolumeExtension, PhysicalIoLength));
//
// Invalidate any file system cache buffers for this byte range if this
// is a write operation.
//
if (MajorFunction == IRP_MJ_WRITE) {
FscInvalidateByteRange(&VolumeExtension->CacheExtension,
StartingPhysicalByteOffset, PhysicalIoLength);
}
//
// Fill in the starting physical byte offset and the number of bytes to
// transfer.
//
AsyncIoEntry->PhysicalSector = (ULONG)(StartingPhysicalByteOffset >>
VolumeExtension->SectorShift);
AsyncIoEntry->PhysicalIoLength = PhysicalIoLength;
AsyncIoEntry++;
} while (IoLengthRemaining > 0);
//
// Start transferring the first entry in the asynchronous I/O descriptor.
//
IoMarkIrpPending(Irp);
FatxStartNextAsynchronousIoEntry(VolumeExtension, Irp,
AsyncIoDescriptor);
return STATUS_PENDING;
}
NTSTATUS
FatxFullyCachedSynchronousIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG FileByteOffset,
IN ULONG BufferByteOffset,
IN ULONG IoLength,
IN BOOLEAN PartialTransfer
)
/*++
Routine Description:
This routine is called to synchronously read or write a buffer through the
file system cache.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
MajorFunction - Specifies the function to be performed; either IRP_MJ_READ
or IRP_MJ_WRITE.
FileObject - Specifies the file object that the I/O request is for.
FileByteOffset - Specifies the file byte offset to start the transfer from.
BufferByteOffset - Specifies the buffer byte offset ot start the transfer
from.
IoLength - Specifies the number of bytes to transfer.
PartialTransfer - TRUE if called from FatxPartiallyCachedSynchronousIo, else
FALSE.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PFAT_FCB Fcb;
ULONG OriginalFileByteOffset;
ULONG IoLengthRemaining;
PVOID UserBuffer;
ULONGLONG PhysicalByteOffset;
ULONG PhysicalRunLength;
ULONG PhysicalRunBytesRemaining;
PVOID CacheBuffer;
ULONG BytesToCopy;
BOOLEAN MapEmptyBuffer;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
Fcb = (PFAT_FCB)FileObject->FsContext;
OriginalFileByteOffset = FileByteOffset;
IoLengthRemaining = IoLength;
UserBuffer = (PUCHAR)Irp->UserBuffer + BufferByteOffset;
ASSERT(IoLengthRemaining > 0);
//
// Get the physical byte offset corresponding to the starting byte offset.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension, Irp, Fcb,
FileByteOffset, TRUE, &PhysicalByteOffset, &PhysicalRunLength);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Do the transfer.
//
for (;;) {
PhysicalRunBytesRemaining = PhysicalRunLength;
do {
//
// Compute the number of bytes remaining on this cache buffer.
//
BytesToCopy = PAGE_SIZE - BYTE_OFFSET(PhysicalByteOffset);
//
// Adjust the number of bytes remaining in this physical run and the
// next cache byte offset depending on whether we're near the end of
// the run or not.
//
if (BytesToCopy < PhysicalRunBytesRemaining) {
PhysicalRunBytesRemaining -= BytesToCopy;
} else {
BytesToCopy = PhysicalRunBytesRemaining;
PhysicalRunBytesRemaining = 0;
}
//
// Limit the number of bytes copied to the number of bytes we
// actually need.
//
if (BytesToCopy > IoLengthRemaining) {
BytesToCopy = IoLengthRemaining;
}
if (MajorFunction == IRP_MJ_WRITE) {
//
// If we're going to be writing out an entire page or if we're
// going to be writing to the first byte of the last page of the
// file, then we can map in an empty cache page.
//
if (BytesToCopy == PAGE_SIZE) {
MapEmptyBuffer = TRUE;
} else if ((BYTE_OFFSET(FileByteOffset) == 0) &&
(FileByteOffset + IoLengthRemaining >= Fcb->FileSize)) {
MapEmptyBuffer = TRUE;
} else {
MapEmptyBuffer = FALSE;
}
//
// Map in the next page of the physical run.
//
if (MapEmptyBuffer) {
status = FscMapEmptyBuffer(&VolumeExtension->CacheExtension,
PhysicalByteOffset, &CacheBuffer);
} else {
status = FscMapBuffer(&VolumeExtension->CacheExtension, Irp,
PhysicalByteOffset, TRUE, &CacheBuffer);
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// Write to the cache buffer and unmap the cache buffer.
//
RtlCopyMemory(CacheBuffer, UserBuffer, BytesToCopy);
status = FscWriteBuffer(&VolumeExtension->CacheExtension, Irp,
PhysicalByteOffset, BytesToCopy, CacheBuffer);
if (!NT_SUCCESS(status)) {
return status;
}
} else {
//
// Map in the next page of the physical run.
//
status = FscMapBuffer(&VolumeExtension->CacheExtension, Irp,
PhysicalByteOffset, FALSE, &CacheBuffer);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Read from the cache buffer and unmap the cache buffer.
//
RtlCopyMemory(UserBuffer, CacheBuffer, BytesToCopy);
FscUnmapBuffer(CacheBuffer);
}
//
// Adjust the number of bytes remaining and check if we're through
// with the transfer.
//
PhysicalByteOffset += BytesToCopy;
IoLengthRemaining -= BytesToCopy;
if (IoLengthRemaining == 0) {
if (!PartialTransfer) {
status = FatxSynchronousIoTail(VolumeExtension, Irp,
MajorFunction, FileObject, OriginalFileByteOffset,
IoLength);
} else {
status = STATUS_SUCCESS;
}
return status;
}
//
// There's still more bytes to transfer. Update the other loop
// variables and continue transfering this physical run.
//
FileByteOffset += BytesToCopy;
UserBuffer = (PUCHAR)UserBuffer + BytesToCopy;
} while (PhysicalRunBytesRemaining > 0);
//
// Obtain the next physical run.
//
status = FatxFileByteOffsetToPhysicalByteOffset(VolumeExtension, Irp,
Fcb, FileByteOffset, TRUE, &PhysicalByteOffset, &PhysicalRunLength);
if (!NT_SUCCESS(status)) {
return status;
}
}
}
NTSTATUS
FatxPartiallyCachedSynchronousIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG FileByteOffset,
IN ULONG IoLength,
IN BOOLEAN NonCachedEndOfFileTransfer
)
/*++
Routine Description:
This routine is called to synchronously read or write a buffer partially
through the file system cache and partially through direct device I/O. This
routine is intended for large I/O transfers where we want to maximize use of
direct device I/O in order to avoid PAGE_SIZE I/O transfers.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
MajorFunction - Specifies the function to be performed; either IRP_MJ_READ
or IRP_MJ_WRITE.
FileObject - Specifies the file object that the I/O request is for.
FileByteOffset - Specifies the file byte offset to start the transfer from.
IoLength - Specifies the number of bytes to transfer.
NonCachedEndOfFileTransfer - Specifies TRUE if the read is to the end of
file and the output buffer is large enough to hold the transfer length
rounded up to a sector boundary.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
ULONG OriginalFileByteOffset;
ULONG OriginalIoLength;
ULONG PartialIoLength;
ULONG BufferByteOffset;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
OriginalFileByteOffset = FileByteOffset;
OriginalIoLength = IoLength;
//
// The below code assumes that the relative byte offsets into the file and
// the file area have the same page alignment as their corresponding
// physical byte offsets.
//
ASSERT(BYTE_OFFSET(VolumeExtension->FileAreaByteOffset) == 0);
//
// Transfer the head of the request if it's not page aligned.
//
if (BYTE_OFFSET(FileByteOffset) != 0) {
PartialIoLength = PAGE_SIZE - BYTE_OFFSET(FileByteOffset);
ASSERT(PartialIoLength < IoLength);
status = FatxFullyCachedSynchronousIo(DeviceObject, Irp, MajorFunction,
FileObject, FileByteOffset, 0, PartialIoLength, TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
IoLength -= PartialIoLength;
FileByteOffset += PartialIoLength;
BufferByteOffset = PartialIoLength;
} else {
BufferByteOffset = 0;
}
//
// If NonCachedEndOfFileTransfer is TRUE, then we're reading to the end of
// the file and the transfer buffer is large enough to hold the transfer
// length rounded up to a sector boundary.
//
if (NonCachedEndOfFileTransfer) {
ASSERT(MajorFunction == IRP_MJ_READ);
IoLength = FatxRoundToSectors(VolumeExtension, IoLength);
PartialIoLength = IoLength;
} else {
ASSERT(IoLength >= PAGE_SIZE);
PartialIoLength = (ULONG)PAGE_ALIGN(IoLength);
}
status = FatxNonCachedSynchronousIo(DeviceObject, Irp, MajorFunction,
FileObject, FileByteOffset, BufferByteOffset, PartialIoLength, TRUE);
//
// Transfer the remaining non whole page of the request if necessary.
//
if (NT_SUCCESS(status)) {
IoLength -= PartialIoLength;
if (IoLength > 0) {
FileByteOffset += PartialIoLength;
BufferByteOffset += PartialIoLength;
status = FatxFullyCachedSynchronousIo(DeviceObject, Irp,
MajorFunction, FileObject, FileByteOffset, BufferByteOffset,
IoLength, TRUE);
}
}
if (NT_SUCCESS(status)) {
status = FatxSynchronousIoTail(VolumeExtension, Irp, MajorFunction,
FileObject, OriginalFileByteOffset, OriginalIoLength);
}
return status;
}
NTSTATUS
FatxFsdReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O manager to handle IRP_MJ_READ and
IRP_MJ_WRITE requests.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
PIO_STACK_LOCATION NextIrpSp;
UCHAR MajorFunction;
PFILE_OBJECT FileObject;
PFAT_FCB Fcb;
ULONG IoLength;
BOOLEAN NonCachedEndOfFileTransfer;
ULONGLONG PartitionBytesRemaining;
ULONG FileBytesRemaining;
BOOLEAN SynchronousIo;
ULONG EndingByteOffset;
ULONG ClusterNumber;
VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
NextIrpSp = IoGetNextIrpStackLocation(Irp);
MajorFunction = IrpSp->MajorFunction;
FileObject = IrpSp->FileObject;
Fcb = (PFAT_FCB)FileObject->FsContext;
IoLength = IrpSp->Parameters.Read.Length;
NonCachedEndOfFileTransfer = FALSE;
//
// Ensure that the file object is not for a directory.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_DIRECTORY)) {
status = STATUS_INVALID_DEVICE_REQUEST;
goto CompleteAndExit;
}
//
// If this is a zero length request, then we can complete the IRP now.
//
if (IoLength == 0) {
Irp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
goto CompleteAndExit;
}
//
// Check if we're reading from or writing to the volume file control block.
//
if (FatxIsFlagSet(Fcb->Flags, FAT_FCB_VOLUME)) {
if (MajorFunction == IRP_MJ_READ) {
FatxAcquireVolumeMutexShared(VolumeExtension);
} else {
FatxAcquireVolumeMutexExclusive(VolumeExtension);
}
//
// Check if the volume has been dismounted.
//
if (FatxIsFlagSet(VolumeExtension->Flags, FAT_VOLUME_DISMOUNTED)) {
status = STATUS_VOLUME_DISMOUNTED;
goto UnlockVolumeAndExit;
}
//
// Check if the starting offset is beyond the end of the partition.
//
if ((ULONGLONG)IrpSp->Parameters.Read.ByteOffset.QuadPart >=
(ULONGLONG)VolumeExtension->PartitionLength.QuadPart) {
status = STATUS_END_OF_FILE;
goto UnlockVolumeAndExit;
}
//
// If the number of bytes to transfer is greater than the number of
// bytes remaining in the partition, then truncate the number of bytes
// we'll actually transfer.
//
PartitionBytesRemaining =
(ULONGLONG)VolumeExtension->PartitionLength.QuadPart -
(ULONGLONG)IrpSp->Parameters.Read.ByteOffset.QuadPart;
if ((ULONGLONG)IoLength >= PartitionBytesRemaining) {
IoLength = (ULONG)PartitionBytesRemaining;
}
ASSERT(IoLength > 0);
//
// We'll leave it to the target device's driver to validate that the
// user's buffer, the starting byte offset, and the transfer length are
// all properly aligned.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// Lock the user's buffer into memory if necessary.
//
IoLockUserBuffer(Irp, IrpSp->Parameters.Read.Length);
//
// Invalidate any file system cache buffers for this byte range if this
// is a write operation.
//
if (MajorFunction == IRP_MJ_WRITE) {
FscInvalidateByteRange(&VolumeExtension->CacheExtension,
IrpSp->Parameters.Read.ByteOffset.QuadPart, IoLength);
}
//
// Limit the number of bytes physically read to the end of the volume.
//
NextIrpSp->Parameters.Read.Length = IoLength;
//
// Set a completion routine to unlock the volume mutex and update any
// state in the file control block.
//
IoSetCompletionRoutine(Irp, FatxVolumeIoCompletion, NULL, TRUE, TRUE,
TRUE);
//
// Call down to the target device.
//
status = IoCallDriver(VolumeExtension->TargetDeviceObject, Irp);
//
// Leave the critical region that we acquired when we took the volume
// mutex.
//
KeLeaveCriticalRegion();
return status;
UnlockVolumeAndExit:
FatxReleaseVolumeMutex(VolumeExtension);
goto CompleteAndExit;
}
//
// Otherwise, we're reading from or writing to a standard file.
//
if (MajorFunction == IRP_MJ_READ) {
FatxAcquireFileMutexShared(Fcb);
} else {
FatxAcquireFileMutexExclusive(Fcb);
}
//
// Increment the dismount unblock count for the volume. We won't be
// holding the volume's mutex throughout the processing of this request,
// so FatxDismountVolume needs to have some synchronization mechanism to
// know when all pending read/write IRPs have completed.
//
// If a request enters the file system after a dismount has been
// unblocked or completed, then the dismount flag will have been set for
// the volume and the code below will fail the request.
//
// We don't need to do this for volume file control block because in
// that code path, the volume's mutex is held for the entire operation.
//
FatxIncrementDismountBlockCount(VolumeExtension);
//
// Check if the volume has been dismounted.
//
if (FatxIsFlagSet(VolumeExtension->Flags, FAT_VOLUME_DISMOUNTED)) {
status = STATUS_VOLUME_DISMOUNTED;
goto UnlockFileAndExit;
}
//
// Check if the file object has already been cleaned up. We don't allow a
// a file object to be modified after its handle has been closed.
//
if (FatxIsFlagSet(FileObject->Flags, FO_CLEANUP_COMPLETE)) {
status = STATUS_FILE_CLOSED;
goto UnlockFileAndExit;
}
if (MajorFunction == IRP_MJ_READ) {
//
// Check if the starting offset is beyond the end of file.
//
if ((IrpSp->Parameters.Read.ByteOffset.HighPart != 0) ||
(IrpSp->Parameters.Read.ByteOffset.LowPart >= Fcb->FileSize)) {
status = STATUS_END_OF_FILE;
goto UnlockFileAndExit;
}
//
// If the number of bytes to read is greater than the number of bytes
// remaining in the file, then truncate the number of bytes we'll
// actually read.
//
FileBytesRemaining = Fcb->FileSize -
IrpSp->Parameters.Read.ByteOffset.LowPart;
if (IoLength >= FileBytesRemaining) {
//
// If the user's buffer is large enough to hold the logical read
// length rounded up to a sector boundary, then set a flag so that
// the below code will potentially read this part of the file as
// non cached.
//
if (IoLength >= (FatxRoundToSectors(VolumeExtension, Fcb->FileSize) -
IrpSp->Parameters.Read.ByteOffset.LowPart)) {
NonCachedEndOfFileTransfer = TRUE;
}
IoLength = FileBytesRemaining;
}
ASSERT(IoLength > 0);
//
// Check if we should do synchronous or asynchronous I/O depending on
// how the file object was originally opened.
//
SynchronousIo = (BOOLEAN)FatxIsFlagSet(FileObject->Flags,
FO_SYNCHRONOUS_IO);
} else {
//
// If we're supposed to write to the end of the file, then change the
// starting byte offset to the current end of file.
//
if ((IrpSp->Parameters.Read.ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) &&
(IrpSp->Parameters.Read.ByteOffset.HighPart == -1)) {
IrpSp->Parameters.Read.ByteOffset.LowPart = Fcb->FileSize;
IrpSp->Parameters.Read.ByteOffset.HighPart = 0;
}
//
// Verify that the starting or ending offset are only 32-bits.
//
EndingByteOffset = IrpSp->Parameters.Read.ByteOffset.LowPart + IoLength;
if ((IrpSp->Parameters.Read.ByteOffset.HighPart != 0) ||
(EndingByteOffset <= IrpSp->Parameters.Read.ByteOffset.LowPart)) {
status = STATUS_DISK_FULL;
goto UnlockFileAndExit;
}
//
// Determine the number of bytes currently allocated to the file.
//
if (Fcb->AllocationSize == MAXULONG) {
//
// Attempt to find the cluster corresponding to the maximum byte
// offset which will have the side effect of filling in the number
// of bytes allocated.
//
FatxAcquireVolumeMutexShared(VolumeExtension);
status = FatxFileByteOffsetToCluster(VolumeExtension, Irp, Fcb,
MAXULONG, &ClusterNumber, NULL);
FatxReleaseVolumeMutex(VolumeExtension);
if (!NT_SUCCESS(status) && (status != STATUS_END_OF_FILE)) {
goto UnlockFileAndExit;
}
}
//
// If the file size is greater than the number of bytes allocated to the
// file, then the file is corrupt and we won't allow any writes to it.
//
if (Fcb->FileSize > Fcb->AllocationSize) {
status = STATUS_FILE_CORRUPT_ERROR;
goto UnlockFileAndExit;
}
//
// If the ending byte offset is greater than the number of bytes
// allocated to the file, then we need to grow the allocation size for
// the file. The volume mutex must be acquired exclusively so that the
// volume can be modified.
//
if (EndingByteOffset > Fcb->AllocationSize) {
FatxAcquireVolumeMutexExclusive(VolumeExtension);
status = FatxExtendFileAllocation(VolumeExtension, Irp, Fcb,
FatxRoundToClusters(VolumeExtension, EndingByteOffset));
FatxReleaseVolumeMutex(VolumeExtension);
if (!NT_SUCCESS(status)) {
goto UnlockFileAndExit;
}
}
//
// Check if we should do synchronous or asynchronous I/O depending on
// how the file object was originally opened.
//
// If this is an asynchronous I/O operation, verify that the ending byte
// offset is before the end of file. If it isn't, then we can't change
// the directory entry's file size asynchronously, so switch this back
// to a synchronous I/O operation.
//
SynchronousIo = (BOOLEAN)FatxIsFlagSet(FileObject->Flags,
FO_SYNCHRONOUS_IO);
if (!SynchronousIo && (EndingByteOffset > Fcb->FileSize)) {
SynchronousIo = TRUE;
}
}
//
// The upper 32-bits of the byte offset should be zero at this point. Code
// below this point will only look at ByteOffset.LowPart.
//
ASSERT(IrpSp->Parameters.Read.ByteOffset.HighPart == 0);
//
// Check if we're supposed to bypass the file system cache.
//
if (FatxIsFlagSet(Irp->Flags, IRP_NOCACHE)) {
//
// Verify that the starting byte offset and transfer length are sector
// aligned. We'll leave it to the target device's driver to validate
// that the user's buffer is properly aligned.
//
if (!FatxIsSectorAligned(VolumeExtension, IrpSp->Parameters.Read.ByteOffset.LowPart) ||
!FatxIsSectorAligned(VolumeExtension, IrpSp->Parameters.Read.Length)) {
status = STATUS_INVALID_PARAMETER;
goto UnlockFileAndExit;
}
//
// Lock the user's buffer into memory if necessary.
//
IoLockUserBuffer(Irp, IrpSp->Parameters.Read.Length);
//
// Do the transfer depending on whether we can block waiting for the
// I/O to complete or not.
//
if (SynchronousIo) {
status = FatxNonCachedSynchronousIo(DeviceObject, Irp, MajorFunction,
FileObject, IrpSp->Parameters.Read.ByteOffset.LowPart, 0,
IoLength, FALSE);
} else {
status = FatxNonCachedAsynchronousIo(DeviceObject, Irp, MajorFunction,
FileObject, IrpSp->Parameters.Read.ByteOffset.LowPart, IoLength);
if (status == STATUS_PENDING) {
//
// Leave the critical region that we acquired when we took the
// file mutex.
//
KeLeaveCriticalRegion();
return status;
}
}
} else {
//
// Scatter/gather operations are always non-cached.
//
ASSERT(FatxIsFlagClear(Irp->Flags, IRP_SCATTER_GATHER_OPERATION));
//
// Otherwise, handle reading the data using the file cache.
//
// Check if this transfer should use the fully or partially cached path.
//
if (FscTestForFullyCachedIo(Irp, IrpSp->Parameters.Read.ByteOffset.LowPart,
IoLength, NonCachedEndOfFileTransfer)) {
status = FatxFullyCachedSynchronousIo(DeviceObject, Irp,
MajorFunction, FileObject,
IrpSp->Parameters.Read.ByteOffset.LowPart, 0, IoLength, FALSE);
} else {
//
// Lock the user's buffer into memory if necessary.
//
IoLockUserBuffer(Irp, IrpSp->Parameters.Read.Length);
status = FatxPartiallyCachedSynchronousIo(DeviceObject, Irp,
MajorFunction, FileObject,
IrpSp->Parameters.Read.ByteOffset.LowPart, IoLength,
NonCachedEndOfFileTransfer);
}
}
ASSERT(status != STATUS_PENDING);
UnlockFileAndExit:
FatxDecrementDismountBlockCount(VolumeExtension);
FatxReleaseFileMutex(Fcb);
CompleteAndExit:
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}