2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/filesystems/fastfat/fat.c
5 * PURPOSE: FastFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 * Pierre Schweitzer (pierre@reactos.org)
11 /* INCLUDES *****************************************************************/
18 /* GLOBALS ******************************************************************/
20 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
21 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
23 /* FIXME: because volume is not cached, we have to perform direct IOs
24 * The day this is fixed, just comment out that line, and check
25 * it still works (and delete old code ;-))
27 #define VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
29 /* FUNCTIONS ****************************************************************/
32 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
37 PDEVICE_EXTENSION DeviceExt
,
41 NTSTATUS Status
= STATUS_SUCCESS
;
48 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
49 FATOffset
= CurrentCluster
* sizeof(ULONG
);
50 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
53 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
55 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
57 _SEH2_YIELD(return _SEH2_GetExceptionCode());
61 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
62 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
63 CurrentCluster
= 0xffffffff;
65 if (CurrentCluster
== 0)
67 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
68 Status
= STATUS_FILE_CORRUPT_ERROR
;
69 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
70 ASSERT(CurrentCluster
!= 0);
73 *NextCluster
= CurrentCluster
;
78 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
82 PDEVICE_EXTENSION DeviceExt
,
86 NTSTATUS Status
= STATUS_SUCCESS
;
93 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
94 FATOffset
= CurrentCluster
* 2;
95 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
98 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
100 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
102 _SEH2_YIELD(return _SEH2_GetExceptionCode());
106 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
107 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
108 CurrentCluster
= 0xffffffff;
110 if (CurrentCluster
== 0)
112 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
113 Status
= STATUS_FILE_CORRUPT_ERROR
;
114 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
115 ASSERT(CurrentCluster
!= 0);
118 CcUnpinData(Context
);
119 *NextCluster
= CurrentCluster
;
124 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
128 PDEVICE_EXTENSION DeviceExt
,
129 ULONG CurrentCluster
,
136 LARGE_INTEGER Offset
;
143 CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
);
145 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
147 _SEH2_YIELD(return _SEH2_GetExceptionCode());
151 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
152 if ((CurrentCluster
% 2) == 0)
154 Entry
= *CBlock
& 0x0fff;
158 Entry
= *CBlock
>> 4;
161 // DPRINT("Entry %x\n",Entry);
162 if (Entry
>= 0xff8 && Entry
<= 0xfff)
165 // DPRINT("Returning %x\n",Entry);
167 *NextCluster
= Entry
;
168 CcUnpinData(Context
);
169 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
170 return STATUS_SUCCESS
;
174 * FUNCTION: Finds the first available cluster in a FAT16 table
177 FAT16FindAndMarkAvailableCluster(
178 PDEVICE_EXTENSION DeviceExt
,
187 LARGE_INTEGER Offset
;
191 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
192 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
194 StartCluster
= DeviceExt
->LastAvailableCluster
;
196 for (j
= 0; j
< 2; j
++)
198 for (i
= StartCluster
; i
< FatLength
;)
200 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
203 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
207 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
208 _SEH2_YIELD(return _SEH2_GetExceptionCode());
212 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
213 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
215 /* Now process the whole block */
216 while (Block
< BlockEnd
&& i
< FatLength
)
220 DPRINT("Found available cluster 0x%x\n", i
);
221 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
223 CcSetDirtyPinnedData(Context
, NULL
);
224 CcUnpinData(Context
);
225 if (DeviceExt
->AvailableClustersValid
)
226 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
227 return STATUS_SUCCESS
;
234 CcUnpinData(Context
);
237 FatLength
= StartCluster
;
241 return STATUS_DISK_FULL
;
245 * FUNCTION: Finds the first available cluster in a FAT12 table
248 FAT12FindAndMarkAvailableCluster(
249 PDEVICE_EXTENSION DeviceExt
,
259 LARGE_INTEGER Offset
;
261 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
263 StartCluster
= DeviceExt
->LastAvailableCluster
;
267 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
);
269 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
271 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
272 _SEH2_YIELD(return _SEH2_GetExceptionCode());
276 for (j
= 0; j
< 2; j
++)
278 for (i
= StartCluster
; i
< FatLength
; i
++)
280 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
283 Entry
= *CBlock
& 0xfff;
287 Entry
= *CBlock
>> 4;
292 DPRINT("Found available cluster 0x%x\n", i
);
293 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
295 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
297 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
298 CcSetDirtyPinnedData(Context
, NULL
);
299 CcUnpinData(Context
);
300 if (DeviceExt
->AvailableClustersValid
)
301 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
302 return STATUS_SUCCESS
;
305 FatLength
= StartCluster
;
308 CcUnpinData(Context
);
309 return STATUS_DISK_FULL
;
313 * FUNCTION: Finds the first available cluster in a FAT32 table
316 FAT32FindAndMarkAvailableCluster(
317 PDEVICE_EXTENSION DeviceExt
,
326 LARGE_INTEGER Offset
;
330 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
331 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
333 StartCluster
= DeviceExt
->LastAvailableCluster
;
335 for (j
= 0; j
< 2; j
++)
337 for (i
= StartCluster
; i
< FatLength
;)
339 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
342 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
346 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
347 _SEH2_YIELD(return _SEH2_GetExceptionCode());
350 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
351 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
353 /* Now process the whole block */
354 while (Block
< BlockEnd
&& i
< FatLength
)
356 if ((*Block
& 0x0fffffff) == 0)
358 DPRINT("Found available cluster 0x%x\n", i
);
359 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
361 CcSetDirtyPinnedData(Context
, NULL
);
362 CcUnpinData(Context
);
363 if (DeviceExt
->AvailableClustersValid
)
364 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
365 return STATUS_SUCCESS
;
372 CcUnpinData(Context
);
374 FatLength
= StartCluster
;
377 return STATUS_DISK_FULL
;
381 * FUNCTION: Counts free cluster in a FAT12 table
385 FAT12CountAvailableClusters(
386 PDEVICE_EXTENSION DeviceExt
)
392 ULONG numberofclusters
;
393 LARGE_INTEGER Offset
;
400 CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
);
402 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
404 _SEH2_YIELD(return _SEH2_GetExceptionCode());
408 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
410 for (i
= 2; i
< numberofclusters
; i
++)
412 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
415 Entry
= *CBlock
& 0x0fff;
419 Entry
= *CBlock
>> 4;
426 CcUnpinData(Context
);
427 DeviceExt
->AvailableClusters
= ulCount
;
428 DeviceExt
->AvailableClustersValid
= TRUE
;
430 return STATUS_SUCCESS
;
435 * FUNCTION: Counts free clusters in a FAT16 table
439 FAT16CountAvailableClusters(
440 PDEVICE_EXTENSION DeviceExt
)
444 PVOID BaseAddress
= NULL
;
448 PVOID Context
= NULL
;
449 LARGE_INTEGER Offset
;
452 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
453 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
455 for (i
= 2; i
< FatLength
; )
457 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
460 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
464 _SEH2_YIELD(return _SEH2_GetExceptionCode());
467 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
468 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
470 /* Now process the whole block */
471 while (Block
< BlockEnd
&& i
< FatLength
)
479 CcUnpinData(Context
);
482 DeviceExt
->AvailableClusters
= ulCount
;
483 DeviceExt
->AvailableClustersValid
= TRUE
;
485 return STATUS_SUCCESS
;
490 * FUNCTION: Counts free clusters in a FAT32 table
494 FAT32CountAvailableClusters(
495 PDEVICE_EXTENSION DeviceExt
)
499 PVOID BaseAddress
= NULL
;
503 PVOID Context
= NULL
;
504 LARGE_INTEGER Offset
;
507 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
508 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
510 for (i
= 2; i
< FatLength
; )
512 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
515 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
517 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
519 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
520 _SEH2_YIELD(return _SEH2_GetExceptionCode());
523 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
524 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
526 /* Now process the whole block */
527 while (Block
< BlockEnd
&& i
< FatLength
)
529 if ((*Block
& 0x0fffffff) == 0)
535 CcUnpinData(Context
);
538 DeviceExt
->AvailableClusters
= ulCount
;
539 DeviceExt
->AvailableClustersValid
= TRUE
;
541 return STATUS_SUCCESS
;
545 CountAvailableClusters(
546 PDEVICE_EXTENSION DeviceExt
,
547 PLARGE_INTEGER Clusters
)
549 NTSTATUS Status
= STATUS_SUCCESS
;
550 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
551 if (!DeviceExt
->AvailableClustersValid
)
553 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
554 Status
= FAT12CountAvailableClusters(DeviceExt
);
555 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
556 Status
= FAT16CountAvailableClusters(DeviceExt
);
558 Status
= FAT32CountAvailableClusters(DeviceExt
);
560 if (Clusters
!= NULL
)
562 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
564 ExReleaseResourceLite (&DeviceExt
->FatResource
);
571 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
575 PDEVICE_EXTENSION DeviceExt
,
576 ULONG ClusterToWrite
,
584 LARGE_INTEGER Offset
;
589 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
);
591 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
593 _SEH2_YIELD(return _SEH2_GetExceptionCode());
596 CBlock
= (PUCHAR
)BaseAddress
;
598 FATOffset
= (ClusterToWrite
* 12) / 8;
599 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
600 NewValue
, ClusterToWrite
, FATOffset
);
601 if ((ClusterToWrite
% 2) == 0)
603 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
604 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
605 CBlock
[FATOffset
+ 1] &= 0xf0;
606 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
610 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
611 CBlock
[FATOffset
] &= 0x0f;
612 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
613 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
615 /* Write the changed FAT sector(s) to disk */
616 CcSetDirtyPinnedData(Context
, NULL
);
617 CcUnpinData(Context
);
618 return STATUS_SUCCESS
;
622 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
626 PDEVICE_EXTENSION DeviceExt
,
627 ULONG ClusterToWrite
,
635 LARGE_INTEGER Offset
;
638 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
639 FATOffset
= ClusterToWrite
* 2;
640 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
643 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
645 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
651 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
653 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
654 *OldValue
= *Cluster
;
655 *Cluster
= (USHORT
)NewValue
;
656 CcSetDirtyPinnedData(Context
, NULL
);
657 CcUnpinData(Context
);
658 return STATUS_SUCCESS
;
662 * FUNCTION: Writes a cluster to the FAT32 physical tables
666 PDEVICE_EXTENSION DeviceExt
,
667 ULONG ClusterToWrite
,
675 LARGE_INTEGER Offset
;
678 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
680 FATOffset
= (ClusterToWrite
* 4);
681 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
684 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
688 _SEH2_YIELD(return _SEH2_GetExceptionCode());
692 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
694 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
695 *OldValue
= *Cluster
& 0x0fffffff;
696 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
698 CcSetDirtyPinnedData(Context
, NULL
);
699 CcUnpinData(Context
);
701 return STATUS_SUCCESS
;
706 * FUNCTION: Write a changed FAT entry
710 PDEVICE_EXTENSION DeviceExt
,
711 ULONG ClusterToWrite
,
717 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
718 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
719 if (DeviceExt
->AvailableClustersValid
)
721 if (OldValue
&& NewValue
== 0)
722 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
723 else if (OldValue
== 0 && NewValue
)
724 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
726 ExReleaseResourceLite(&DeviceExt
->FatResource
);
731 * FUNCTION: Converts the cluster number to a sector number for this physical
736 PDEVICE_EXTENSION DeviceExt
,
739 return DeviceExt
->FatInfo
.dataStart
+
740 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
745 * FUNCTION: Retrieve the next cluster depending on the FAT type
749 PDEVICE_EXTENSION DeviceExt
,
750 ULONG CurrentCluster
,
755 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
756 DeviceExt
, CurrentCluster
);
758 if (CurrentCluster
== 0)
760 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
761 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
762 ASSERT(CurrentCluster
!= 0);
763 return STATUS_FILE_CORRUPT_ERROR
;
766 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
767 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
768 ExReleaseResourceLite(&DeviceExt
->FatResource
);
774 * FUNCTION: Retrieve the next cluster depending on the FAT type
777 GetNextClusterExtend(
778 PDEVICE_EXTENSION DeviceExt
,
779 ULONG CurrentCluster
,
785 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
786 DeviceExt
, CurrentCluster
);
788 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
790 * If the file hasn't any clusters allocated then we need special
793 if (CurrentCluster
== 0)
795 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
796 if (!NT_SUCCESS(Status
))
798 ExReleaseResourceLite(&DeviceExt
->FatResource
);
802 *NextCluster
= NewCluster
;
803 ExReleaseResourceLite(&DeviceExt
->FatResource
);
804 return STATUS_SUCCESS
;
807 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
809 if ((*NextCluster
) == 0xFFFFFFFF)
811 /* We are after last existing cluster, we must add one to file */
812 /* Firstly, find the next available open allocation unit and
813 mark it as end of file */
814 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
815 if (!NT_SUCCESS(Status
))
817 ExReleaseResourceLite(&DeviceExt
->FatResource
);
821 /* Now, write the AU of the LastCluster with the value of the newly
823 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
824 *NextCluster
= NewCluster
;
827 ExReleaseResourceLite(&DeviceExt
->FatResource
);
832 * FUNCTION: Retrieve the dirty status
836 PDEVICE_EXTENSION DeviceExt
,
837 PBOOLEAN DirtyStatus
)
841 DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt
);
843 /* FAT12 has no dirty bit */
844 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
846 *DirtyStatus
= FALSE
;
847 return STATUS_SUCCESS
;
850 /* Not really in the FAT, but share the lock because
851 * we're really low-level and shouldn't happent that often
852 * And call the appropriate function
854 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
855 Status
= DeviceExt
->GetDirtyStatus(DeviceExt
, DirtyStatus
);
856 ExReleaseResourceLite(&DeviceExt
->FatResource
);
863 PDEVICE_EXTENSION DeviceExt
,
864 PBOOLEAN DirtyStatus
)
866 LARGE_INTEGER Offset
;
868 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
873 struct _BootSector
* Sector
;
875 /* We'll read the bootsector at 0 */
877 Length
= DeviceExt
->FatInfo
.BytesPerSector
;
878 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
879 /* Go through Cc for this */
882 CcPinRead(DeviceExt
->VolumeFcb
->FileObject
, &Offset
, Length
, PIN_WAIT
, &Context
, (PVOID
*)&Sector
);
884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
886 _SEH2_YIELD(return _SEH2_GetExceptionCode());
890 /* No Cc, do it the old way:
891 * - Allocate a big enough buffer
892 * - And read the disk
894 Sector
= ExAllocatePoolWithTag(NonPagedPool
, Length
, TAG_BUFFER
);
898 return STATUS_INSUFFICIENT_RESOURCES
;
901 Status
= VfatReadDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
902 if (!NT_SUCCESS(Status
))
905 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
910 /* Make sure we have a boot sector...
911 * FIXME: This check is a bit lame and should be improved
913 if (Sector
->Signatur1
!= 0xaa55)
915 /* Set we are dirty so that we don't attempt anything */
917 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
918 CcUnpinData(Context
);
920 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
922 return STATUS_DISK_CORRUPT_ERROR
;
925 /* Return the status of the dirty bit */
926 if (Sector
->Res1
& FAT_DIRTY_BIT
)
929 *DirtyStatus
= FALSE
;
931 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
932 CcUnpinData(Context
);
934 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
936 return STATUS_SUCCESS
;
941 PDEVICE_EXTENSION DeviceExt
,
942 PBOOLEAN DirtyStatus
)
944 LARGE_INTEGER Offset
;
946 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
951 struct _BootSector32
* Sector
;
953 /* We'll read the bootsector at 0 */
955 Length
= DeviceExt
->FatInfo
.BytesPerSector
;
956 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
957 /* Go through Cc for this */
960 CcPinRead(DeviceExt
->VolumeFcb
->FileObject
, &Offset
, Length
, PIN_WAIT
, &Context
, (PVOID
*)&Sector
);
962 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
964 _SEH2_YIELD(return _SEH2_GetExceptionCode());
968 /* No Cc, do it the old way:
969 * - Allocate a big enough buffer
970 * - And read the disk
972 Sector
= ExAllocatePoolWithTag(NonPagedPool
, Length
, TAG_BUFFER
);
976 return STATUS_INSUFFICIENT_RESOURCES
;
979 Status
= VfatReadDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
980 if (!NT_SUCCESS(Status
))
983 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
988 /* Make sure we have a boot sector...
989 * FIXME: This check is a bit lame and should be improved
991 if (Sector
->Signature1
!= 0xaa55)
993 /* Set we are dirty so that we don't attempt anything */
995 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
996 CcUnpinData(Context
);
998 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1000 return STATUS_DISK_CORRUPT_ERROR
;
1003 /* Return the status of the dirty bit */
1004 if (Sector
->Res4
& FAT_DIRTY_BIT
)
1005 *DirtyStatus
= TRUE
;
1007 *DirtyStatus
= FALSE
;
1009 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1010 CcUnpinData(Context
);
1012 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1014 return STATUS_SUCCESS
;
1018 * FUNCTION: Set the dirty status
1022 PDEVICE_EXTENSION DeviceExt
,
1023 BOOLEAN DirtyStatus
)
1027 DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt
, DirtyStatus
);
1029 /* FAT12 has no dirty bit */
1030 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
1032 return STATUS_SUCCESS
;
1035 /* Not really in the FAT, but share the lock because
1036 * we're really low-level and shouldn't happent that often
1037 * And call the appropriate function
1038 * Acquire exclusive because we will modify ondisk value
1040 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
1041 Status
= DeviceExt
->SetDirtyStatus(DeviceExt
, DirtyStatus
);
1042 ExReleaseResourceLite(&DeviceExt
->FatResource
);
1048 FAT16SetDirtyStatus(
1049 PDEVICE_EXTENSION DeviceExt
,
1050 BOOLEAN DirtyStatus
)
1052 LARGE_INTEGER Offset
;
1054 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1059 struct _BootSector
* Sector
;
1061 /* We'll read (and then write) the bootsector at 0 */
1062 Offset
.QuadPart
= 0;
1063 Length
= DeviceExt
->FatInfo
.BytesPerSector
;
1064 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1065 /* Go through Cc for this */
1068 CcPinRead(DeviceExt
->VolumeFcb
->FileObject
, &Offset
, Length
, PIN_WAIT
, &Context
, (PVOID
*)&Sector
);
1070 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1072 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1076 /* No Cc, do it the old way:
1077 * - Allocate a big enough buffer
1078 * - And read the disk
1080 Sector
= ExAllocatePoolWithTag(NonPagedPool
, Length
, TAG_BUFFER
);
1083 return STATUS_INSUFFICIENT_RESOURCES
;
1086 Status
= VfatReadDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1087 if (!NT_SUCCESS(Status
))
1089 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1094 /* Make sure we have a boot sector...
1095 * FIXME: This check is a bit lame and should be improved
1097 if (Sector
->Signatur1
!= 0xaa55)
1099 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1100 CcUnpinData(Context
);
1102 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1104 return STATUS_DISK_CORRUPT_ERROR
;
1107 /* Modify the dirty bit status according
1112 Sector
->Res1
&= ~FAT_DIRTY_BIT
;
1116 Sector
->Res1
|= FAT_DIRTY_BIT
;
1119 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1120 /* Mark boot sector dirty so that it gets written to the disk */
1121 CcSetDirtyPinnedData(Context
, NULL
);
1122 CcUnpinData(Context
);
1123 return STATUS_SUCCESS
;
1125 /* Write back the boot sector to the disk */
1126 Status
= VfatWriteDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1127 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1133 FAT32SetDirtyStatus(
1134 PDEVICE_EXTENSION DeviceExt
,
1135 BOOLEAN DirtyStatus
)
1137 LARGE_INTEGER Offset
;
1139 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1144 struct _BootSector32
* Sector
;
1146 /* We'll read (and then write) the bootsector at 0 */
1147 Offset
.QuadPart
= 0;
1148 Length
= DeviceExt
->FatInfo
.BytesPerSector
;
1149 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1150 /* Go through Cc for this */
1153 CcPinRead(DeviceExt
->VolumeFcb
->FileObject
, &Offset
, Length
, PIN_WAIT
, &Context
, (PVOID
*)&Sector
);
1155 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1157 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1161 /* No Cc, do it the old way:
1162 * - Allocate a big enough buffer
1163 * - And read the disk
1165 Sector
= ExAllocatePoolWithTag(NonPagedPool
, Length
, TAG_BUFFER
);
1168 return STATUS_INSUFFICIENT_RESOURCES
;
1171 Status
= VfatReadDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1172 if (!NT_SUCCESS(Status
))
1174 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1179 /* Make sure we have a boot sector...
1180 * FIXME: This check is a bit lame and should be improved
1182 if (Sector
->Signature1
!= 0xaa55)
1185 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1186 CcUnpinData(Context
);
1188 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1190 return STATUS_DISK_CORRUPT_ERROR
;
1193 /* Modify the dirty bit status according
1198 Sector
->Res4
&= ~FAT_DIRTY_BIT
;
1202 Sector
->Res4
|= FAT_DIRTY_BIT
;
1205 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1206 /* Mark boot sector dirty so that it gets written to the disk */
1207 CcSetDirtyPinnedData(Context
, NULL
);
1208 CcUnpinData(Context
);
1209 return STATUS_SUCCESS
;
1211 /* Write back the boot sector to the disk */
1212 Status
= VfatWriteDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1213 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1219 FAT32UpdateFreeClustersCount(
1220 PDEVICE_EXTENSION DeviceExt
)
1222 LARGE_INTEGER Offset
;
1224 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1229 struct _FsInfoSector
* Sector
;
1231 if (!DeviceExt
->AvailableClustersValid
)
1233 return STATUS_INVALID_PARAMETER
;
1236 /* We'll read (and then write) the fsinfo sector */
1237 Offset
.QuadPart
= DeviceExt
->FatInfo
.FSInfoSector
* DeviceExt
->FatInfo
.BytesPerSector
;
1238 Length
= DeviceExt
->FatInfo
.BytesPerSector
;
1239 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1240 /* Go through Cc for this */
1243 CcPinRead(DeviceExt
->VolumeFcb
->FileObject
, &Offset
, Length
, PIN_WAIT
, &Context
, (PVOID
*)&Sector
);
1245 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1247 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1251 /* No Cc, do it the old way:
1252 * - Allocate a big enough buffer
1253 * - And read the disk
1255 Sector
= ExAllocatePoolWithTag(NonPagedPool
, Length
, TAG_BUFFER
);
1258 return STATUS_INSUFFICIENT_RESOURCES
;
1261 Status
= VfatReadDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1262 if (!NT_SUCCESS(Status
))
1264 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1269 /* Make sure we have a FSINFO sector */
1270 if (Sector
->ExtBootSignature2
!= 0x41615252 ||
1271 Sector
->FSINFOSignature
!= 0x61417272 ||
1272 Sector
->Signatur2
!= 0xaa550000)
1275 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1276 CcUnpinData(Context
);
1278 ExFreePoolWithTag(Sector
, TAG_BUFFER
);
1280 return STATUS_DISK_CORRUPT_ERROR
;
1283 /* Update the free clusters count */
1284 Sector
->FreeCluster
= InterlockedCompareExchange((PLONG
)&DeviceExt
->AvailableClusters
, 0, 0);
1286 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1287 /* Mark FSINFO sector dirty so that it gets written to the disk */
1288 CcSetDirtyPinnedData(Context
, NULL
);
1289 CcUnpinData(Context
);
1290 return STATUS_SUCCESS
;
1292 /* Write back the FSINFO sector to the disk */
1293 Status
= VfatWriteDisk(DeviceExt
->StorageDevice
, &Offset
, Length
, (PUCHAR
)Sector
, FALSE
);
1294 ExFreePoolWithTag(Sector
, TAG_BUFFER
);