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
);
44 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
46 return STATUS_UNSUCCESSFUL
;
49 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
50 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
51 CurrentCluster
= 0xffffffff;
53 if (CurrentCluster
== 0)
55 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
56 Status
= STATUS_FILE_CORRUPT_ERROR
;
57 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
58 ASSERT(CurrentCluster
!= 0);
61 *NextCluster
= CurrentCluster
;
66 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
70 PDEVICE_EXTENSION DeviceExt
,
74 NTSTATUS Status
= STATUS_SUCCESS
;
81 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
82 FATOffset
= CurrentCluster
* 2;
83 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
84 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
86 return STATUS_UNSUCCESSFUL
;
89 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
90 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
91 CurrentCluster
= 0xffffffff;
93 if (CurrentCluster
== 0)
95 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
96 Status
= STATUS_FILE_CORRUPT_ERROR
;
97 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
98 ASSERT(CurrentCluster
!= 0);
101 CcUnpinData(Context
);
102 *NextCluster
= CurrentCluster
;
107 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
111 PDEVICE_EXTENSION DeviceExt
,
112 ULONG CurrentCluster
,
119 LARGE_INTEGER Offset
;
124 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
))
126 return STATUS_UNSUCCESSFUL
;
129 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
130 if ((CurrentCluster
% 2) == 0)
132 Entry
= *CBlock
& 0x0fff;
136 Entry
= *CBlock
>> 4;
139 // DPRINT("Entry %x\n",Entry);
140 if (Entry
>= 0xff8 && Entry
<= 0xfff)
143 // DPRINT("Returning %x\n",Entry);
145 *NextCluster
= Entry
;
146 CcUnpinData(Context
);
147 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
148 return STATUS_SUCCESS
;
152 * FUNCTION: Finds the first available cluster in a FAT16 table
155 FAT16FindAndMarkAvailableCluster(
156 PDEVICE_EXTENSION DeviceExt
,
165 LARGE_INTEGER Offset
;
169 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
170 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
172 StartCluster
= DeviceExt
->LastAvailableCluster
;
174 for (j
= 0; j
< 2; j
++)
176 for (i
= StartCluster
; i
< FatLength
;)
178 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
179 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
))
181 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
182 return STATUS_UNSUCCESSFUL
;
185 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
186 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
188 /* Now process the whole block */
189 while (Block
< BlockEnd
&& i
< FatLength
)
193 DPRINT("Found available cluster 0x%x\n", i
);
194 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
196 CcSetDirtyPinnedData(Context
, NULL
);
197 CcUnpinData(Context
);
198 if (DeviceExt
->AvailableClustersValid
)
199 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
200 return STATUS_SUCCESS
;
207 CcUnpinData(Context
);
210 FatLength
= StartCluster
;
214 return STATUS_DISK_FULL
;
218 * FUNCTION: Finds the first available cluster in a FAT12 table
221 FAT12FindAndMarkAvailableCluster(
222 PDEVICE_EXTENSION DeviceExt
,
232 LARGE_INTEGER Offset
;
234 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
236 StartCluster
= DeviceExt
->LastAvailableCluster
;
238 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
))
240 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
241 return STATUS_UNSUCCESSFUL
;
244 for (j
= 0; j
< 2; j
++)
246 for (i
= StartCluster
; i
< FatLength
; i
++)
248 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
251 Entry
= *CBlock
& 0xfff;
255 Entry
= *CBlock
>> 4;
260 DPRINT("Found available cluster 0x%x\n", i
);
261 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
263 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
265 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
266 CcSetDirtyPinnedData(Context
, NULL
);
267 CcUnpinData(Context
);
268 if (DeviceExt
->AvailableClustersValid
)
269 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
270 return STATUS_SUCCESS
;
273 FatLength
= StartCluster
;
276 CcUnpinData(Context
);
277 return STATUS_DISK_FULL
;
281 * FUNCTION: Finds the first available cluster in a FAT32 table
284 FAT32FindAndMarkAvailableCluster(
285 PDEVICE_EXTENSION DeviceExt
,
294 LARGE_INTEGER Offset
;
298 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
299 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
301 StartCluster
= DeviceExt
->LastAvailableCluster
;
303 for (j
= 0; j
< 2; j
++)
305 for (i
= StartCluster
; i
< FatLength
;)
307 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
308 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
))
310 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
311 return STATUS_UNSUCCESSFUL
;
313 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
314 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
316 /* Now process the whole block */
317 while (Block
< BlockEnd
&& i
< FatLength
)
319 if ((*Block
& 0x0fffffff) == 0)
321 DPRINT("Found available cluster 0x%x\n", i
);
322 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
324 CcSetDirtyPinnedData(Context
, NULL
);
325 CcUnpinData(Context
);
326 if (DeviceExt
->AvailableClustersValid
)
327 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
328 return STATUS_SUCCESS
;
335 CcUnpinData(Context
);
337 FatLength
= StartCluster
;
340 return STATUS_DISK_FULL
;
344 * FUNCTION: Counts free cluster in a FAT12 table
348 FAT12CountAvailableClusters(
349 PDEVICE_EXTENSION DeviceExt
)
355 ULONG numberofclusters
;
356 LARGE_INTEGER Offset
;
361 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, MAP_WAIT
, &Context
, &BaseAddress
))
363 return STATUS_UNSUCCESSFUL
;
366 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
368 for (i
= 2; i
< numberofclusters
; i
++)
370 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
373 Entry
= *CBlock
& 0x0fff;
377 Entry
= *CBlock
>> 4;
384 CcUnpinData(Context
);
385 DeviceExt
->AvailableClusters
= ulCount
;
386 DeviceExt
->AvailableClustersValid
= TRUE
;
388 return STATUS_SUCCESS
;
393 * FUNCTION: Counts free clusters in a FAT16 table
397 FAT16CountAvailableClusters(
398 PDEVICE_EXTENSION DeviceExt
)
402 PVOID BaseAddress
= NULL
;
406 PVOID Context
= NULL
;
407 LARGE_INTEGER Offset
;
410 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
411 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
413 for (i
= 2; i
< FatLength
; )
415 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
416 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
418 return STATUS_UNSUCCESSFUL
;
420 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
421 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
423 /* Now process the whole block */
424 while (Block
< BlockEnd
&& i
< FatLength
)
432 CcUnpinData(Context
);
435 DeviceExt
->AvailableClusters
= ulCount
;
436 DeviceExt
->AvailableClustersValid
= TRUE
;
438 return STATUS_SUCCESS
;
443 * FUNCTION: Counts free clusters in a FAT32 table
447 FAT32CountAvailableClusters(
448 PDEVICE_EXTENSION DeviceExt
)
452 PVOID BaseAddress
= NULL
;
456 PVOID Context
= NULL
;
457 LARGE_INTEGER Offset
;
460 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
461 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
463 for (i
= 2; i
< FatLength
; )
465 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
466 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
468 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
469 return STATUS_UNSUCCESSFUL
;
471 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
472 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
474 /* Now process the whole block */
475 while (Block
< BlockEnd
&& i
< FatLength
)
477 if ((*Block
& 0x0fffffff) == 0)
483 CcUnpinData(Context
);
486 DeviceExt
->AvailableClusters
= ulCount
;
487 DeviceExt
->AvailableClustersValid
= TRUE
;
489 return STATUS_SUCCESS
;
493 CountAvailableClusters(
494 PDEVICE_EXTENSION DeviceExt
,
495 PLARGE_INTEGER Clusters
)
497 NTSTATUS Status
= STATUS_SUCCESS
;
498 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
499 if (!DeviceExt
->AvailableClustersValid
)
501 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
502 Status
= FAT12CountAvailableClusters(DeviceExt
);
503 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
504 Status
= FAT16CountAvailableClusters(DeviceExt
);
506 Status
= FAT32CountAvailableClusters(DeviceExt
);
508 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
509 ExReleaseResourceLite (&DeviceExt
->FatResource
);
516 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
520 PDEVICE_EXTENSION DeviceExt
,
521 ULONG ClusterToWrite
,
529 LARGE_INTEGER Offset
;
532 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, PIN_WAIT
, &Context
, &BaseAddress
))
534 return STATUS_UNSUCCESSFUL
;
536 CBlock
= (PUCHAR
)BaseAddress
;
538 FATOffset
= (ClusterToWrite
* 12) / 8;
539 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
540 NewValue
, ClusterToWrite
, FATOffset
);
541 if ((ClusterToWrite
% 2) == 0)
543 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
544 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
545 CBlock
[FATOffset
+ 1] &= 0xf0;
546 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
550 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
551 CBlock
[FATOffset
] &= 0x0f;
552 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
553 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
555 /* Write the changed FAT sector(s) to disk */
556 CcSetDirtyPinnedData(Context
, NULL
);
557 CcUnpinData(Context
);
558 return STATUS_SUCCESS
;
562 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
566 PDEVICE_EXTENSION DeviceExt
,
567 ULONG ClusterToWrite
,
575 LARGE_INTEGER Offset
;
578 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
579 FATOffset
= ClusterToWrite
* 2;
580 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
581 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
))
583 return STATUS_UNSUCCESSFUL
;
586 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
588 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
589 *OldValue
= *Cluster
;
590 *Cluster
= (USHORT
)NewValue
;
591 CcSetDirtyPinnedData(Context
, NULL
);
592 CcUnpinData(Context
);
593 return STATUS_SUCCESS
;
597 * FUNCTION: Writes a cluster to the FAT32 physical tables
601 PDEVICE_EXTENSION DeviceExt
,
602 ULONG ClusterToWrite
,
610 LARGE_INTEGER Offset
;
613 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
615 FATOffset
= (ClusterToWrite
* 4);
616 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
617 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, PIN_WAIT
, &Context
, &BaseAddress
))
619 return STATUS_UNSUCCESSFUL
;
622 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
624 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
625 *OldValue
= *Cluster
& 0x0fffffff;
626 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
628 CcSetDirtyPinnedData(Context
, NULL
);
629 CcUnpinData(Context
);
631 return STATUS_SUCCESS
;
636 * FUNCTION: Write a changed FAT entry
640 PDEVICE_EXTENSION DeviceExt
,
641 ULONG ClusterToWrite
,
647 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
648 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
649 if (DeviceExt
->AvailableClustersValid
)
651 if (OldValue
&& NewValue
== 0)
652 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
653 else if (OldValue
== 0 && NewValue
)
654 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
656 ExReleaseResourceLite(&DeviceExt
->FatResource
);
661 * FUNCTION: Converts the cluster number to a sector number for this physical
666 PDEVICE_EXTENSION DeviceExt
,
669 return DeviceExt
->FatInfo
.dataStart
+
670 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
675 * FUNCTION: Retrieve the next cluster depending on the FAT type
679 PDEVICE_EXTENSION DeviceExt
,
680 ULONG CurrentCluster
,
685 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
686 DeviceExt
, CurrentCluster
);
688 if (CurrentCluster
== 0)
690 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
691 if (VfatGlobalData
->Flags
& VFAT_BREAK_ON_CORRUPTION
)
692 ASSERT(CurrentCluster
!= 0);
693 return STATUS_FILE_CORRUPT_ERROR
;
696 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
697 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
698 ExReleaseResourceLite(&DeviceExt
->FatResource
);
704 * FUNCTION: Retrieve the next cluster depending on the FAT type
707 GetNextClusterExtend(
708 PDEVICE_EXTENSION DeviceExt
,
709 ULONG CurrentCluster
,
715 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
716 DeviceExt
, CurrentCluster
);
718 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
720 * If the file hasn't any clusters allocated then we need special
723 if (CurrentCluster
== 0)
725 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
726 if (!NT_SUCCESS(Status
))
728 ExReleaseResourceLite(&DeviceExt
->FatResource
);
732 *NextCluster
= NewCluster
;
733 ExReleaseResourceLite(&DeviceExt
->FatResource
);
734 return STATUS_SUCCESS
;
737 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
739 if ((*NextCluster
) == 0xFFFFFFFF)
741 /* We are after last existing cluster, we must add one to file */
742 /* Firstly, find the next available open allocation unit and
743 mark it as end of file */
744 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
745 if (!NT_SUCCESS(Status
))
747 ExReleaseResourceLite(&DeviceExt
->FatResource
);
751 /* Now, write the AU of the LastCluster with the value of the newly
753 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
754 *NextCluster
= NewCluster
;
757 ExReleaseResourceLite(&DeviceExt
->FatResource
);