2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/fat.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
10 /* INCLUDES *****************************************************************/
17 /* GLOBALS ******************************************************************/
19 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
20 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
22 /* FUNCTIONS ****************************************************************/
25 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
30 PDEVICE_EXTENSION DeviceExt
,
34 NTSTATUS Status
= STATUS_SUCCESS
;
41 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
42 FATOffset
= CurrentCluster
* sizeof(ULONG
);
43 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
46 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
48 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
50 _SEH2_YIELD(return _SEH2_GetExceptionCode());
54 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
55 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
56 CurrentCluster
= 0xffffffff;
58 if (CurrentCluster
== 0)
60 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
61 Status
= STATUS_FILE_CORRUPT_ERROR
;
62 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
63 ASSERT(CurrentCluster
!= 0);
66 *NextCluster
= CurrentCluster
;
71 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
75 PDEVICE_EXTENSION DeviceExt
,
79 NTSTATUS Status
= STATUS_SUCCESS
;
86 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
87 FATOffset
= CurrentCluster
* 2;
88 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
91 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
93 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
95 _SEH2_YIELD(return _SEH2_GetExceptionCode());
99 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
100 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
101 CurrentCluster
= 0xffffffff;
103 if (CurrentCluster
== 0)
105 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
106 Status
= STATUS_FILE_CORRUPT_ERROR
;
107 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
108 ASSERT(CurrentCluster
!= 0);
111 CcUnpinData(Context
);
112 *NextCluster
= CurrentCluster
;
117 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
121 PDEVICE_EXTENSION DeviceExt
,
122 ULONG CurrentCluster
,
129 LARGE_INTEGER Offset
;
136 CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
);
138 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
140 _SEH2_YIELD(return _SEH2_GetExceptionCode());
144 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
145 if ((CurrentCluster
% 2) == 0)
147 Entry
= *CBlock
& 0x0fff;
151 Entry
= *CBlock
>> 4;
154 // DPRINT("Entry %x\n",Entry);
155 if (Entry
>= 0xff8 && Entry
<= 0xfff)
158 // DPRINT("Returning %x\n",Entry);
160 *NextCluster
= Entry
;
161 CcUnpinData(Context
);
162 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
163 return STATUS_SUCCESS
;
167 * FUNCTION: Finds the first available cluster in a FAT16 table
170 FAT16FindAndMarkAvailableCluster(
171 PDEVICE_EXTENSION DeviceExt
,
180 LARGE_INTEGER Offset
;
184 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
185 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
187 StartCluster
= DeviceExt
->LastAvailableCluster
;
189 for (j
= 0; j
< 2; j
++)
191 for (i
= StartCluster
; i
< FatLength
;)
193 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
196 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
200 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
201 _SEH2_YIELD(return _SEH2_GetExceptionCode());
205 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
206 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
208 /* Now process the whole block */
209 while (Block
< BlockEnd
&& i
< FatLength
)
213 DPRINT("Found available cluster 0x%x\n", i
);
214 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
216 CcSetDirtyPinnedData(Context
, NULL
);
217 CcUnpinData(Context
);
218 if (DeviceExt
->AvailableClustersValid
)
219 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
220 return STATUS_SUCCESS
;
227 CcUnpinData(Context
);
230 FatLength
= StartCluster
;
234 return STATUS_DISK_FULL
;
238 * FUNCTION: Finds the first available cluster in a FAT12 table
241 FAT12FindAndMarkAvailableCluster(
242 PDEVICE_EXTENSION DeviceExt
,
252 LARGE_INTEGER Offset
;
254 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
256 StartCluster
= DeviceExt
->LastAvailableCluster
;
260 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
);
262 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
264 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
265 _SEH2_YIELD(return _SEH2_GetExceptionCode());
269 for (j
= 0; j
< 2; j
++)
271 for (i
= StartCluster
; i
< FatLength
; i
++)
273 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
276 Entry
= *CBlock
& 0xfff;
280 Entry
= *CBlock
>> 4;
285 DPRINT("Found available cluster 0x%x\n", i
);
286 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
288 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
290 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
291 CcSetDirtyPinnedData(Context
, NULL
);
292 CcUnpinData(Context
);
293 if (DeviceExt
->AvailableClustersValid
)
294 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
295 return STATUS_SUCCESS
;
298 FatLength
= StartCluster
;
301 CcUnpinData(Context
);
302 return STATUS_DISK_FULL
;
306 * FUNCTION: Finds the first available cluster in a FAT32 table
309 FAT32FindAndMarkAvailableCluster(
310 PDEVICE_EXTENSION DeviceExt
,
319 LARGE_INTEGER Offset
;
323 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
324 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
326 StartCluster
= DeviceExt
->LastAvailableCluster
;
328 for (j
= 0; j
< 2; j
++)
330 for (i
= StartCluster
; i
< FatLength
;)
332 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
335 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
339 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
340 _SEH2_YIELD(return _SEH2_GetExceptionCode());
343 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
344 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
346 /* Now process the whole block */
347 while (Block
< BlockEnd
&& i
< FatLength
)
349 if ((*Block
& 0x0fffffff) == 0)
351 DPRINT("Found available cluster 0x%x\n", i
);
352 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
354 CcSetDirtyPinnedData(Context
, NULL
);
355 CcUnpinData(Context
);
356 if (DeviceExt
->AvailableClustersValid
)
357 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
358 return STATUS_SUCCESS
;
365 CcUnpinData(Context
);
367 FatLength
= StartCluster
;
370 return STATUS_DISK_FULL
;
374 * FUNCTION: Counts free cluster in a FAT12 table
378 FAT12CountAvailableClusters(
379 PDEVICE_EXTENSION DeviceExt
)
385 ULONG numberofclusters
;
386 LARGE_INTEGER Offset
;
393 CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
);
395 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
401 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
403 for (i
= 2; i
< numberofclusters
; i
++)
405 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
408 Entry
= *CBlock
& 0x0fff;
412 Entry
= *CBlock
>> 4;
419 CcUnpinData(Context
);
420 DeviceExt
->AvailableClusters
= ulCount
;
421 DeviceExt
->AvailableClustersValid
= TRUE
;
423 return STATUS_SUCCESS
;
428 * FUNCTION: Counts free clusters in a FAT16 table
432 FAT16CountAvailableClusters(
433 PDEVICE_EXTENSION DeviceExt
)
437 PVOID BaseAddress
= NULL
;
441 PVOID Context
= NULL
;
442 LARGE_INTEGER Offset
;
445 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
446 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
448 for (i
= 2; i
< FatLength
; )
450 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
453 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
455 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
457 _SEH2_YIELD(return _SEH2_GetExceptionCode());
460 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
461 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
463 /* Now process the whole block */
464 while (Block
< BlockEnd
&& i
< FatLength
)
472 CcUnpinData(Context
);
475 DeviceExt
->AvailableClusters
= ulCount
;
476 DeviceExt
->AvailableClustersValid
= TRUE
;
478 return STATUS_SUCCESS
;
483 * FUNCTION: Counts free clusters in a FAT32 table
487 FAT32CountAvailableClusters(
488 PDEVICE_EXTENSION DeviceExt
)
492 PVOID BaseAddress
= NULL
;
496 PVOID Context
= NULL
;
497 LARGE_INTEGER Offset
;
500 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
501 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
503 for (i
= 2; i
< FatLength
; )
505 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
508 CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
);
510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
512 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
513 _SEH2_YIELD(return _SEH2_GetExceptionCode());
516 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
517 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
519 /* Now process the whole block */
520 while (Block
< BlockEnd
&& i
< FatLength
)
522 if ((*Block
& 0x0fffffff) == 0)
528 CcUnpinData(Context
);
531 DeviceExt
->AvailableClusters
= ulCount
;
532 DeviceExt
->AvailableClustersValid
= TRUE
;
534 return STATUS_SUCCESS
;
538 CountAvailableClusters(
539 PDEVICE_EXTENSION DeviceExt
,
540 PLARGE_INTEGER Clusters
)
542 NTSTATUS Status
= STATUS_SUCCESS
;
543 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
544 if (!DeviceExt
->AvailableClustersValid
)
546 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
547 Status
= FAT12CountAvailableClusters(DeviceExt
);
548 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
549 Status
= FAT16CountAvailableClusters(DeviceExt
);
551 Status
= FAT32CountAvailableClusters(DeviceExt
);
553 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
554 ExReleaseResourceLite (&DeviceExt
->FatResource
);
561 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
565 PDEVICE_EXTENSION DeviceExt
,
566 ULONG ClusterToWrite
,
574 LARGE_INTEGER Offset
;
579 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
);
581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
583 _SEH2_YIELD(return _SEH2_GetExceptionCode());
586 CBlock
= (PUCHAR
)BaseAddress
;
588 FATOffset
= (ClusterToWrite
* 12) / 8;
589 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
590 NewValue
, ClusterToWrite
, FATOffset
);
591 if ((ClusterToWrite
% 2) == 0)
593 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
594 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
595 CBlock
[FATOffset
+ 1] &= 0xf0;
596 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
600 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
601 CBlock
[FATOffset
] &= 0x0f;
602 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
603 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
605 /* Write the changed FAT sector(s) to disk */
606 CcSetDirtyPinnedData(Context
, NULL
);
607 CcUnpinData(Context
);
608 return STATUS_SUCCESS
;
612 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
616 PDEVICE_EXTENSION DeviceExt
,
617 ULONG ClusterToWrite
,
625 LARGE_INTEGER Offset
;
628 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
629 FATOffset
= ClusterToWrite
* 2;
630 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
633 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
635 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
637 _SEH2_YIELD(return _SEH2_GetExceptionCode());
641 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
643 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
644 *OldValue
= *Cluster
;
645 *Cluster
= (USHORT
)NewValue
;
646 CcSetDirtyPinnedData(Context
, NULL
);
647 CcUnpinData(Context
);
648 return STATUS_SUCCESS
;
652 * FUNCTION: Writes a cluster to the FAT32 physical tables
656 PDEVICE_EXTENSION DeviceExt
,
657 ULONG ClusterToWrite
,
665 LARGE_INTEGER Offset
;
668 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
670 FATOffset
= (ClusterToWrite
* 4);
671 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
674 CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
);
676 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
678 _SEH2_YIELD(return _SEH2_GetExceptionCode());
682 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
684 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
685 *OldValue
= *Cluster
& 0x0fffffff;
686 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
688 CcSetDirtyPinnedData(Context
, NULL
);
689 CcUnpinData(Context
);
691 return STATUS_SUCCESS
;
696 * FUNCTION: Write a changed FAT entry
700 PDEVICE_EXTENSION DeviceExt
,
701 ULONG ClusterToWrite
,
707 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
708 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
709 if (DeviceExt
->AvailableClustersValid
)
711 if (OldValue
&& NewValue
== 0)
712 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
713 else if (OldValue
== 0 && NewValue
)
714 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
716 ExReleaseResourceLite(&DeviceExt
->FatResource
);
721 * FUNCTION: Converts the cluster number to a sector number for this physical
726 PDEVICE_EXTENSION DeviceExt
,
729 return DeviceExt
->FatInfo
.dataStart
+
730 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
735 * FUNCTION: Retrieve the next cluster depending on the FAT type
739 PDEVICE_EXTENSION DeviceExt
,
740 ULONG CurrentCluster
,
745 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
746 DeviceExt
, CurrentCluster
);
748 if (CurrentCluster
== 0)
750 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
751 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
752 ASSERT(CurrentCluster
!= 0);
753 return STATUS_FILE_CORRUPT_ERROR
;
756 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
757 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
758 ExReleaseResourceLite(&DeviceExt
->FatResource
);
764 * FUNCTION: Retrieve the next cluster depending on the FAT type
767 GetNextClusterExtend(
768 PDEVICE_EXTENSION DeviceExt
,
769 ULONG CurrentCluster
,
775 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
776 DeviceExt
, CurrentCluster
);
778 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
780 * If the file hasn't any clusters allocated then we need special
783 if (CurrentCluster
== 0)
785 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
786 if (!NT_SUCCESS(Status
))
788 ExReleaseResourceLite(&DeviceExt
->FatResource
);
792 *NextCluster
= NewCluster
;
793 ExReleaseResourceLite(&DeviceExt
->FatResource
);
794 return STATUS_SUCCESS
;
797 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
799 if ((*NextCluster
) == 0xFFFFFFFF)
801 /* We are after last existing cluster, we must add one to file */
802 /* Firstly, find the next available open allocation unit and
803 mark it as end of file */
804 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
805 if (!NT_SUCCESS(Status
))
807 ExReleaseResourceLite(&DeviceExt
->FatResource
);
811 /* Now, write the AU of the LastCluster with the value of the newly
813 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
814 *NextCluster
= NewCluster
;
817 ExReleaseResourceLite(&DeviceExt
->FatResource
);