mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
542e9f2ba0
No code changes. Addendum to14c39362
and6d65da93
.
1230 lines
36 KiB
C
1230 lines
36 KiB
C
/*
|
|
* PROJECT: VFAT Filesystem
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Reading and writing routines
|
|
* COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
|
|
* Copyright 2008-2018 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;
|
|
}
|