reactos/drivers/filesystems/vfatfs/rw.c
Hervé Poussineau 14c3936251 [VFATFS] Add it back to build on XBOX, with the name vfatfs.sys
Adapt some comments to new name.

CORE-16373
2022-09-24 12:12:33 +02:00

1233 lines
36 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystems/vfatfs/rw.c
* PURPOSE: VFAT Filesystem
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
* Pierre Schweitzer (pierre@reactos.org)
*
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/*
* Uncomment to enable strict verification of cluster/offset pair
* caching. If this option is enabled you lose all the benefits of
* the caching and the read/write operations will actually be
* slower. It's meant only for debugging!!!
* - Filip Navara, 26/07/2004
*/
/* #define DEBUG_VERIFY_OFFSET_CACHING */
/* Arbitrary, taken from MS FastFAT, should be
* refined given what we experience in common
* out of stack operations
*/
#define OVERFLOW_READ_THRESHHOLD 0xE00
/* FUNCTIONS *****************************************************************/
/*
* Return the next cluster in a FAT chain, possibly extending the chain if
* necessary
*/
NTSTATUS
NextCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG FirstCluster,
PULONG CurrentCluster,
BOOLEAN Extend)
{
if (FirstCluster == 1)
{
(*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
return STATUS_SUCCESS;
}
else
{
if (Extend)
return GetNextClusterExtend(DeviceExt, (*CurrentCluster), CurrentCluster);
else
return GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster);
}
}
NTSTATUS
OffsetToCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG FirstCluster,
ULONG FileOffset,
PULONG Cluster,
BOOLEAN Extend)
{
ULONG CurrentCluster;
ULONG i;
NTSTATUS Status;
/*
DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
" FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
Fcb, FirstCluster, FileOffset, Cluster, Extend);
*/
if (FirstCluster == 0)
{
DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
ASSERT(FALSE);
}
if (FirstCluster == 1)
{
/* root of FAT16 or FAT12 */
*Cluster = DeviceExt->FatInfo.rootStart + FileOffset
/ (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
return STATUS_SUCCESS;
}
else
{
CurrentCluster = FirstCluster;
if (Extend)
{
for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
{
Status = GetNextClusterExtend (DeviceExt, CurrentCluster, &CurrentCluster);
if (!NT_SUCCESS(Status))
return Status;
}
*Cluster = CurrentCluster;
}
else
{
for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
{
Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster);
if (!NT_SUCCESS(Status))
return Status;
}
*Cluster = CurrentCluster;
}
return STATUS_SUCCESS;
}
}
/*
* FUNCTION: Reads data from a file
*/
static
NTSTATUS
VfatReadFileData(
PVFAT_IRP_CONTEXT IrpContext,
ULONG Length,
LARGE_INTEGER ReadOffset,
PULONG LengthRead)
{
ULONG CurrentCluster;
ULONG FirstCluster;
ULONG StartCluster;
ULONG ClusterCount;
LARGE_INTEGER StartOffset;
PDEVICE_EXTENSION DeviceExt;
BOOLEAN First = TRUE;
PVFATFCB Fcb;
NTSTATUS Status;
ULONG BytesDone;
ULONG BytesPerSector;
ULONG BytesPerCluster;
ULONG LastCluster;
ULONG LastOffset;
/* PRECONDITION */
ASSERT(IrpContext);
DeviceExt = IrpContext->DeviceExt;
ASSERT(DeviceExt);
ASSERT(DeviceExt->FatInfo.BytesPerCluster);
ASSERT(IrpContext->FileObject);
ASSERT(IrpContext->FileObject->FsContext2 != NULL);
DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
"Length %u, ReadOffset 0x%I64x)\n", DeviceExt,
IrpContext->FileObject, Length, ReadOffset.QuadPart);
*LengthRead = 0;
Fcb = IrpContext->FileObject->FsContext;
BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
ASSERT(ReadOffset.QuadPart + Length <= ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
ASSERT(ReadOffset.u.LowPart % BytesPerSector == 0);
ASSERT(Length % BytesPerSector == 0);
/* Is this a read of the FAT? */
if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
{
ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
if (NT_SUCCESS(Status))
{
*LengthRead = Length;
}
else
{
DPRINT1("FAT reading failed, Status %x\n", Status);
}
return Status;
}
/* Is this a read of the Volume ? */
if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
{
Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
if (NT_SUCCESS(Status))
{
*LengthRead = Length;
}
else
{
DPRINT1("Volume reading failed, Status %x\n", Status);
}
return Status;
}
/* Find the first cluster */
FirstCluster =
CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
if (FirstCluster == 1)
{
/* Directory of FAT12/16 needs a special handling */
if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
{
Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
}
ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
/* Fire up the read command */
Status = VfatReadDiskPartial (IrpContext, &ReadOffset, Length, 0, TRUE);
if (NT_SUCCESS(Status))
{
*LengthRead = Length;
}
return Status;
}
ExAcquireFastMutex(&Fcb->LastMutex);
LastCluster = Fcb->LastCluster;
LastOffset = Fcb->LastOffset;
ExReleaseFastMutex(&Fcb->LastMutex);
/* Find the cluster to start the read from */
if (LastCluster > 0 && ReadOffset.u.LowPart >= LastOffset)
{
Status = OffsetToCluster(DeviceExt, LastCluster,
ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) -
LastOffset,
&CurrentCluster, FALSE);
#ifdef DEBUG_VERIFY_OFFSET_CACHING
/* DEBUG VERIFICATION */
{
ULONG CorrectCluster;
OffsetToCluster(DeviceExt, FirstCluster,
ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
&CorrectCluster, FALSE);
if (CorrectCluster != CurrentCluster)
KeBugCheck(FAT_FILE_SYSTEM);
}
#endif
}
else
{
Status = OffsetToCluster(DeviceExt, FirstCluster,
ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
&CurrentCluster, FALSE);
}
if (!NT_SUCCESS(Status))
{
return Status;
}
ExAcquireFastMutex(&Fcb->LastMutex);
Fcb->LastCluster = CurrentCluster;
Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
ExReleaseFastMutex(&Fcb->LastMutex);
KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
IrpContext->RefCount = 1;
while (Length > 0 && CurrentCluster != 0xffffffff)
{
StartCluster = CurrentCluster;
StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
BytesDone = 0;
ClusterCount = 0;
do
{
ClusterCount++;
if (First)
{
BytesDone = min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
First = FALSE;
}
else
{
if (Length - BytesDone > BytesPerCluster)
{
BytesDone += BytesPerCluster;
}
else
{
BytesDone = Length;
}
}
Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
}
while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
DPRINT("start %08x, next %08x, count %u\n",
StartCluster, CurrentCluster, ClusterCount);
ExAcquireFastMutex(&Fcb->LastMutex);
Fcb->LastCluster = StartCluster + (ClusterCount - 1);
Fcb->LastOffset = ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
ExReleaseFastMutex(&Fcb->LastMutex);
/* Fire up the read command */
Status = VfatReadDiskPartial (IrpContext, &StartOffset, BytesDone, *LengthRead, FALSE);
if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
{
break;
}
*LengthRead += BytesDone;
Length -= BytesDone;
ReadOffset.u.LowPart += BytesDone;
}
if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
{
KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
}
if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
{
if (Length > 0)
{
Status = STATUS_UNSUCCESSFUL;
}
else
{
Status = IrpContext->Irp->IoStatus.Status;
}
}
return Status;
}
static
NTSTATUS
VfatWriteFileData(
PVFAT_IRP_CONTEXT IrpContext,
ULONG Length,
LARGE_INTEGER WriteOffset)
{
PDEVICE_EXTENSION DeviceExt;
PVFATFCB Fcb;
ULONG Count;
ULONG FirstCluster;
ULONG CurrentCluster;
ULONG BytesDone;
ULONG StartCluster;
ULONG ClusterCount;
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN First = TRUE;
ULONG BytesPerSector;
ULONG BytesPerCluster;
LARGE_INTEGER StartOffset;
ULONG BufferOffset;
ULONG LastCluster;
ULONG LastOffset;
/* PRECONDITION */
ASSERT(IrpContext);
DeviceExt = IrpContext->DeviceExt;
ASSERT(DeviceExt);
ASSERT(DeviceExt->FatInfo.BytesPerCluster);
ASSERT(IrpContext->FileObject);
ASSERT(IrpContext->FileObject->FsContext2 != NULL);
Fcb = IrpContext->FileObject->FsContext;
BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
"Length %u, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
IrpContext->FileObject, Length, WriteOffset.QuadPart,
&Fcb->PathNameU);
ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
ASSERT(Length % BytesPerSector == 0);
/* Is this a write of the volume? */
if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
{
Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Volume writing failed, Status %x\n", Status);
}
return Status;
}
/* Is this a write to the FAT? */
if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
{
WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
IrpContext->RefCount = 1;
for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
{
Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
{
DPRINT1("FAT writing failed, Status %x\n", Status);
break;
}
WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
}
if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
{
KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
}
if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
{
Status = IrpContext->Irp->IoStatus.Status;
}
return Status;
}
/*
* Find the first cluster
*/
FirstCluster =
CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
if (FirstCluster == 1)
{
ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
// Directory of FAT12/16 needs a special handling
WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
// Fire up the write command
Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
return Status;
}
ExAcquireFastMutex(&Fcb->LastMutex);
LastCluster = Fcb->LastCluster;
LastOffset = Fcb->LastOffset;
ExReleaseFastMutex(&Fcb->LastMutex);
/*
* Find the cluster to start the write from
*/
if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
{
Status = OffsetToCluster(DeviceExt, LastCluster,
ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
LastOffset,
&CurrentCluster, FALSE);
#ifdef DEBUG_VERIFY_OFFSET_CACHING
/* DEBUG VERIFICATION */
{
ULONG CorrectCluster;
OffsetToCluster(DeviceExt, FirstCluster,
ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
&CorrectCluster, FALSE);
if (CorrectCluster != CurrentCluster)
KeBugCheck(FAT_FILE_SYSTEM);
}
#endif
}
else
{
Status = OffsetToCluster(DeviceExt, FirstCluster,
ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
&CurrentCluster, FALSE);
}
if (!NT_SUCCESS(Status))
{
return Status;
}
ExAcquireFastMutex(&Fcb->LastMutex);
Fcb->LastCluster = CurrentCluster;
Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
ExReleaseFastMutex(&Fcb->LastMutex);
IrpContext->RefCount = 1;
BufferOffset = 0;
while (Length > 0 && CurrentCluster != 0xffffffff)
{
StartCluster = CurrentCluster;
StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
BytesDone = 0;
ClusterCount = 0;
do
{
ClusterCount++;
if (First)
{
BytesDone = min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
First = FALSE;
}
else
{
if (Length - BytesDone > BytesPerCluster)
{
BytesDone += BytesPerCluster;
}
else
{
BytesDone = Length;
}
}
Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
}
while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
DPRINT("start %08x, next %08x, count %u\n",
StartCluster, CurrentCluster, ClusterCount);
ExAcquireFastMutex(&Fcb->LastMutex);
Fcb->LastCluster = StartCluster + (ClusterCount - 1);
Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
ExReleaseFastMutex(&Fcb->LastMutex);
// Fire up the write command
Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
{
break;
}
BufferOffset += BytesDone;
Length -= BytesDone;
WriteOffset.u.LowPart += BytesDone;
}
if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
{
KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
}
if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
{
if (Length > 0)
{
Status = STATUS_UNSUCCESSFUL;
}
else
{
Status = IrpContext->Irp->IoStatus.Status;
}
}
return Status;
}
NTSTATUS
VfatCommonRead(
PVFAT_IRP_CONTEXT IrpContext)
{
PVFATFCB Fcb;
PVOID Buffer;
NTSTATUS Status;
ULONG Length = 0;
ULONG BytesPerSector;
LARGE_INTEGER ByteOffset;
ULONG ReturnedLength = 0;
BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
Fcb = IrpContext->FileObject->FsContext;
IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
Length = IrpContext->Stack->Parameters.Read.Length;
BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
if (!PagingIo &&
FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
{
if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
{
return STATUS_FILE_LOCK_CONFLICT;
}
}
Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
if (!PagingIo && !NoCache && !IsVolume)
{
// cached read
Status = STATUS_SUCCESS;
if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
{
Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
}
vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReads, 1);
vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReadBytes, Length);
_SEH2_TRY
{
if (IrpContext->FileObject->PrivateCacheMap == NULL)
{
CcInitializeCacheMap(IrpContext->FileObject,
(PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
FALSE,
&(VfatGlobalData->CacheMgrCallbacks),
Fcb);
}
if (!CcCopyRead(IrpContext->FileObject,
&ByteOffset,
Length,
CanWait,
Buffer,
&IrpContext->Irp->IoStatus))
{
ASSERT(!CanWait);
Status = STATUS_PENDING;
goto ByeBye;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
goto ByeBye;
}
_SEH2_END;
if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
{
Status = IrpContext->Irp->IoStatus.Status;
}
}
else
{
// non cached read
Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
if (!NT_SUCCESS(Status))
{
goto ByeBye;
}
if (ByteOffset.QuadPart + Length > ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
{
Length = (ULONG)(ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
}
if (!IsVolume)
{
vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReads, 1);
vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReadBytes, Length);
}
else
{
vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReads, 1);
vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReadBytes, Length);
}
Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
if (NT_SUCCESS(Status))
{
IrpContext->Irp->IoStatus.Information = ReturnedLength;
}
}
ByeBye:
return Status;
}
VOID
NTAPI
VfatStackOverflowRead(
PVOID Context,
IN PKEVENT Event)
{
PVFAT_IRP_CONTEXT IrpContext;
IrpContext = Context;
/* In a separate thread, we can wait and resources got locked */
SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
/* Perform the read operation */
DPRINT1("Performing posted read\n");
VfatCommonRead(IrpContext);
KeSetEvent(Event, 0, FALSE);
}
VOID
VfatPostRead(
PVFAT_IRP_CONTEXT IrpContext,
PERESOURCE Lock,
BOOLEAN PagingIo)
{
KEVENT Event;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
ExAcquireResourceSharedLite(Lock, TRUE);
/* If paging IO, call the non failing but blocking routine */
if (PagingIo)
{
FsRtlPostPagingFileStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
}
else
{
FsRtlPostStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
}
/* Wait till it's done */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
NTSTATUS
VfatRead(
PVFAT_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
PVFATFCB Fcb;
ULONG Length = 0;
PERESOURCE Resource = NULL;
LARGE_INTEGER ByteOffset;
ULONG BytesPerSector;
BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
ASSERT(IrpContext);
DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext->DeviceObject);
PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
// This request is not allowed on the main device object
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
{
DPRINT("VfatRead is called with the main device object.\n");
Status = STATUS_INVALID_DEVICE_REQUEST;
goto ByeBye;
}
ASSERT(IrpContext->DeviceExt);
ASSERT(IrpContext->FileObject);
Fcb = IrpContext->FileObject->FsContext;
ASSERT(Fcb);
IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
{
PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
IoSkipCurrentIrpStackLocation(IrpContext->Irp);
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
return Status;
}
DPRINT("<%wZ>\n", &Fcb->PathNameU);
ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
Length = IrpContext->Stack->Parameters.Read.Length;
BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
/* fail if file is a directory and no paged read */
if (vfatFCBIsDirectory(Fcb) && !PagingIo)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
DPRINT("'%wZ', Offset: %u, Length %u\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
if (ByteOffset.u.HighPart && !IsVolume)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
if (Length == 0)
{
IrpContext->Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
goto ByeBye;
}
if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
{
IrpContext->Irp->IoStatus.Information = 0;
Status = STATUS_END_OF_FILE;
goto ByeBye;
}
if (NoCache || PagingIo || IsVolume)
{
if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
{
DPRINT("%u %u\n", ByteOffset.u.LowPart, Length);
// non cached read must be sector aligned
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
}
if (IsVolume)
{
Resource = &IrpContext->DeviceExt->DirResource;
}
else if (PagingIo)
{
Resource = &Fcb->PagingIoResource;
}
else
{
Resource = &Fcb->MainResource;
}
/* Are we out of stack for the rest of the operation? */
if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD)
{
/* Lock the buffer */
Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* And post the read to the overflow thread */
VfatPostRead(IrpContext, Resource, PagingIo);
/* Return the appropriate status */
return IrpContext->Irp->IoStatus.Status;
}
if (!ExAcquireResourceSharedLite(Resource, CanWait))
{
Resource = NULL;
Status = STATUS_PENDING;
goto ByeBye;
}
Status = VfatCommonRead(IrpContext);
ByeBye:
if (Resource)
{
ExReleaseResourceLite(Resource);
}
if (Status == STATUS_PENDING)
{
Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
if (NT_SUCCESS(Status))
{
Status = VfatMarkIrpContextForQueue(IrpContext);
}
}
else
{
IrpContext->Irp->IoStatus.Status = Status;
if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
!PagingIo &&
(NT_SUCCESS(Status) || Status == STATUS_END_OF_FILE))
{
IrpContext->FileObject->CurrentByteOffset.QuadPart =
ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
}
if (NT_SUCCESS(Status))
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
}
DPRINT("%x\n", Status);
return Status;
}
NTSTATUS
VfatWrite(
PVFAT_IRP_CONTEXT *pIrpContext)
{
PVFAT_IRP_CONTEXT IrpContext = *pIrpContext;
PVFATFCB Fcb;
PERESOURCE Resource = NULL;
LARGE_INTEGER ByteOffset;
LARGE_INTEGER OldFileSize;
NTSTATUS Status = STATUS_SUCCESS;
ULONG Length = 0;
PVOID Buffer;
ULONG BytesPerSector;
BOOLEAN PagingIo, CanWait, IsVolume, IsFAT, NoCache;
ASSERT(IrpContext);
DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext->DeviceObject);
PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
// This request is not allowed on the main device object
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
{
DPRINT("VfatWrite is called with the main device object.\n");
Status = STATUS_INVALID_DEVICE_REQUEST;
goto ByeBye;
}
ASSERT(IrpContext->DeviceExt);
ASSERT(IrpContext->FileObject);
Fcb = IrpContext->FileObject->FsContext;
ASSERT(Fcb);
IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
IsFAT = BooleanFlagOn(Fcb->Flags, FCB_IS_FAT);
if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
{
PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
IoSkipCurrentIrpStackLocation(IrpContext->Irp);
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
return Status;
}
DPRINT("<%wZ>\n", &Fcb->PathNameU);
/* fail if file is a directory and no paged read */
if (vfatFCBIsDirectory(Fcb) && !PagingIo)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
ByteOffset.u.HighPart == -1)
{
ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
}
Length = IrpContext->Stack->Parameters.Write.Length;
BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
if (ByteOffset.u.HighPart && !IsVolume)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
if (IsFAT || IsVolume ||
vfatDirEntryGetFirstCluster(IrpContext->DeviceExt, &Fcb->entry) == 1)
{
if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
{
// we can't extend the FAT, the volume or the root on FAT12/FAT16
Status = STATUS_END_OF_FILE;
goto ByeBye;
}
}
if (PagingIo || NoCache || IsVolume)
{
if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
{
// non cached write must be sector aligned
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
}
OldFileSize = Fcb->RFCB.FileSize;
if (Length == 0)
{
/* Update last write time */
IrpContext->Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
goto Metadata;
}
if (PagingIo)
{
if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
{
Length = ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
}
}
if (!NoCache && !CcCanIWrite(IrpContext->FileObject, Length, CanWait,
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE)))
{
BOOLEAN Retrying;
Retrying = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
SetFlag(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
IoMarkIrpPending(IrpContext->Irp);
Status = STATUS_PENDING;
DPRINT1("Deferring write for Irp %p, context %p!\n", IrpContext->Irp, IrpContext);
CcDeferWrite(IrpContext->FileObject, VfatHandleDeferredWrite,
IrpContext, NULL, Length, Retrying);
*pIrpContext = NULL;
return Status;
}
if (IsVolume)
{
Resource = &IrpContext->DeviceExt->DirResource;
}
else if (PagingIo)
{
Resource = &Fcb->PagingIoResource;
}
else
{
Resource = &Fcb->MainResource;
}
if (PagingIo)
{
if (!ExAcquireResourceSharedLite(Resource, CanWait))
{
Resource = NULL;
Status = STATUS_PENDING;
goto ByeBye;
}
}
else
{
if (!ExAcquireResourceExclusiveLite(Resource, CanWait))
{
Resource = NULL;
Status = STATUS_PENDING;
goto ByeBye;
}
}
if (!PagingIo &&
FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
{
if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
{
Status = STATUS_FILE_LOCK_CONFLICT;
goto ByeBye;
}
}
if (!CanWait && !IsVolume)
{
if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
{
Status = STATUS_PENDING;
goto ByeBye;
}
}
Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
if (!IsFAT && !IsVolume && !PagingIo &&
ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
{
LARGE_INTEGER AllocationSize;
if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, CanWait))
{
Status = STATUS_PENDING;
goto ByeBye;
}
AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
IrpContext->DeviceExt, &AllocationSize);
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
if (!NT_SUCCESS (Status))
{
goto ByeBye;
}
}
if (!NoCache && !PagingIo && !IsVolume)
{
// cached write
vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWrites, 1);
vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWriteBytes, Length);
_SEH2_TRY
{
if (IrpContext->FileObject->PrivateCacheMap == NULL)
{
CcInitializeCacheMap(IrpContext->FileObject,
(PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
FALSE,
&VfatGlobalData->CacheMgrCallbacks,
Fcb);
}
if (ByteOffset.QuadPart > OldFileSize.QuadPart)
{
CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
}
if (CcCopyWrite(IrpContext->FileObject,
&ByteOffset,
Length,
TRUE /*CanWait*/,
Buffer))
{
IrpContext->Irp->IoStatus.Information = Length;
Status = STATUS_SUCCESS;
}
else
{
ASSERT(FALSE /*!CanWait*/);
Status = STATUS_UNSUCCESSFUL;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
// non cached write
Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
if (!NT_SUCCESS(Status))
{
Status = STATUS_INVALID_USER_BUFFER;
goto ByeBye;
}
if (ByteOffset.QuadPart > OldFileSize.QuadPart)
{
CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
}
if (!IsVolume)
{
vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWrites, 1);
vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWriteBytes, Length);
}
else
{
vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWrites, 1);
vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWriteBytes, Length);
}
Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
if (NT_SUCCESS(Status))
{
IrpContext->Irp->IoStatus.Information = Length;
}
}
Metadata:
if (!PagingIo && !IsFAT && !IsVolume)
{
if(!vfatFCBIsDirectory(Fcb))
{
LARGE_INTEGER SystemTime;
ULONG Filter;
// set dates and times
KeQuerySystemTime (&SystemTime);
if (vfatVolumeIsFatX(IrpContext->DeviceExt))
{
FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
&SystemTime, &Fcb->entry.FatX.UpdateDate,
&Fcb->entry.FatX.UpdateTime);
Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
}
else
{
FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
&SystemTime, &Fcb->entry.Fat.UpdateDate,
&Fcb->entry.Fat.UpdateTime);
Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
}
/* set date and times to dirty */
Fcb->Flags |= FCB_IS_DIRTY;
/* Time to notify the OS */
Filter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
if (ByteOffset.QuadPart != OldFileSize.QuadPart) Filter |= FILE_NOTIFY_CHANGE_SIZE;
vfatReportChange(IrpContext->DeviceExt, Fcb, Filter, FILE_ACTION_MODIFIED);
}
}
ByeBye:
if (Resource)
{
ExReleaseResourceLite(Resource);
}
if (Status == STATUS_PENDING)
{
Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
if (NT_SUCCESS(Status))
{
Status = VfatMarkIrpContextForQueue(IrpContext);
}
}
else
{
IrpContext->Irp->IoStatus.Status = Status;
if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
!PagingIo && NT_SUCCESS(Status))
{
IrpContext->FileObject->CurrentByteOffset.QuadPart =
ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
}
if (NT_SUCCESS(Status))
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
}
DPRINT("%x\n", Status);
return Status;
}