reactos/drivers/filesystems/fastfat/rw.c

1233 lines
36 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystems/fastfat/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>
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
/*
* 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);
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
#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
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
{
Status = OffsetToCluster(DeviceExt, FirstCluster,
ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
&CurrentCluster, FALSE);
}
if (!NT_SUCCESS(Status))
{
return Status;
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
}
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
ExAcquireFastMutex(&Fcb->LastMutex);
Fcb->LastCluster = CurrentCluster;
Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
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);
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
#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);
}
- Change all FAT*FindAvailableCluster functions to new functions FAT*FindAndMarkAvailableCluster which have incorporated the setting of EOF marker in the File Allocation Table for the returned cluster. - Rewritten FAT32CountAvailableClusters, FAT32FindAndMarkAvailableCluster, FAT16CountAvailableClusters and FAT16FindAndMarkAvailableCluster for better performance. These functions were using one big loop for traversing the FAT and had a block of code in the loop for requesting cache manger for next chunk of FAT *if necessary* (which commonly wasn't the case). Now it's changed to request always a whole FAT chunk and process it at once in a nested loop. - Cache last offset + cluster pair while reading/writing from/to file. This ensures almost linear times when doing sequential reads/writes, because the whole FAT traversing for the file is not done again for every request. Previously there was code for this, but it was neither correctly used nor working. It stored the last offset + cluster pair in file CCB structure (that is unique for each opened file handle) and not FCB structure (which is shared among all instances of the same file). This resulted in inconsistent cluster + offset number when the file allocation chain was changed (eg. by enlarging/shrinking the file), but since the cached offsets weren't actully used it went unnoticed. - Remove old hack from NextCluster and fix the call to it in VfatAddEntry. Not much to say about it, there was an temporary hack in NextCluster and with fixed VfatAddEntry it's now no longer needed. - Add pointers to WriteCluster, FindAndMarkAvailableCluster and GetNextCluster functions to device extension (set during mount) and use them at appropriate locations. This avoids some nasty if's in the code and causes some unnoticable performance improvment in the low-level FAT code. - Lock the directory FCB in VfatAddEntry while modifying it. This should propably be done on more places, but it needs more investigations. - Increase the file cache allocation size in VfatRead/VfatWrite. It boosts the overall speed of the driver a lot. svn path=/trunk/; revision=10344
2004-08-01 21:57:18 +00:00
#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
2017-11-21 21:18:11 +00:00
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);
}
2017-11-21 21:18:11 +00:00
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;
}