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
,
40 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
41 FATOffset
= CurrentCluster
* sizeof(ULONG
);
42 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
43 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
45 return STATUS_UNSUCCESSFUL
;
48 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
49 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
50 CurrentCluster
= 0xffffffff;
53 *NextCluster
= CurrentCluster
;
54 return STATUS_SUCCESS
;
58 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
62 PDEVICE_EXTENSION DeviceExt
,
72 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
73 FATOffset
= CurrentCluster
* 2;
74 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
75 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
77 return STATUS_UNSUCCESSFUL
;
80 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
81 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
82 CurrentCluster
= 0xffffffff;
84 *NextCluster
= CurrentCluster
;
85 return STATUS_SUCCESS
;
89 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
93 PDEVICE_EXTENSION DeviceExt
,
101 LARGE_INTEGER Offset
;
106 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
108 return STATUS_UNSUCCESSFUL
;
111 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
112 if ((CurrentCluster
% 2) == 0)
114 Entry
= *CBlock
& 0x0fff;
118 Entry
= *CBlock
>> 4;
121 // DPRINT("Entry %x\n",Entry);
122 if (Entry
>= 0xff8 && Entry
<= 0xfff)
125 // DPRINT("Returning %x\n",Entry);
126 *NextCluster
= Entry
;
127 CcUnpinData(Context
);
128 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
129 return STATUS_SUCCESS
;
133 * FUNCTION: Finds the first available cluster in a FAT16 table
136 FAT16FindAndMarkAvailableCluster(
137 PDEVICE_EXTENSION DeviceExt
,
146 LARGE_INTEGER Offset
;
150 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
151 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
153 StartCluster
= DeviceExt
->LastAvailableCluster
;
155 for (j
= 0; j
< 2; j
++)
157 for (i
= StartCluster
; i
< FatLength
;)
159 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
160 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
162 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
163 return STATUS_UNSUCCESSFUL
;
166 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
167 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
169 /* Now process the whole block */
170 while (Block
< BlockEnd
&& i
< FatLength
)
174 DPRINT("Found available cluster 0x%x\n", i
);
175 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
177 CcSetDirtyPinnedData(Context
, NULL
);
178 CcUnpinData(Context
);
179 if (DeviceExt
->AvailableClustersValid
)
180 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
181 return STATUS_SUCCESS
;
188 CcUnpinData(Context
);
191 FatLength
= StartCluster
;
195 return STATUS_DISK_FULL
;
199 * FUNCTION: Finds the first available cluster in a FAT12 table
202 FAT12FindAndMarkAvailableCluster(
203 PDEVICE_EXTENSION DeviceExt
,
213 LARGE_INTEGER Offset
;
215 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
217 StartCluster
= DeviceExt
->LastAvailableCluster
;
219 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
221 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
222 return STATUS_UNSUCCESSFUL
;
225 for (j
= 0; j
< 2; j
++)
227 for (i
= StartCluster
; i
< FatLength
; i
++)
229 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
232 Entry
= *CBlock
& 0xfff;
236 Entry
= *CBlock
>> 4;
241 DPRINT("Found available cluster 0x%x\n", i
);
242 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
244 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
246 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
247 CcSetDirtyPinnedData(Context
, NULL
);
248 CcUnpinData(Context
);
249 if (DeviceExt
->AvailableClustersValid
)
250 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
251 return STATUS_SUCCESS
;
254 FatLength
= StartCluster
;
257 CcUnpinData(Context
);
258 return STATUS_DISK_FULL
;
262 * FUNCTION: Finds the first available cluster in a FAT32 table
265 FAT32FindAndMarkAvailableCluster(
266 PDEVICE_EXTENSION DeviceExt
,
275 LARGE_INTEGER Offset
;
279 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
280 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
282 StartCluster
= DeviceExt
->LastAvailableCluster
;
284 for (j
= 0; j
< 2; j
++)
286 for (i
= StartCluster
; i
< FatLength
;)
288 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
289 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
291 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
292 return STATUS_UNSUCCESSFUL
;
294 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
295 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
297 /* Now process the whole block */
298 while (Block
< BlockEnd
&& i
< FatLength
)
300 if ((*Block
& 0x0fffffff) == 0)
302 DPRINT("Found available cluster 0x%x\n", i
);
303 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
305 CcSetDirtyPinnedData(Context
, NULL
);
306 CcUnpinData(Context
);
307 if (DeviceExt
->AvailableClustersValid
)
308 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
309 return STATUS_SUCCESS
;
316 CcUnpinData(Context
);
318 FatLength
= StartCluster
;
321 return STATUS_DISK_FULL
;
325 * FUNCTION: Counts free cluster in a FAT12 table
329 FAT12CountAvailableClusters(
330 PDEVICE_EXTENSION DeviceExt
)
336 ULONG numberofclusters
;
337 LARGE_INTEGER Offset
;
342 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
344 return STATUS_UNSUCCESSFUL
;
347 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
349 for (i
= 2; i
< numberofclusters
; i
++)
351 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
354 Entry
= *CBlock
& 0x0fff;
358 Entry
= *CBlock
>> 4;
365 CcUnpinData(Context
);
366 DeviceExt
->AvailableClusters
= ulCount
;
367 DeviceExt
->AvailableClustersValid
= TRUE
;
369 return STATUS_SUCCESS
;
374 * FUNCTION: Counts free clusters in a FAT16 table
378 FAT16CountAvailableClusters(
379 PDEVICE_EXTENSION DeviceExt
)
383 PVOID BaseAddress
= NULL
;
387 PVOID Context
= NULL
;
388 LARGE_INTEGER Offset
;
391 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
392 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
394 for (i
= 2; i
< FatLength
; )
396 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
397 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
399 return STATUS_UNSUCCESSFUL
;
401 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
402 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
404 /* Now process the whole block */
405 while (Block
< BlockEnd
&& i
< FatLength
)
413 CcUnpinData(Context
);
416 DeviceExt
->AvailableClusters
= ulCount
;
417 DeviceExt
->AvailableClustersValid
= TRUE
;
419 return STATUS_SUCCESS
;
424 * FUNCTION: Counts free clusters in a FAT32 table
428 FAT32CountAvailableClusters(
429 PDEVICE_EXTENSION DeviceExt
)
433 PVOID BaseAddress
= NULL
;
437 PVOID Context
= NULL
;
438 LARGE_INTEGER Offset
;
441 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
442 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
444 for (i
= 2; i
< FatLength
; )
446 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
447 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
449 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
450 return STATUS_UNSUCCESSFUL
;
452 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
453 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
455 /* Now process the whole block */
456 while (Block
< BlockEnd
&& i
< FatLength
)
458 if ((*Block
& 0x0fffffff) == 0)
464 CcUnpinData(Context
);
467 DeviceExt
->AvailableClusters
= ulCount
;
468 DeviceExt
->AvailableClustersValid
= TRUE
;
470 return STATUS_SUCCESS
;
474 CountAvailableClusters(
475 PDEVICE_EXTENSION DeviceExt
,
476 PLARGE_INTEGER Clusters
)
478 NTSTATUS Status
= STATUS_SUCCESS
;
479 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
480 if (!DeviceExt
->AvailableClustersValid
)
482 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
483 Status
= FAT12CountAvailableClusters(DeviceExt
);
484 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
485 Status
= FAT16CountAvailableClusters(DeviceExt
);
487 Status
= FAT32CountAvailableClusters(DeviceExt
);
489 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
490 ExReleaseResourceLite (&DeviceExt
->FatResource
);
497 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
501 PDEVICE_EXTENSION DeviceExt
,
502 ULONG ClusterToWrite
,
510 LARGE_INTEGER Offset
;
513 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
515 return STATUS_UNSUCCESSFUL
;
517 CBlock
= (PUCHAR
)BaseAddress
;
519 FATOffset
= (ClusterToWrite
* 12) / 8;
520 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
521 NewValue
, ClusterToWrite
, FATOffset
);
522 if ((ClusterToWrite
% 2) == 0)
524 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
525 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
526 CBlock
[FATOffset
+ 1] &= 0xf0;
527 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
531 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
532 CBlock
[FATOffset
] &= 0x0f;
533 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
534 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
536 /* Write the changed FAT sector(s) to disk */
537 CcSetDirtyPinnedData(Context
, NULL
);
538 CcUnpinData(Context
);
539 return STATUS_SUCCESS
;
543 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
547 PDEVICE_EXTENSION DeviceExt
,
548 ULONG ClusterToWrite
,
556 LARGE_INTEGER Offset
;
559 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
560 FATOffset
= ClusterToWrite
* 2;
561 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
562 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
564 return STATUS_UNSUCCESSFUL
;
567 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
569 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
570 *OldValue
= *Cluster
;
571 *Cluster
= (USHORT
)NewValue
;
572 CcSetDirtyPinnedData(Context
, NULL
);
573 CcUnpinData(Context
);
574 return STATUS_SUCCESS
;
578 * FUNCTION: Writes a cluster to the FAT32 physical tables
582 PDEVICE_EXTENSION DeviceExt
,
583 ULONG ClusterToWrite
,
591 LARGE_INTEGER Offset
;
594 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
596 FATOffset
= (ClusterToWrite
* 4);
597 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
598 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
600 return STATUS_UNSUCCESSFUL
;
603 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
605 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
606 *OldValue
= *Cluster
& 0x0fffffff;
607 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
609 CcSetDirtyPinnedData(Context
, NULL
);
610 CcUnpinData(Context
);
612 return STATUS_SUCCESS
;
617 * FUNCTION: Write a changed FAT entry
621 PDEVICE_EXTENSION DeviceExt
,
622 ULONG ClusterToWrite
,
628 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
629 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
630 if (DeviceExt
->AvailableClustersValid
)
632 if (OldValue
&& NewValue
== 0)
633 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
634 else if (OldValue
== 0 && NewValue
)
635 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
637 ExReleaseResourceLite(&DeviceExt
->FatResource
);
642 * FUNCTION: Converts the cluster number to a sector number for this physical
647 PDEVICE_EXTENSION DeviceExt
,
650 return DeviceExt
->FatInfo
.dataStart
+
651 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
656 * FUNCTION: Retrieve the next cluster depending on the FAT type
660 PDEVICE_EXTENSION DeviceExt
,
661 ULONG CurrentCluster
,
666 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
667 DeviceExt
, CurrentCluster
);
669 if (CurrentCluster
== 0)
670 return STATUS_INVALID_PARAMETER
;
672 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
673 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
674 ExReleaseResourceLite(&DeviceExt
->FatResource
);
680 * FUNCTION: Retrieve the next cluster depending on the FAT type
683 GetNextClusterExtend(
684 PDEVICE_EXTENSION DeviceExt
,
685 ULONG CurrentCluster
,
691 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
692 DeviceExt
, CurrentCluster
);
694 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
696 * If the file hasn't any clusters allocated then we need special
699 if (CurrentCluster
== 0)
701 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
702 if (!NT_SUCCESS(Status
))
704 ExReleaseResourceLite(&DeviceExt
->FatResource
);
708 *NextCluster
= NewCluster
;
709 ExReleaseResourceLite(&DeviceExt
->FatResource
);
710 return STATUS_SUCCESS
;
713 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
715 if ((*NextCluster
) == 0xFFFFFFFF)
717 /* We are after last existing cluster, we must add one to file */
718 /* Firstly, find the next available open allocation unit and
719 mark it as end of file */
720 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
721 if (!NT_SUCCESS(Status
))
723 ExReleaseResourceLite(&DeviceExt
->FatResource
);
727 /* Now, write the AU of the LastCluster with the value of the newly
729 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
730 *NextCluster
= NewCluster
;
733 ExReleaseResourceLite(&DeviceExt
->FatResource
);