mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
542e9f2ba0
No code changes. Addendum to14c39362
and6d65da93
.
1295 lines
34 KiB
C
1295 lines
34 KiB
C
/*
|
|
* PROJECT: VFAT Filesystem
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: File Allocation Table routines
|
|
* COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
|
|
* Copyright 2015-2018 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)
|
|
|
|
/* 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 = NULL;
|
|
LARGE_INTEGER Offset;
|
|
|
|
ChunkSize = CACHEPAGESIZE(DeviceExt);
|
|
FATOffset = CurrentCluster * sizeof(ULONG);
|
|
Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
|
|
_SEH2_TRY
|
|
{
|
|
if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress))
|
|
{
|
|
NT_ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
_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);
|
|
}
|
|
if (Clusters != NULL)
|
|
{
|
|
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_BUFFER);
|
|
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_BUFFER);
|
|
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_BUFFER);
|
|
#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_BUFFER);
|
|
#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_BUFFER);
|
|
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_BUFFER);
|
|
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_BUFFER);
|
|
#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_BUFFER);
|
|
#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_BUFFER);
|
|
if (Sector == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Sector, TAG_BUFFER);
|
|
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_BUFFER);
|
|
#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_BUFFER);
|
|
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_BUFFER);
|
|
if (Sector == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Sector, TAG_BUFFER);
|
|
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_BUFFER);
|
|
#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_BUFFER);
|
|
return Status;
|
|
#endif
|
|
}
|
|
|
|
NTSTATUS
|
|
FAT32UpdateFreeClustersCount(
|
|
PDEVICE_EXTENSION DeviceExt)
|
|
{
|
|
LARGE_INTEGER Offset;
|
|
ULONG Length;
|
|
#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
|
|
NTSTATUS Status;
|
|
#else
|
|
PVOID Context;
|
|
#endif
|
|
struct _FsInfoSector * Sector;
|
|
|
|
if (!DeviceExt->AvailableClustersValid)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* We'll read (and then write) the fsinfo sector */
|
|
Offset.QuadPart = DeviceExt->FatInfo.FSInfoSector * DeviceExt->FatInfo.BytesPerSector;
|
|
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_BUFFER);
|
|
if (Sector == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Sector, TAG_BUFFER);
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
/* Make sure we have a FSINFO sector */
|
|
if (Sector->ExtBootSignature2 != 0x41615252 ||
|
|
Sector->FSINFOSignature != 0x61417272 ||
|
|
Sector->Signatur2 != 0xaa550000)
|
|
{
|
|
ASSERT(FALSE);
|
|
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
|
|
CcUnpinData(Context);
|
|
#else
|
|
ExFreePoolWithTag(Sector, TAG_BUFFER);
|
|
#endif
|
|
return STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
|
|
/* Update the free clusters count */
|
|
Sector->FreeCluster = InterlockedCompareExchange((PLONG)&DeviceExt->AvailableClusters, 0, 0);
|
|
|
|
#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
|
|
/* Mark FSINFO sector dirty so that it gets written to the disk */
|
|
CcSetDirtyPinnedData(Context, NULL);
|
|
CcUnpinData(Context);
|
|
return STATUS_SUCCESS;
|
|
#else
|
|
/* Write back the FSINFO sector to the disk */
|
|
Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
|
|
ExFreePoolWithTag(Sector, TAG_BUFFER);
|
|
return Status;
|
|
#endif
|
|
}
|
|
|
|
/* EOF */
|