mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
b77824a375
- Cache the RootFcb so that its cleanup can be handled separately during dismounting. - Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag is set. - Actually dismount a volume if its VCB has been flagged as not good, or if we force dismounting. NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers as well as MS' fastfat, perform a comparison check of the current VCB's VPB ReferenceCount with some sort of "dangling"/"residual" open count. It seems to be related to the fact that the volume root directory as well as auxiliary data stream(s) are still opened, and only these are allowed to be opened at that moment. After analysis it appears that for the ReactOS' fastfat, this number is equal to "3". - On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and finish by removing the dismounted volume from the VolumeListEntry and cleaning up the notify synchronization object and the resources. - During dismounting, and on shutdown, flush the volume before resetting its dirty bit. - On shutdown, after volume flushing, try to unmount it without forcing. - Release the VCB resources only when we actually dismount the volume in VfatCheckForDismount(). - Initialize first the notify list and the synchronization object, before sending the FSRTL_VOLUME_MOUNT notification. - If we failed at mounting a volume but its VCB's FATFileObject was already initialized, first call CcUninitializeCacheMap() on it before dereferencing it. - Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure) and volume unlocking. - Flush the volume before locking it, and clean its dirty bit if needed. NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this allows us to not re-clean the bit if it has been previously cleaned. This is needed for instance in this scenario: - The volume is locked (it gets flushed and the dirty bit is possibly cleared); - The volume then gets formatted with a completely different FS, that possibly clears up the first sector (e.g. BTRFS ignores 1st sector); - The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY was set prior to resetting it, we could attempt clearing it again! But now that the volume's filesystem has been completely changed, we would then try to modify the dirty bit on an erroneous position on disk! That's why it should not be touched in this case during dismounting. - The volume is unlocked (same comment as above), and later can be detected as being BTRFS.
1293 lines
34 KiB
C
1293 lines
34 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)
|
|
|
|
/* 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);
|
|
}
|
|
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 */
|