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 *****************************************************************/
15 /* GLOBALS ******************************************************************/
17 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
18 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
20 /* FUNCTIONS ****************************************************************/
23 FAT32GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
27 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
37 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
38 FATOffset
= CurrentCluster
* sizeof(ULONG
);
39 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
40 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
42 return STATUS_UNSUCCESSFUL
;
44 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
45 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
46 CurrentCluster
= 0xffffffff;
48 *NextCluster
= CurrentCluster
;
49 return (STATUS_SUCCESS
);
53 FAT16GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
57 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
66 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
67 FATOffset
= CurrentCluster
* 2;
68 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
69 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
71 return STATUS_UNSUCCESSFUL
;
73 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
74 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
75 CurrentCluster
= 0xffffffff;
77 *NextCluster
= CurrentCluster
;
78 return (STATUS_SUCCESS
);
82 FAT12GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
86 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
99 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
101 return STATUS_UNSUCCESSFUL
;
103 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
104 if ((CurrentCluster
% 2) == 0)
106 Entry
= *CBlock
& 0x0fff;
110 Entry
= *CBlock
>> 4;
112 // DPRINT("Entry %x\n",Entry);
113 if (Entry
>= 0xff8 && Entry
<= 0xfff)
115 // DPRINT("Returning %x\n",Entry);
116 *NextCluster
= Entry
;
117 CcUnpinData(Context
);
118 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
119 return STATUS_SUCCESS
;
123 FAT16FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
,
126 * FUNCTION: Finds the first available cluster in a FAT16 table
135 LARGE_INTEGER Offset
;
139 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
140 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
142 StartCluster
= DeviceExt
->LastAvailableCluster
;
144 for (j
= 0; j
< 2; j
++)
146 for (i
= StartCluster
; i
< FatLength
; )
148 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
149 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
151 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
152 return STATUS_UNSUCCESSFUL
;
154 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
155 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
157 /* Now process the whole block */
158 while (Block
< BlockEnd
&& i
< FatLength
)
162 DPRINT("Found available cluster 0x%x\n", i
);
163 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
165 CcSetDirtyPinnedData(Context
, NULL
);
166 CcUnpinData(Context
);
167 if (DeviceExt
->AvailableClustersValid
)
168 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
169 return(STATUS_SUCCESS
);
176 CcUnpinData(Context
);
178 FatLength
= StartCluster
;
181 return(STATUS_DISK_FULL
);
185 FAT12FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
187 * FUNCTION: Finds the first available cluster in a FAT12 table
197 LARGE_INTEGER Offset
;
199 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
201 StartCluster
= DeviceExt
->LastAvailableCluster
;
203 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
205 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
206 return STATUS_UNSUCCESSFUL
;
209 for (j
= 0; j
< 2; j
++)
211 for (i
= StartCluster
; i
< FatLength
; i
++)
213 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
216 Entry
= *CBlock
& 0xfff;
220 Entry
= *CBlock
>> 4;
224 DPRINT("Found available cluster 0x%x\n", i
);
225 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
227 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
229 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
230 CcSetDirtyPinnedData(Context
, NULL
);
231 CcUnpinData(Context
);
232 if (DeviceExt
->AvailableClustersValid
)
233 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
234 return(STATUS_SUCCESS
);
237 FatLength
= StartCluster
;
240 CcUnpinData(Context
);
241 return (STATUS_DISK_FULL
);
245 FAT32FindAndMarkAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
247 * FUNCTION: Finds the first available cluster in a FAT32 table
256 LARGE_INTEGER Offset
;
260 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
261 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
263 StartCluster
= DeviceExt
->LastAvailableCluster
;
265 for (j
= 0; j
< 2; j
++)
267 for (i
= StartCluster
; i
< FatLength
;)
269 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
270 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
272 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
273 return STATUS_UNSUCCESSFUL
;
275 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
276 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
278 /* Now process the whole block */
279 while (Block
< BlockEnd
&& i
< FatLength
)
281 if ((*Block
& 0x0fffffff) == 0)
283 DPRINT("Found available cluster 0x%x\n", i
);
284 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
286 CcSetDirtyPinnedData(Context
, NULL
);
287 CcUnpinData(Context
);
288 if (DeviceExt
->AvailableClustersValid
)
289 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
290 return(STATUS_SUCCESS
);
297 CcUnpinData(Context
);
299 FatLength
= StartCluster
;
302 return (STATUS_DISK_FULL
);
306 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
308 * FUNCTION: Counts free cluster in a FAT12 table
315 ULONG numberofclusters
;
316 LARGE_INTEGER Offset
;
321 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
323 return STATUS_UNSUCCESSFUL
;
326 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
328 for (i
= 2; i
< numberofclusters
; i
++)
330 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
333 Entry
= *CBlock
& 0x0fff;
337 Entry
= *CBlock
>> 4;
343 CcUnpinData(Context
);
344 DeviceExt
->AvailableClusters
= ulCount
;
345 DeviceExt
->AvailableClustersValid
= TRUE
;
347 return(STATUS_SUCCESS
);
352 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
354 * FUNCTION: Counts free clusters in a FAT16 table
359 PVOID BaseAddress
= NULL
;
363 PVOID Context
= NULL
;
364 LARGE_INTEGER Offset
;
367 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
368 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
370 for (i
= 2; i
< FatLength
; )
372 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
373 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
375 return STATUS_UNSUCCESSFUL
;
377 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
378 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
380 /* Now process the whole block */
381 while (Block
< BlockEnd
&& i
< FatLength
)
389 CcUnpinData(Context
);
392 DeviceExt
->AvailableClusters
= ulCount
;
393 DeviceExt
->AvailableClustersValid
= TRUE
;
395 return(STATUS_SUCCESS
);
400 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
402 * FUNCTION: Counts free clusters in a FAT32 table
407 PVOID BaseAddress
= NULL
;
411 PVOID Context
= NULL
;
412 LARGE_INTEGER Offset
;
415 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
416 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
418 for (i
= 2; i
< FatLength
; )
420 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
421 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
423 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
424 return STATUS_UNSUCCESSFUL
;
426 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
427 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
429 /* Now process the whole block */
430 while (Block
< BlockEnd
&& i
< FatLength
)
432 if ((*Block
& 0x0fffffff) == 0)
438 CcUnpinData(Context
);
441 DeviceExt
->AvailableClusters
= ulCount
;
442 DeviceExt
->AvailableClustersValid
= TRUE
;
444 return(STATUS_SUCCESS
);
448 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
,
449 PLARGE_INTEGER Clusters
)
451 NTSTATUS Status
= STATUS_SUCCESS
;
452 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
453 if (!DeviceExt
->AvailableClustersValid
)
455 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
456 Status
= FAT12CountAvailableClusters(DeviceExt
);
457 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
458 Status
= FAT16CountAvailableClusters(DeviceExt
);
460 Status
= FAT32CountAvailableClusters(DeviceExt
);
462 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
463 ExReleaseResourceLite (&DeviceExt
->FatResource
);
473 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt
,
474 ULONG ClusterToWrite
,
478 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
485 LARGE_INTEGER Offset
;
488 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
490 return STATUS_UNSUCCESSFUL
;
492 CBlock
= (PUCHAR
)BaseAddress
;
494 FATOffset
= (ClusterToWrite
* 12) / 8;
495 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
496 NewValue
, ClusterToWrite
, FATOffset
);
497 if ((ClusterToWrite
% 2) == 0)
499 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
500 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
501 CBlock
[FATOffset
+ 1] &= 0xf0;
502 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
506 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
507 CBlock
[FATOffset
] &= 0x0f;
508 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
509 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
511 /* Write the changed FAT sector(s) to disk */
512 CcSetDirtyPinnedData(Context
, NULL
);
513 CcUnpinData(Context
);
514 return(STATUS_SUCCESS
);
518 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt
,
519 ULONG ClusterToWrite
,
523 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
530 LARGE_INTEGER Offset
;
533 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
534 FATOffset
= ClusterToWrite
* 2;
535 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
536 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
538 return STATUS_UNSUCCESSFUL
;
540 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
542 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
543 *OldValue
= *Cluster
;
544 *Cluster
= (USHORT
)NewValue
;
545 CcSetDirtyPinnedData(Context
, NULL
);
546 CcUnpinData(Context
);
547 return(STATUS_SUCCESS
);
551 FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt
,
552 ULONG ClusterToWrite
,
556 * FUNCTION: Writes a cluster to the FAT32 physical tables
563 LARGE_INTEGER Offset
;
566 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
568 FATOffset
= (ClusterToWrite
* 4);
569 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
570 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
572 return STATUS_UNSUCCESSFUL
;
574 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
576 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
577 *OldValue
= *Cluster
& 0x0fffffff;
578 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
580 CcSetDirtyPinnedData(Context
, NULL
);
581 CcUnpinData(Context
);
583 return(STATUS_SUCCESS
);
588 WriteCluster(PDEVICE_EXTENSION DeviceExt
,
589 ULONG ClusterToWrite
,
592 * FUNCTION: Write a changed FAT entry
597 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
598 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
599 if (DeviceExt
->AvailableClustersValid
)
601 if (OldValue
&& NewValue
== 0)
602 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
603 else if (OldValue
== 0 && NewValue
)
604 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
606 ExReleaseResourceLite(&DeviceExt
->FatResource
);
611 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
614 * FUNCTION: Converts the cluster number to a sector number for this physical
618 return DeviceExt
->FatInfo
.dataStart
+
619 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
624 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
625 ULONG CurrentCluster
,
628 * FUNCTION: Retrieve the next cluster depending on the FAT type
633 DPRINT ("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
634 DeviceExt
, CurrentCluster
);
636 if (CurrentCluster
== 0)
637 return(STATUS_INVALID_PARAMETER
);
639 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
640 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
641 ExReleaseResourceLite(&DeviceExt
->FatResource
);
647 GetNextClusterExtend(PDEVICE_EXTENSION DeviceExt
,
648 ULONG CurrentCluster
,
651 * FUNCTION: Retrieve the next cluster depending on the FAT type
656 DPRINT ("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
657 DeviceExt
, CurrentCluster
);
659 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
661 * If the file hasn't any clusters allocated then we need special
664 if (CurrentCluster
== 0)
668 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
669 if (!NT_SUCCESS(Status
))
671 ExReleaseResourceLite(&DeviceExt
->FatResource
);
675 *NextCluster
= NewCluster
;
676 ExReleaseResourceLite(&DeviceExt
->FatResource
);
677 return(STATUS_SUCCESS
);
680 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
682 if ((*NextCluster
) == 0xFFFFFFFF)
686 /* We are after last existing cluster, we must add one to file */
687 /* Firstly, find the next available open allocation unit and
688 mark it as end of file */
689 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
690 if (!NT_SUCCESS(Status
))
692 ExReleaseResourceLite(&DeviceExt
->FatResource
);
696 /* Now, write the AU of the LastCluster with the value of the newly
698 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
699 *NextCluster
= NewCluster
;
702 ExReleaseResourceLite(&DeviceExt
->FatResource
);