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)
11 /* INCLUDES *****************************************************************/
16 /* GLOBALS ******************************************************************/
18 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
19 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
21 /* FUNCTIONS ****************************************************************/
24 FAT32GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
28 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
38 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
39 FATOffset
= CurrentCluster
* sizeof(ULONG
);
40 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
41 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
43 return STATUS_UNSUCCESSFUL
;
45 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
46 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
47 CurrentCluster
= 0xffffffff;
49 *NextCluster
= CurrentCluster
;
50 return (STATUS_SUCCESS
);
54 FAT16GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
58 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
67 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
68 FATOffset
= CurrentCluster
* 2;
69 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
70 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
72 return STATUS_UNSUCCESSFUL
;
74 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
75 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
76 CurrentCluster
= 0xffffffff;
78 *NextCluster
= CurrentCluster
;
79 return (STATUS_SUCCESS
);
83 FAT12GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
87 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
100 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
102 return STATUS_UNSUCCESSFUL
;
104 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
105 if ((CurrentCluster
% 2) == 0)
107 Entry
= *CBlock
& 0x0fff;
111 Entry
= *CBlock
>> 4;
113 // DPRINT("Entry %x\n",Entry);
114 if (Entry
>= 0xff8 && Entry
<= 0xfff)
116 // DPRINT("Returning %x\n",Entry);
117 *NextCluster
= Entry
;
118 CcUnpinData(Context
);
119 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
120 return STATUS_SUCCESS
;
124 FAT16FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
,
127 * FUNCTION: Finds the first available cluster in a FAT16 table
136 LARGE_INTEGER Offset
;
140 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
141 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
143 StartCluster
= DeviceExt
->LastAvailableCluster
;
145 for (j
= 0; j
< 2; j
++)
147 for (i
= StartCluster
; i
< FatLength
; )
149 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
150 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
152 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
153 return STATUS_UNSUCCESSFUL
;
155 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
156 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
158 /* Now process the whole block */
159 while (Block
< BlockEnd
&& i
< FatLength
)
163 DPRINT("Found available cluster 0x%x\n", i
);
164 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
166 CcSetDirtyPinnedData(Context
, NULL
);
167 CcUnpinData(Context
);
168 return(STATUS_SUCCESS
);
175 CcUnpinData(Context
);
177 FatLength
= StartCluster
;
180 return(STATUS_DISK_FULL
);
184 FAT12FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
186 * FUNCTION: Finds the first available cluster in a FAT12 table
196 LARGE_INTEGER Offset
;
198 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
200 StartCluster
= DeviceExt
->LastAvailableCluster
;
202 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
204 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
205 return STATUS_UNSUCCESSFUL
;
208 for (j
= 0; j
< 2; j
++)
210 for (i
= StartCluster
; i
< FatLength
; i
++)
212 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
215 Entry
= *CBlock
& 0xfff;
219 Entry
= *CBlock
>> 4;
223 DPRINT("Found available cluster 0x%x\n", i
);
224 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
226 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
228 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
229 CcSetDirtyPinnedData(Context
, NULL
);
230 CcUnpinData(Context
);
231 return(STATUS_SUCCESS
);
234 FatLength
= StartCluster
;
237 CcUnpinData(Context
);
238 return (STATUS_DISK_FULL
);
242 FAT32FindAndMarkAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
244 * FUNCTION: Finds the first available cluster in a FAT32 table
253 LARGE_INTEGER Offset
;
257 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
258 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
260 StartCluster
= DeviceExt
->LastAvailableCluster
;
262 for (j
= 0; j
< 2; j
++)
264 for (i
= StartCluster
; i
< FatLength
;)
266 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
267 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
269 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
270 return STATUS_UNSUCCESSFUL
;
272 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
273 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
275 /* Now process the whole block */
276 while (Block
< BlockEnd
&& i
< FatLength
)
278 if ((*Block
& 0x0fffffff) == 0)
280 DPRINT("Found available cluster 0x%x\n", i
);
281 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
283 CcSetDirtyPinnedData(Context
, NULL
);
284 CcUnpinData(Context
);
285 return(STATUS_SUCCESS
);
292 CcUnpinData(Context
);
294 FatLength
= StartCluster
;
297 return (STATUS_DISK_FULL
);
301 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
303 * FUNCTION: Counts free cluster in a FAT12 table
310 ULONG numberofclusters
;
311 LARGE_INTEGER Offset
;
316 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
318 return STATUS_UNSUCCESSFUL
;
321 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
323 for (i
= 2; i
< numberofclusters
; i
++)
325 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
328 Entry
= *CBlock
& 0x0fff;
332 Entry
= *CBlock
>> 4;
338 CcUnpinData(Context
);
339 DeviceExt
->AvailableClusters
= ulCount
;
340 DeviceExt
->AvailableClustersValid
= TRUE
;
342 return(STATUS_SUCCESS
);
347 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
349 * FUNCTION: Counts free clusters in a FAT16 table
354 PVOID BaseAddress
= NULL
;
358 PVOID Context
= NULL
;
359 LARGE_INTEGER Offset
;
362 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
363 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
365 for (i
= 2; i
< FatLength
; )
367 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
368 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
370 return STATUS_UNSUCCESSFUL
;
372 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
373 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
375 /* Now process the whole block */
376 while (Block
< BlockEnd
&& i
< FatLength
)
384 CcUnpinData(Context
);
387 DeviceExt
->AvailableClusters
= ulCount
;
388 DeviceExt
->AvailableClustersValid
= TRUE
;
390 return(STATUS_SUCCESS
);
395 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
397 * FUNCTION: Counts free clusters in a FAT32 table
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
* 4, ChunkSize
);
416 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
418 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
419 return STATUS_UNSUCCESSFUL
;
421 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
422 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
424 /* Now process the whole block */
425 while (Block
< BlockEnd
&& i
< FatLength
)
427 if ((*Block
& 0x0fffffff) == 0)
433 CcUnpinData(Context
);
436 DeviceExt
->AvailableClusters
= ulCount
;
437 DeviceExt
->AvailableClustersValid
= TRUE
;
439 return(STATUS_SUCCESS
);
443 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
,
444 PLARGE_INTEGER Clusters
)
446 NTSTATUS Status
= STATUS_SUCCESS
;
447 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
448 if (!DeviceExt
->AvailableClustersValid
)
450 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
451 Status
= FAT12CountAvailableClusters(DeviceExt
);
452 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
453 Status
= FAT16CountAvailableClusters(DeviceExt
);
455 Status
= FAT32CountAvailableClusters(DeviceExt
);
457 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
458 ExReleaseResourceLite (&DeviceExt
->FatResource
);
468 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt
,
469 ULONG ClusterToWrite
,
473 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
481 LARGE_INTEGER Offset
;
484 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
486 return STATUS_UNSUCCESSFUL
;
488 CBlock
= (PUCHAR
)BaseAddress
;
490 FATOffset
= (ClusterToWrite
* 12) / 8;
491 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
492 NewValue
, ClusterToWrite
, FATOffset
);
493 if ((ClusterToWrite
% 2) == 0)
495 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
496 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
497 CBlock
[FATOffset
+ 1] &= 0xf0;
498 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
502 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
503 CBlock
[FATOffset
] &= 0x0f;
504 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
505 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
507 /* Write the changed FAT sector(s) to disk */
508 FATsector
= FATOffset
/ DeviceExt
->FatInfo
.BytesPerSector
;
509 CcSetDirtyPinnedData(Context
, NULL
);
510 CcUnpinData(Context
);
511 return(STATUS_SUCCESS
);
515 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt
,
516 ULONG ClusterToWrite
,
520 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
527 LARGE_INTEGER Offset
;
530 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
531 FATOffset
= ClusterToWrite
* 2;
532 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
533 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
535 return STATUS_UNSUCCESSFUL
;
537 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
539 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
540 *OldValue
= *Cluster
;
541 *Cluster
= (USHORT
)NewValue
;
542 CcSetDirtyPinnedData(Context
, NULL
);
543 CcUnpinData(Context
);
544 return(STATUS_SUCCESS
);
548 FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt
,
549 ULONG ClusterToWrite
,
553 * FUNCTION: Writes a cluster to the FAT32 physical tables
560 LARGE_INTEGER Offset
;
563 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
565 FATOffset
= (ClusterToWrite
* 4);
566 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
567 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
569 return STATUS_UNSUCCESSFUL
;
571 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
573 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
574 *OldValue
= *Cluster
& 0x0fffffff;
575 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
577 CcSetDirtyPinnedData(Context
, NULL
);
578 CcUnpinData(Context
);
580 return(STATUS_SUCCESS
);
585 WriteCluster(PDEVICE_EXTENSION DeviceExt
,
586 ULONG ClusterToWrite
,
589 * FUNCTION: Write a changed FAT entry
594 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
595 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
596 if (DeviceExt
->AvailableClustersValid
)
598 if (OldValue
&& NewValue
== 0)
599 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
600 else if (OldValue
== 0 && NewValue
)
601 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
603 ExReleaseResourceLite(&DeviceExt
->FatResource
);
608 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
611 * FUNCTION: Converts the cluster number to a sector number for this physical
615 return DeviceExt
->FatInfo
.dataStart
+
616 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
621 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
622 ULONG CurrentCluster
,
625 * FUNCTION: Retrieve the next cluster depending on the FAT type
630 DPRINT ("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
631 DeviceExt
, CurrentCluster
);
633 if (CurrentCluster
== 0)
634 return(STATUS_INVALID_PARAMETER
);
636 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
637 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
638 ExReleaseResourceLite(&DeviceExt
->FatResource
);
644 GetNextClusterExtend(PDEVICE_EXTENSION DeviceExt
,
645 ULONG CurrentCluster
,
648 * FUNCTION: Retrieve the next cluster depending on the FAT type
653 DPRINT ("GetNextClusterExtend(DeviceExt %x, CurrentCluster %x)\n",
654 DeviceExt
, CurrentCluster
);
656 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
659 * If the file hasn't any clusters allocated then we need special
662 if (CurrentCluster
== 0)
666 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
667 if (!NT_SUCCESS(Status
))
669 ExReleaseResourceLite(&DeviceExt
->FatResource
);
673 *NextCluster
= NewCluster
;
674 ExReleaseResourceLite(&DeviceExt
->FatResource
);
675 return(STATUS_SUCCESS
);
678 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
680 if ((*NextCluster
) == 0xFFFFFFFF)
684 /* We are after last existing cluster, we must add one to file */
685 /* Firstly, find the next available open allocation unit and
686 mark it as end of file */
687 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
688 if (!NT_SUCCESS(Status
))
690 ExReleaseResourceLite(&DeviceExt
->FatResource
);
694 /* Now, write the AU of the LastCluster with the value of the newly
696 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
697 *NextCluster
= NewCluster
;
700 ExReleaseResourceLite(&DeviceExt
->FatResource
);