reactos/drivers/filesystems/fastfat/fat.c
Pierre Schweitzer 7c01587680
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.

This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.

As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).

If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!

CORE-13758
CORE-13760
CORE-13759
2018-05-18 23:05:05 +02:00

1215 lines
32 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystems/fastfat/fat.c
* PURPOSE: FastFAT Filesystem
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
* Pierre Schweitzer (pierre@reactos.org)
*
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
#define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
(pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
/* FIXME: because volume is not cached, we have to perform direct IOs
* The day this is fixed, just comment out that line, and check
* it still works (and delete old code ;-))
*/
#define VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* FUNCTIONS ****************************************************************/
/*
* FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
* disk read
*/
NTSTATUS
FAT32GetNextCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG CurrentCluster,
PULONG NextCluster)
{
NTSTATUS Status = STATUS_SUCCESS;
PVOID BaseAddress;
ULONG FATOffset;
ULONG ChunkSize;
PVOID Context;
LARGE_INTEGER Offset;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FATOffset = CurrentCluster * sizeof(ULONG);
Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff;
if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
CurrentCluster = 0xffffffff;
if (CurrentCluster == 0)
{
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
Status = STATUS_FILE_CORRUPT_ERROR;
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
ASSERT(CurrentCluster != 0);
}
CcUnpinData(Context);
*NextCluster = CurrentCluster;
return Status;
}
/*
* FUNCTION: Retrieve the next FAT16 cluster from the FAT table
*/
NTSTATUS
FAT16GetNextCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG CurrentCluster,
PULONG NextCluster)
{
NTSTATUS Status = STATUS_SUCCESS;
PVOID BaseAddress;
ULONG FATOffset;
ULONG ChunkSize;
PVOID Context;
LARGE_INTEGER Offset;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FATOffset = CurrentCluster * 2;
Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
CurrentCluster = 0xffffffff;
if (CurrentCluster == 0)
{
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
Status = STATUS_FILE_CORRUPT_ERROR;
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
ASSERT(CurrentCluster != 0);
}
CcUnpinData(Context);
*NextCluster = CurrentCluster;
return Status;
}
/*
* FUNCTION: Retrieve the next FAT12 cluster from the FAT table
*/
NTSTATUS
FAT12GetNextCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG CurrentCluster,
PULONG NextCluster)
{
PUSHORT CBlock;
ULONG Entry;
PVOID BaseAddress;
PVOID Context;
LARGE_INTEGER Offset;
*NextCluster = 0;
Offset.QuadPart = 0;
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8);
if ((CurrentCluster % 2) == 0)
{
Entry = *CBlock & 0x0fff;
}
else
{
Entry = *CBlock >> 4;
}
// DPRINT("Entry %x\n",Entry);
if (Entry >= 0xff8 && Entry <= 0xfff)
Entry = 0xffffffff;
// DPRINT("Returning %x\n",Entry);
ASSERT(Entry != 0);
*NextCluster = Entry;
CcUnpinData(Context);
// return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
return STATUS_SUCCESS;
}
/*
* FUNCTION: Finds the first available cluster in a FAT16 table
*/
NTSTATUS
FAT16FindAndMarkAvailableCluster(
PDEVICE_EXTENSION DeviceExt,
PULONG Cluster)
{
ULONG FatLength;
ULONG StartCluster;
ULONG i, j;
PVOID BaseAddress;
ULONG ChunkSize;
PVOID Context = 0;
LARGE_INTEGER Offset;
PUSHORT Block;
PUSHORT BlockEnd;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
*Cluster = 0;
StartCluster = DeviceExt->LastAvailableCluster;
for (j = 0; j < 2; j++)
{
for (i = StartCluster; i < FatLength;)
{
Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
/* Now process the whole block */
while (Block < BlockEnd && i < FatLength)
{
if (*Block == 0)
{
DPRINT("Found available cluster 0x%x\n", i);
DeviceExt->LastAvailableCluster = *Cluster = i;
*Block = 0xffff;
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
if (DeviceExt->AvailableClustersValid)
InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
return STATUS_SUCCESS;
}
Block++;
i++;
}
CcUnpinData(Context);
}
FatLength = StartCluster;
StartCluster = 2;
}
return STATUS_DISK_FULL;
}
/*
* FUNCTION: Finds the first available cluster in a FAT12 table
*/
NTSTATUS
FAT12FindAndMarkAvailableCluster(
PDEVICE_EXTENSION DeviceExt,
PULONG Cluster)
{
ULONG FatLength;
ULONG StartCluster;
ULONG Entry;
PUSHORT CBlock;
ULONG i, j;
PVOID BaseAddress;
PVOID Context;
LARGE_INTEGER Offset;
FatLength = DeviceExt->FatInfo.NumberOfClusters + 2;
*Cluster = 0;
StartCluster = DeviceExt->LastAvailableCluster;
Offset.QuadPart = 0;
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
for (j = 0; j < 2; j++)
{
for (i = StartCluster; i < FatLength; i++)
{
CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
if ((i % 2) == 0)
{
Entry = *CBlock & 0xfff;
}
else
{
Entry = *CBlock >> 4;
}
if (Entry == 0)
{
DPRINT("Found available cluster 0x%x\n", i);
DeviceExt->LastAvailableCluster = *Cluster = i;
if ((i % 2) == 0)
*CBlock = (*CBlock & 0xf000) | 0xfff;
else
*CBlock = (*CBlock & 0xf) | 0xfff0;
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
if (DeviceExt->AvailableClustersValid)
InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
return STATUS_SUCCESS;
}
}
FatLength = StartCluster;
StartCluster = 2;
}
CcUnpinData(Context);
return STATUS_DISK_FULL;
}
/*
* FUNCTION: Finds the first available cluster in a FAT32 table
*/
NTSTATUS
FAT32FindAndMarkAvailableCluster(
PDEVICE_EXTENSION DeviceExt,
PULONG Cluster)
{
ULONG FatLength;
ULONG StartCluster;
ULONG i, j;
PVOID BaseAddress;
ULONG ChunkSize;
PVOID Context;
LARGE_INTEGER Offset;
PULONG Block;
PULONG BlockEnd;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
*Cluster = 0;
StartCluster = DeviceExt->LastAvailableCluster;
for (j = 0; j < 2; j++)
{
for (i = StartCluster; i < FatLength;)
{
Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
/* Now process the whole block */
while (Block < BlockEnd && i < FatLength)
{
if ((*Block & 0x0fffffff) == 0)
{
DPRINT("Found available cluster 0x%x\n", i);
DeviceExt->LastAvailableCluster = *Cluster = i;
*Block = 0x0fffffff;
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
if (DeviceExt->AvailableClustersValid)
InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
return STATUS_SUCCESS;
}
Block++;
i++;
}
CcUnpinData(Context);
}
FatLength = StartCluster;
StartCluster = 2;
}
return STATUS_DISK_FULL;
}
/*
* FUNCTION: Counts free cluster in a FAT12 table
*/
static
NTSTATUS
FAT12CountAvailableClusters(
PDEVICE_EXTENSION DeviceExt)
{
ULONG Entry;
PVOID BaseAddress;
ULONG ulCount = 0;
ULONG i;
ULONG numberofclusters;
LARGE_INTEGER Offset;
PVOID Context;
PUSHORT CBlock;
Offset.QuadPart = 0;
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2;
for (i = 2; i < numberofclusters; i++)
{
CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
if ((i % 2) == 0)
{
Entry = *CBlock & 0x0fff;
}
else
{
Entry = *CBlock >> 4;
}
if (Entry == 0)
ulCount++;
}
CcUnpinData(Context);
DeviceExt->AvailableClusters = ulCount;
DeviceExt->AvailableClustersValid = TRUE;
return STATUS_SUCCESS;
}
/*
* FUNCTION: Counts free clusters in a FAT16 table
*/
static
NTSTATUS
FAT16CountAvailableClusters(
PDEVICE_EXTENSION DeviceExt)
{
PUSHORT Block;
PUSHORT BlockEnd;
PVOID BaseAddress = NULL;
ULONG ulCount = 0;
ULONG i;
ULONG ChunkSize;
PVOID Context = NULL;
LARGE_INTEGER Offset;
ULONG FatLength;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
for (i = 2; i < FatLength; )
{
Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
/* Now process the whole block */
while (Block < BlockEnd && i < FatLength)
{
if (*Block == 0)
ulCount++;
Block++;
i++;
}
CcUnpinData(Context);
}
DeviceExt->AvailableClusters = ulCount;
DeviceExt->AvailableClustersValid = TRUE;
return STATUS_SUCCESS;
}
/*
* FUNCTION: Counts free clusters in a FAT32 table
*/
static
NTSTATUS
FAT32CountAvailableClusters(
PDEVICE_EXTENSION DeviceExt)
{
PULONG Block;
PULONG BlockEnd;
PVOID BaseAddress = NULL;
ULONG ulCount = 0;
ULONG i;
ULONG ChunkSize;
PVOID Context = NULL;
LARGE_INTEGER Offset;
ULONG FatLength;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
for (i = 2; i < FatLength; )
{
Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
_SEH2_TRY
{
CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
/* Now process the whole block */
while (Block < BlockEnd && i < FatLength)
{
if ((*Block & 0x0fffffff) == 0)
ulCount++;
Block++;
i++;
}
CcUnpinData(Context);
}
DeviceExt->AvailableClusters = ulCount;
DeviceExt->AvailableClustersValid = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS
CountAvailableClusters(
PDEVICE_EXTENSION DeviceExt,
PLARGE_INTEGER Clusters)
{
NTSTATUS Status = STATUS_SUCCESS;
ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
if (!DeviceExt->AvailableClustersValid)
{
if (DeviceExt->FatInfo.FatType == FAT12)
Status = FAT12CountAvailableClusters(DeviceExt);
else if (DeviceExt->FatInfo.FatType == FAT16 || DeviceExt->FatInfo.FatType == FATX16)
Status = FAT16CountAvailableClusters(DeviceExt);
else
Status = FAT32CountAvailableClusters(DeviceExt);
}
Clusters->QuadPart = DeviceExt->AvailableClusters;
ExReleaseResourceLite (&DeviceExt->FatResource);
return Status;
}
/*
* FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
*/
NTSTATUS
FAT12WriteCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG ClusterToWrite,
ULONG NewValue,
PULONG OldValue)
{
ULONG FATOffset;
PUCHAR CBlock;
PVOID BaseAddress;
PVOID Context;
LARGE_INTEGER Offset;
Offset.QuadPart = 0;
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
CBlock = (PUCHAR)BaseAddress;
FATOffset = (ClusterToWrite * 12) / 8;
DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
NewValue, ClusterToWrite, FATOffset);
if ((ClusterToWrite % 2) == 0)
{
*OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8);
CBlock[FATOffset] = (UCHAR)NewValue;
CBlock[FATOffset + 1] &= 0xf0;
CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8;
}
else
{
*OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4);
CBlock[FATOffset] &= 0x0f;
CBlock[FATOffset] |= (NewValue & 0xf) << 4;
CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4);
}
/* Write the changed FAT sector(s) to disk */
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
*/
NTSTATUS
FAT16WriteCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG ClusterToWrite,
ULONG NewValue,
PULONG OldValue)
{
PVOID BaseAddress;
ULONG FATOffset;
ULONG ChunkSize;
PVOID Context;
LARGE_INTEGER Offset;
PUSHORT Cluster;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FATOffset = ClusterToWrite * 2;
Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
ClusterToWrite);
Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
*OldValue = *Cluster;
*Cluster = (USHORT)NewValue;
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Writes a cluster to the FAT32 physical tables
*/
NTSTATUS
FAT32WriteCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG ClusterToWrite,
ULONG NewValue,
PULONG OldValue)
{
PVOID BaseAddress;
ULONG FATOffset;
ULONG ChunkSize;
PVOID Context;
LARGE_INTEGER Offset;
PULONG Cluster;
ChunkSize = CACHEPAGESIZE(DeviceExt);
FATOffset = (ClusterToWrite * 4);
Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
_SEH2_TRY
{
CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
ClusterToWrite);
Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize)));
*OldValue = *Cluster & 0x0fffffff;
*Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff);
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Write a changed FAT entry
*/
NTSTATUS
WriteCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG ClusterToWrite,
ULONG NewValue)
{
NTSTATUS Status;
ULONG OldValue;
ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
Status = DeviceExt->WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
if (DeviceExt->AvailableClustersValid)
{
if (OldValue && NewValue == 0)
InterlockedIncrement((PLONG)&DeviceExt->AvailableClusters);
else if (OldValue == 0 && NewValue)
InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
}
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
/*
* FUNCTION: Converts the cluster number to a sector number for this physical
* device
*/
ULONGLONG
ClusterToSector(
PDEVICE_EXTENSION DeviceExt,
ULONG Cluster)
{
return DeviceExt->FatInfo.dataStart +
((ULONGLONG)(Cluster - 2) * DeviceExt->FatInfo.SectorsPerCluster);
}
/*
* FUNCTION: Retrieve the next cluster depending on the FAT type
*/
NTSTATUS
GetNextCluster(
PDEVICE_EXTENSION DeviceExt,
ULONG CurrentCluster,
PULONG NextCluster)
{
NTSTATUS Status;
DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
DeviceExt, CurrentCluster);
if (CurrentCluster == 0)
{
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
ASSERT(CurrentCluster != 0);
return STATUS_FILE_CORRUPT_ERROR;
}
ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
/*
* FUNCTION: Retrieve the next cluster depending on the FAT type
*/
NTSTATUS
GetNextClusterExtend(
PDEVICE_EXTENSION DeviceExt,
ULONG CurrentCluster,
PULONG NextCluster)
{
ULONG NewCluster;
NTSTATUS Status;
DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
DeviceExt, CurrentCluster);
ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
/*
* If the file hasn't any clusters allocated then we need special
* handling
*/
if (CurrentCluster == 0)
{
Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
if (!NT_SUCCESS(Status))
{
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
*NextCluster = NewCluster;
ExReleaseResourceLite(&DeviceExt->FatResource);
return STATUS_SUCCESS;
}
Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
if ((*NextCluster) == 0xFFFFFFFF)
{
/* We are after last existing cluster, we must add one to file */
/* Firstly, find the next available open allocation unit and
mark it as end of file */
Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
if (!NT_SUCCESS(Status))
{
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
/* Now, write the AU of the LastCluster with the value of the newly
found AU */
WriteCluster(DeviceExt, CurrentCluster, NewCluster);
*NextCluster = NewCluster;
}
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
/*
* FUNCTION: Retrieve the dirty status
*/
NTSTATUS
GetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
PBOOLEAN DirtyStatus)
{
NTSTATUS Status;
DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt);
/* FAT12 has no dirty bit */
if (DeviceExt->FatInfo.FatType == FAT12)
{
*DirtyStatus = FALSE;
return STATUS_SUCCESS;
}
/* Not really in the FAT, but share the lock because
* we're really low-level and shouldn't happent that often
* And call the appropriate function
*/
ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
Status = DeviceExt->GetDirtyStatus(DeviceExt, DirtyStatus);
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
NTSTATUS
FAT16GetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
PBOOLEAN DirtyStatus)
{
LARGE_INTEGER Offset;
ULONG Length;
#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
NTSTATUS Status;
#else
PVOID Context;
#endif
struct _BootSector * Sector;
/* We'll read the bootsector at 0 */
Offset.QuadPart = 0;
Length = DeviceExt->FatInfo.BytesPerSector;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Go through Cc for this */
_SEH2_TRY
{
CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
#else
/* No Cc, do it the old way:
* - Allocate a big enough buffer
* - And read the disk
*/
Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
if (Sector == NULL)
{
*DirtyStatus = TRUE;
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
if (!NT_SUCCESS(Status))
{
*DirtyStatus = TRUE;
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
}
#endif
/* Make sure we have a boot sector...
* FIXME: This check is a bit lame and should be improved
*/
if (Sector->Signatur1 != 0xaa55)
{
/* Set we are dirty so that we don't attempt anything */
*DirtyStatus = TRUE;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_DISK_CORRUPT_ERROR;
}
/* Return the status of the dirty bit */
if (Sector->Res1 & FAT_DIRTY_BIT)
*DirtyStatus = TRUE;
else
*DirtyStatus = FALSE;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_SUCCESS;
}
NTSTATUS
FAT32GetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
PBOOLEAN DirtyStatus)
{
LARGE_INTEGER Offset;
ULONG Length;
#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
NTSTATUS Status;
#else
PVOID Context;
#endif
struct _BootSector32 * Sector;
/* We'll read the bootsector at 0 */
Offset.QuadPart = 0;
Length = DeviceExt->FatInfo.BytesPerSector;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Go through Cc for this */
_SEH2_TRY
{
CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
#else
/* No Cc, do it the old way:
* - Allocate a big enough buffer
* - And read the disk
*/
Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
if (Sector == NULL)
{
*DirtyStatus = TRUE;
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
if (!NT_SUCCESS(Status))
{
*DirtyStatus = TRUE;
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
}
#endif
/* Make sure we have a boot sector...
* FIXME: This check is a bit lame and should be improved
*/
if (Sector->Signature1 != 0xaa55)
{
/* Set we are dirty so that we don't attempt anything */
*DirtyStatus = TRUE;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_DISK_CORRUPT_ERROR;
}
/* Return the status of the dirty bit */
if (Sector->Res4 & FAT_DIRTY_BIT)
*DirtyStatus = TRUE;
else
*DirtyStatus = FALSE;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_SUCCESS;
}
/*
* FUNCTION: Set the dirty status
*/
NTSTATUS
SetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
BOOLEAN DirtyStatus)
{
NTSTATUS Status;
DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt, DirtyStatus);
/* FAT12 has no dirty bit */
if (DeviceExt->FatInfo.FatType == FAT12)
{
return STATUS_SUCCESS;
}
/* Not really in the FAT, but share the lock because
* we're really low-level and shouldn't happent that often
* And call the appropriate function
* Acquire exclusive because we will modify ondisk value
*/
ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
Status = DeviceExt->SetDirtyStatus(DeviceExt, DirtyStatus);
ExReleaseResourceLite(&DeviceExt->FatResource);
return Status;
}
NTSTATUS
FAT16SetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
BOOLEAN DirtyStatus)
{
LARGE_INTEGER Offset;
ULONG Length;
#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
NTSTATUS Status;
#else
PVOID Context;
#endif
struct _BootSector * Sector;
/* We'll read (and then write) the bootsector at 0 */
Offset.QuadPart = 0;
Length = DeviceExt->FatInfo.BytesPerSector;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Go through Cc for this */
_SEH2_TRY
{
CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
#else
/* No Cc, do it the old way:
* - Allocate a big enough buffer
* - And read the disk
*/
Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
if (Sector == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
}
#endif
/* Make sure we have a boot sector...
* FIXME: This check is a bit lame and should be improved
*/
if (Sector->Signatur1 != 0xaa55)
{
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_DISK_CORRUPT_ERROR;
}
/* Modify the dirty bit status according
* to caller needs
*/
if (!DirtyStatus)
{
Sector->Res1 &= ~FAT_DIRTY_BIT;
}
else
{
Sector->Res1 |= FAT_DIRTY_BIT;
}
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Mark boot sector dirty so that it gets written to the disk */
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
return STATUS_SUCCESS;
#else
/* Write back the boot sector to the disk */
Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
#endif
}
NTSTATUS
FAT32SetDirtyStatus(
PDEVICE_EXTENSION DeviceExt,
BOOLEAN DirtyStatus)
{
LARGE_INTEGER Offset;
ULONG Length;
#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
NTSTATUS Status;
#else
PVOID Context;
#endif
struct _BootSector32 * Sector;
/* We'll read (and then write) the bootsector at 0 */
Offset.QuadPart = 0;
Length = DeviceExt->FatInfo.BytesPerSector;
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Go through Cc for this */
_SEH2_TRY
{
CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
#else
/* No Cc, do it the old way:
* - Allocate a big enough buffer
* - And read the disk
*/
Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
if (Sector == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
}
#endif
/* Make sure we have a boot sector...
* FIXME: This check is a bit lame and should be improved
*/
if (Sector->Signature1 != 0xaa55)
{
ASSERT(FALSE);
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
CcUnpinData(Context);
#else
ExFreePoolWithTag(Sector, TAG_VFAT);
#endif
return STATUS_DISK_CORRUPT_ERROR;
}
/* Modify the dirty bit status according
* to caller needs
*/
if (!DirtyStatus)
{
Sector->Res4 &= ~FAT_DIRTY_BIT;
}
else
{
Sector->Res4 |= FAT_DIRTY_BIT;
}
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
/* Mark boot sector dirty so that it gets written to the disk */
CcSetDirtyPinnedData(Context, NULL);
CcUnpinData(Context);
return STATUS_SUCCESS;
#else
/* Write back the boot sector to the disk */
Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
ExFreePoolWithTag(Sector, TAG_VFAT);
return Status;
#endif
}
/* EOF */