2 * $Id: fat.c,v 1.40 2002/10/01 19:27:18 chorns Exp $
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS kernel
6 * FILE: services/fs/vfat/fat.c
7 * PURPOSE: VFAT Filesystem
8 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
12 /* INCLUDES *****************************************************************/
14 #include <ddk/ntddk.h>
16 #include <ntos/minmax.h>
23 /* GLOBALS ******************************************************************/
25 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
27 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
28 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
30 /* FUNCTIONS ****************************************************************/
33 Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
37 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
48 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
49 FATOffset
= CurrentCluster
* sizeof(ULONG
);
50 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
51 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
53 return STATUS_UNSUCCESSFUL
;
55 CurrentCluster
= (*(PULONG
)(BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
56 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
57 CurrentCluster
= 0xffffffff;
59 *NextCluster
= CurrentCluster
;
60 return (STATUS_SUCCESS
);
64 Fat16GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
68 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
78 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
79 FATOffset
= CurrentCluster
* 2;
80 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
81 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
83 return STATUS_UNSUCCESSFUL
;
85 CurrentCluster
= *((PUSHORT
)(BaseAddress
+ (FATOffset
% ChunkSize
)));
86 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
87 CurrentCluster
= 0xffffffff;
89 *NextCluster
= CurrentCluster
;
90 return (STATUS_SUCCESS
);
94 Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
98 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
107 LARGE_INTEGER Offset
;
113 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
115 return STATUS_UNSUCCESSFUL
;
117 CBlock
= (PUSHORT
)(BaseAddress
+ (CurrentCluster
* 12) / 8);
118 if ((CurrentCluster
% 2) == 0)
120 Entry
= *CBlock
& 0x0fff;
124 Entry
= *CBlock
>> 4;
126 // DPRINT("Entry %x\n",Entry);
127 if (Entry
>= 0xff8 && Entry
<= 0xfff)
129 // DPRINT("Returning %x\n",Entry);
130 *NextCluster
= Entry
;
131 CcUnpinData(Context
);
132 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
133 return STATUS_SUCCESS
;
137 FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt
,
140 * FUNCTION: Finds the first available cluster in a FAT16 table
150 LARGE_INTEGER Offset
;
153 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
154 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+2 ) * 2;
156 StartCluster
= DeviceExt
->LastAvailableCluster
;
158 for (j
= 0; j
< 2; j
++)
160 for (i
= StartCluster
* 2; i
< FatLength
; i
+= 2, Block
++)
162 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
164 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
167 CcUnpinData(Context
);
170 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
172 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
173 return STATUS_UNSUCCESSFUL
;
176 Block
= (PUSHORT
)(BaseAddress
+ i
% ChunkSize
);
181 DPRINT("Found available cluster 0x%x\n", i
/ 2);
182 DeviceExt
->LastAvailableCluster
= *Cluster
= i
/ 2;
183 CcUnpinData(Context
);
184 return(STATUS_SUCCESS
);
187 FatLength
= StartCluster
* 2;
191 CcUnpinData(Context
);
195 return(STATUS_DISK_FULL
);
199 FAT12FindAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
201 * FUNCTION: Finds the first available cluster in a FAT12 table
213 LARGE_INTEGER Offset
;
215 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
217 StartCluster
= DeviceExt
->LastAvailableCluster
;
219 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
221 DPRINT1("CcMapData(Offset %x, Length %d) 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
)(BaseAddress
+ (i
* 12) / 8);
232 Entry
= *CBlock
& 0xfff;
236 Entry
= *CBlock
>> 4;
240 DPRINT("Found available cluster 0x%x\n", i
);
241 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
242 CcUnpinData(Context
);
243 return(STATUS_SUCCESS
);
246 FatLength
= StartCluster
;
249 CcUnpinData(Context
);
250 return (STATUS_DISK_FULL
);
254 FAT32FindAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
256 * FUNCTION: Finds the first available cluster in a FAT32 table
266 LARGE_INTEGER Offset
;
269 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
270 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 4;
272 StartCluster
= DeviceExt
->LastAvailableCluster
;
274 for (j
= 0; j
< 2; j
++)
276 for (i
= StartCluster
* 4; i
< FatLength
; i
+= 4, Block
++)
278 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
280 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
283 CcUnpinData(Context
);
285 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
287 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
288 return STATUS_UNSUCCESSFUL
;
290 Block
= (PULONG
)(BaseAddress
+ i
% ChunkSize
);
293 if ((*Block
& 0x0fffffff) == 0)
295 DPRINT("Found available cluster 0x%x\n", i
/ 4);
296 DeviceExt
->LastAvailableCluster
= *Cluster
= i
/ 4;
297 CcUnpinData(Context
);
298 return(STATUS_SUCCESS
);
301 FatLength
= StartCluster
* 4;
305 CcUnpinData(Context
);
309 return (STATUS_DISK_FULL
);
314 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
316 * FUNCTION: Counts free cluster in a FAT12 table
325 ULONG numberofclusters
;
326 LARGE_INTEGER Offset
;
331 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
333 return STATUS_UNSUCCESSFUL
;
336 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
338 for (i
= 2; i
< numberofclusters
; i
++)
340 CBlock
= (PUSHORT
)(BaseAddress
+ (i
* 12) / 8);
343 Entry
= *CBlock
& 0x0fff;
347 Entry
= *CBlock
>> 4;
353 CcUnpinData(Context
);
354 DeviceExt
->AvailableClusters
= ulCount
;
355 DeviceExt
->AvailableClustersValid
= TRUE
;
357 return(STATUS_SUCCESS
);
362 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
364 * FUNCTION: Counts free clusters in a FAT16 table
368 PVOID BaseAddress
= NULL
;
373 PVOID Context
= NULL
;
374 LARGE_INTEGER Offset
;
377 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
378 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 2;
380 for (i
= 4; i
< FatLength
; i
+= 2, Block
++)
382 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
387 CcUnpinData(Context
);
389 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
390 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
392 return STATUS_UNSUCCESSFUL
;
394 Block
= (PUSHORT
)(BaseAddress
+ i
% ChunkSize
);
400 DeviceExt
->AvailableClusters
= ulCount
;
401 DeviceExt
->AvailableClustersValid
= TRUE
;
402 CcUnpinData(Context
);
404 return(STATUS_SUCCESS
);
409 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
411 * FUNCTION: Counts free clusters in a FAT32 table
415 PVOID BaseAddress
= NULL
;
420 PVOID Context
= NULL
;
421 LARGE_INTEGER Offset
;
424 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
425 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 4;
427 for (i
= 8; i
< FatLength
; i
+= 4, Block
++)
429 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
433 CcUnpinData(Context
);
435 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
436 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
438 return STATUS_UNSUCCESSFUL
;
440 Block
= (PULONG
)(BaseAddress
+ i
% ChunkSize
);
442 if ((*Block
& 0x0fffffff) == 0)
446 DeviceExt
->AvailableClusters
= ulCount
;
447 DeviceExt
->AvailableClustersValid
= TRUE
;
448 CcUnpinData(Context
);
450 return(STATUS_SUCCESS
);
454 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
,
455 PLARGE_INTEGER Clusters
)
457 NTSTATUS Status
= STATUS_SUCCESS
;
458 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
459 if (!DeviceExt
->AvailableClustersValid
)
461 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
462 Status
= FAT12CountAvailableClusters(DeviceExt
);
463 else if (DeviceExt
->FatInfo
.FatType
== FAT16
)
464 Status
= FAT16CountAvailableClusters(DeviceExt
);
466 Status
= FAT32CountAvailableClusters(DeviceExt
);
468 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
469 ExReleaseResourceLite (&DeviceExt
->FatResource
);
479 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt
,
480 ULONG ClusterToWrite
,
484 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
494 LARGE_INTEGER Offset
;
497 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
499 return STATUS_UNSUCCESSFUL
;
501 CBlock
= (PUCHAR
)BaseAddress
;
503 FATOffset
= (ClusterToWrite
* 12) / 8;
504 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
505 NewValue
, ClusterToWrite
, FATOffset
);
506 if ((ClusterToWrite
% 2) == 0)
508 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
509 CBlock
[FATOffset
] = NewValue
;
510 CBlock
[FATOffset
+ 1] &= 0xf0;
511 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
515 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
516 CBlock
[FATOffset
] &= 0x0f;
517 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
518 CBlock
[FATOffset
+ 1] = NewValue
>> 4;
520 /* Write the changed FAT sector(s) to disk */
521 FATsector
= FATOffset
/ DeviceExt
->FatInfo
.BytesPerSector
;
522 CcSetDirtyPinnedData(Context
, NULL
);
523 CcUnpinData(Context
);
524 return(STATUS_SUCCESS
);
528 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt
,
529 ULONG ClusterToWrite
,
533 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
542 LARGE_INTEGER Offset
;
545 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
546 FATOffset
= ClusterToWrite
* 2;
547 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
548 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
550 return STATUS_UNSUCCESSFUL
;
552 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
554 Cluster
= ((PUSHORT
)(BaseAddress
+ (FATOffset
% ChunkSize
)));
555 *OldValue
= *Cluster
;
557 CcSetDirtyPinnedData(Context
, NULL
);
558 CcUnpinData(Context
);
559 return(STATUS_SUCCESS
);
563 FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt
,
564 ULONG ClusterToWrite
,
568 * FUNCTION: Writes a cluster to the FAT32 physical tables
577 LARGE_INTEGER Offset
;
580 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
582 FATOffset
= (ClusterToWrite
* 4);
583 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
584 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
586 return STATUS_UNSUCCESSFUL
;
588 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
590 Cluster
= ((PULONG
)(BaseAddress
+ (FATOffset
% ChunkSize
)));
591 *OldValue
= *Cluster
& 0x0fffffff;
592 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
594 CcSetDirtyPinnedData(Context
, NULL
);
595 CcUnpinData(Context
);
597 return(STATUS_SUCCESS
);
602 WriteCluster(PDEVICE_EXTENSION DeviceExt
,
603 ULONG ClusterToWrite
,
606 * FUNCTION: Write a changed FAT entry
611 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
612 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
614 Status
= FAT16WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
616 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
618 Status
= FAT32WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
622 Status
= FAT12WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
624 if (DeviceExt
->AvailableClustersValid
)
626 if (OldValue
&& NewValue
== 0)
627 InterlockedIncrement(&DeviceExt
->AvailableClusters
);
628 else if (OldValue
== 0 && NewValue
)
629 InterlockedDecrement(&DeviceExt
->AvailableClusters
);
631 ExReleaseResourceLite(&DeviceExt
->FatResource
);
636 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
639 * FUNCTION: Converts the cluster number to a sector number for this physical
643 return DeviceExt
->FatInfo
.dataStart
+
644 ((Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
648 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
649 ULONG CurrentCluster
,
653 * FUNCTION: Retrieve the next cluster depending on the FAT type
658 DPRINT ("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
659 DeviceExt
, CurrentCluster
);
663 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
667 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
671 * If the file hasn't any clusters allocated then we need special
674 if (CurrentCluster
== 0 && Extend
)
678 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
681 Status
= FAT16FindAvailableCluster(DeviceExt
, &NewCluster
);
683 if (!NT_SUCCESS(Status
))
685 ExReleaseResourceLite(&DeviceExt
->FatResource
);
689 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
691 Status
= FAT32FindAvailableCluster(DeviceExt
, &NewCluster
);
692 if (!NT_SUCCESS(Status
))
694 ExReleaseResourceLite(&DeviceExt
->FatResource
);
700 Status
= FAT12FindAvailableCluster(DeviceExt
, &NewCluster
);
701 if (!NT_SUCCESS(Status
))
703 ExReleaseResourceLite(&DeviceExt
->FatResource
);
707 /* Mark the new AU as the EOF */
708 WriteCluster (DeviceExt
, NewCluster
, 0xFFFFFFFF);
709 *NextCluster
= NewCluster
;
710 ExReleaseResourceLite(&DeviceExt
->FatResource
);
711 return(STATUS_SUCCESS
);
713 else if (CurrentCluster
== 0)
715 ExReleaseResourceLite(&DeviceExt
->FatResource
);
716 return(STATUS_UNSUCCESSFUL
);
719 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
721 Status
= Fat16GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
723 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
725 Status
= Fat32GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
729 Status
= Fat12GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
731 if (Extend
&& (*NextCluster
) == 0xFFFFFFFF)
735 /* We are after last existing cluster, we must add one to file */
736 /* Firstly, find the next available open allocation unit */
737 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
739 Status
= FAT16FindAvailableCluster(DeviceExt
, &NewCluster
);
740 if (!NT_SUCCESS(Status
))
742 ExReleaseResourceLite(&DeviceExt
->FatResource
);
746 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
748 Status
= FAT32FindAvailableCluster(DeviceExt
, &NewCluster
);
749 if (!NT_SUCCESS(Status
))
751 ExReleaseResourceLite(&DeviceExt
->FatResource
);
757 Status
= FAT12FindAvailableCluster(DeviceExt
, &NewCluster
);
758 if (!NT_SUCCESS(Status
))
760 ExReleaseResourceLite(&DeviceExt
->FatResource
);
764 /* Mark the new AU as the EOF */
765 WriteCluster(DeviceExt
, NewCluster
, 0xFFFFFFFF);
766 /* Now, write the AU of the LastCluster with the value of the newly
768 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
769 *NextCluster
= NewCluster
;
772 ExReleaseResourceLite(&DeviceExt
->FatResource
);
779 GetNextSector(PDEVICE_EXTENSION DeviceExt
,
783 /* Some functions don't have access to the cluster they're really reading from.
784 Maybe this is a dirty solution, but it will allow them to handle fragmentation. */
788 DPRINT("GetNextSector(DeviceExt %x, CurrentSector %x)\n",
791 if (CurrentSector
<DeviceExt
->FatInfo
.dataStart
|| ((CurrentSector
- DeviceExt
->FatInfo
.dataStart
+ 1) % DeviceExt
->FatInfo
.SectorsPerCluster
))
792 /* Basically, if the next sequential sector would be on a cluster border, then we'll need to check in the FAT */
794 (*NextSector
)=CurrentSector
+1;
795 return (STATUS_SUCCESS
);
799 CurrentSector
= (CurrentSector
- DeviceExt
->FatInfo
.dataStart
) / DeviceExt
->FatInfo
.SectorsPerCluster
+ 2;
801 Status
= GetNextCluster(DeviceExt
, CurrentSector
, NextSector
, Extend
);
802 if (!NT_SUCCESS(Status
))
806 if ((*NextSector
) == 0 || (*NextSector
) == 0xffffffff)
808 /* The caller wants to know a sector. These FAT codes don't correspond to any sector. */
809 return(STATUS_UNSUCCESSFUL
);
812 (*NextSector
) = ClusterToSector(DeviceExt
,(*NextSector
));
813 return(STATUS_SUCCESS
);