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 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
28 PDEVICE_EXTENSION DeviceExt
,
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
;
46 CurrentCluster
= (*(PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
))) & 0x0fffffff;
47 if (CurrentCluster
>= 0xffffff8 && CurrentCluster
<= 0xfffffff)
48 CurrentCluster
= 0xffffffff;
51 *NextCluster
= CurrentCluster
;
52 return STATUS_SUCCESS
;
56 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
60 PDEVICE_EXTENSION DeviceExt
,
70 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
71 FATOffset
= CurrentCluster
* 2;
72 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
73 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, MAP_WAIT
, &Context
, &BaseAddress
))
75 return STATUS_UNSUCCESSFUL
;
78 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
79 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
80 CurrentCluster
= 0xffffffff;
82 *NextCluster
= CurrentCluster
;
83 return STATUS_SUCCESS
;
87 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
91 PDEVICE_EXTENSION DeviceExt
,
104 if (!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
106 return STATUS_UNSUCCESSFUL
;
109 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
110 if ((CurrentCluster
% 2) == 0)
112 Entry
= *CBlock
& 0x0fff;
116 Entry
= *CBlock
>> 4;
119 // DPRINT("Entry %x\n",Entry);
120 if (Entry
>= 0xff8 && Entry
<= 0xfff)
123 // DPRINT("Returning %x\n",Entry);
124 *NextCluster
= Entry
;
125 CcUnpinData(Context
);
126 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
127 return STATUS_SUCCESS
;
131 * FUNCTION: Finds the first available cluster in a FAT16 table
134 FAT16FindAndMarkAvailableCluster(
135 PDEVICE_EXTENSION DeviceExt
,
144 LARGE_INTEGER Offset
;
148 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
149 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
151 StartCluster
= DeviceExt
->LastAvailableCluster
;
153 for (j
= 0; j
< 2; j
++)
155 for (i
= StartCluster
; i
< FatLength
;)
157 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
158 if (!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
160 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
161 return STATUS_UNSUCCESSFUL
;
164 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
165 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
167 /* Now process the whole block */
168 while (Block
< BlockEnd
&& i
< FatLength
)
172 DPRINT("Found available cluster 0x%x\n", i
);
173 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
175 CcSetDirtyPinnedData(Context
, NULL
);
176 CcUnpinData(Context
);
177 if (DeviceExt
->AvailableClustersValid
)
178 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
179 return STATUS_SUCCESS
;
186 CcUnpinData(Context
);
189 FatLength
= StartCluster
;
193 return STATUS_DISK_FULL
;
197 FAT12FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
199 * FUNCTION: Finds the first available cluster in a FAT12 table
209 LARGE_INTEGER Offset
;
211 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
213 StartCluster
= DeviceExt
->LastAvailableCluster
;
215 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
217 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
218 return STATUS_UNSUCCESSFUL
;
221 for (j
= 0; j
< 2; j
++)
223 for (i
= StartCluster
; i
< FatLength
; i
++)
225 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
228 Entry
= *CBlock
& 0xfff;
232 Entry
= *CBlock
>> 4;
236 DPRINT("Found available cluster 0x%x\n", i
);
237 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
239 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
241 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
242 CcSetDirtyPinnedData(Context
, NULL
);
243 CcUnpinData(Context
);
244 if (DeviceExt
->AvailableClustersValid
)
245 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
246 return(STATUS_SUCCESS
);
249 FatLength
= StartCluster
;
252 CcUnpinData(Context
);
253 return (STATUS_DISK_FULL
);
257 FAT32FindAndMarkAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
259 * FUNCTION: Finds the first available cluster in a FAT32 table
268 LARGE_INTEGER Offset
;
272 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
273 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
275 StartCluster
= DeviceExt
->LastAvailableCluster
;
277 for (j
= 0; j
< 2; j
++)
279 for (i
= StartCluster
; i
< FatLength
;)
281 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
282 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
284 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
285 return STATUS_UNSUCCESSFUL
;
287 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
288 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
290 /* Now process the whole block */
291 while (Block
< BlockEnd
&& i
< FatLength
)
293 if ((*Block
& 0x0fffffff) == 0)
295 DPRINT("Found available cluster 0x%x\n", i
);
296 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
298 CcSetDirtyPinnedData(Context
, NULL
);
299 CcUnpinData(Context
);
300 if (DeviceExt
->AvailableClustersValid
)
301 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
302 return(STATUS_SUCCESS
);
309 CcUnpinData(Context
);
311 FatLength
= StartCluster
;
314 return (STATUS_DISK_FULL
);
318 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
320 * FUNCTION: Counts free cluster in a FAT12 table
327 ULONG numberofclusters
;
328 LARGE_INTEGER Offset
;
333 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
335 return STATUS_UNSUCCESSFUL
;
338 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
340 for (i
= 2; i
< numberofclusters
; i
++)
342 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
345 Entry
= *CBlock
& 0x0fff;
349 Entry
= *CBlock
>> 4;
355 CcUnpinData(Context
);
356 DeviceExt
->AvailableClusters
= ulCount
;
357 DeviceExt
->AvailableClustersValid
= TRUE
;
359 return(STATUS_SUCCESS
);
364 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
366 * FUNCTION: Counts free clusters in a FAT16 table
371 PVOID BaseAddress
= NULL
;
375 PVOID Context
= NULL
;
376 LARGE_INTEGER Offset
;
379 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
380 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
382 for (i
= 2; i
< FatLength
; )
384 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
385 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
387 return STATUS_UNSUCCESSFUL
;
389 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
390 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
392 /* Now process the whole block */
393 while (Block
< BlockEnd
&& i
< FatLength
)
401 CcUnpinData(Context
);
404 DeviceExt
->AvailableClusters
= ulCount
;
405 DeviceExt
->AvailableClustersValid
= TRUE
;
407 return(STATUS_SUCCESS
);
412 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
414 * FUNCTION: Counts free clusters in a FAT32 table
419 PVOID BaseAddress
= NULL
;
423 PVOID Context
= NULL
;
424 LARGE_INTEGER Offset
;
427 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
428 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
430 for (i
= 2; i
< FatLength
; )
432 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
433 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
435 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
436 return STATUS_UNSUCCESSFUL
;
438 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
439 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
441 /* Now process the whole block */
442 while (Block
< BlockEnd
&& i
< FatLength
)
444 if ((*Block
& 0x0fffffff) == 0)
450 CcUnpinData(Context
);
453 DeviceExt
->AvailableClusters
= ulCount
;
454 DeviceExt
->AvailableClustersValid
= TRUE
;
456 return(STATUS_SUCCESS
);
460 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
,
461 PLARGE_INTEGER Clusters
)
463 NTSTATUS Status
= STATUS_SUCCESS
;
464 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
465 if (!DeviceExt
->AvailableClustersValid
)
467 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
468 Status
= FAT12CountAvailableClusters(DeviceExt
);
469 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
470 Status
= FAT16CountAvailableClusters(DeviceExt
);
472 Status
= FAT32CountAvailableClusters(DeviceExt
);
474 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
475 ExReleaseResourceLite (&DeviceExt
->FatResource
);
485 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt
,
486 ULONG ClusterToWrite
,
490 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
497 LARGE_INTEGER Offset
;
500 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
502 return STATUS_UNSUCCESSFUL
;
504 CBlock
= (PUCHAR
)BaseAddress
;
506 FATOffset
= (ClusterToWrite
* 12) / 8;
507 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
508 NewValue
, ClusterToWrite
, FATOffset
);
509 if ((ClusterToWrite
% 2) == 0)
511 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
512 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
513 CBlock
[FATOffset
+ 1] &= 0xf0;
514 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
518 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
519 CBlock
[FATOffset
] &= 0x0f;
520 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
521 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
523 /* Write the changed FAT sector(s) to disk */
524 CcSetDirtyPinnedData(Context
, NULL
);
525 CcUnpinData(Context
);
526 return(STATUS_SUCCESS
);
530 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt
,
531 ULONG ClusterToWrite
,
535 * 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(!CcPinRead(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
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
555 *OldValue
= *Cluster
;
556 *Cluster
= (USHORT
)NewValue
;
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
575 LARGE_INTEGER Offset
;
578 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
580 FATOffset
= (ClusterToWrite
* 4);
581 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
582 if(!CcPinRead(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
584 return STATUS_UNSUCCESSFUL
;
586 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
588 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
589 *OldValue
= *Cluster
& 0x0fffffff;
590 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
592 CcSetDirtyPinnedData(Context
, NULL
);
593 CcUnpinData(Context
);
595 return(STATUS_SUCCESS
);
600 WriteCluster(PDEVICE_EXTENSION DeviceExt
,
601 ULONG ClusterToWrite
,
604 * FUNCTION: Write a changed FAT entry
609 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
610 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
611 if (DeviceExt
->AvailableClustersValid
)
613 if (OldValue
&& NewValue
== 0)
614 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
615 else if (OldValue
== 0 && NewValue
)
616 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
618 ExReleaseResourceLite(&DeviceExt
->FatResource
);
623 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
626 * FUNCTION: Converts the cluster number to a sector number for this physical
630 return DeviceExt
->FatInfo
.dataStart
+
631 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
636 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
637 ULONG CurrentCluster
,
640 * FUNCTION: Retrieve the next cluster depending on the FAT type
645 DPRINT ("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
646 DeviceExt
, CurrentCluster
);
648 if (CurrentCluster
== 0)
649 return(STATUS_INVALID_PARAMETER
);
651 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
652 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
653 ExReleaseResourceLite(&DeviceExt
->FatResource
);
659 GetNextClusterExtend(PDEVICE_EXTENSION DeviceExt
,
660 ULONG CurrentCluster
,
663 * FUNCTION: Retrieve the next cluster depending on the FAT type
668 DPRINT ("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
669 DeviceExt
, CurrentCluster
);
671 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
673 * If the file hasn't any clusters allocated then we need special
676 if (CurrentCluster
== 0)
680 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
681 if (!NT_SUCCESS(Status
))
683 ExReleaseResourceLite(&DeviceExt
->FatResource
);
687 *NextCluster
= NewCluster
;
688 ExReleaseResourceLite(&DeviceExt
->FatResource
);
689 return(STATUS_SUCCESS
);
692 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
694 if ((*NextCluster
) == 0xFFFFFFFF)
698 /* We are after last existing cluster, we must add one to file */
699 /* Firstly, find the next available open allocation unit and
700 mark it as end of file */
701 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
702 if (!NT_SUCCESS(Status
))
704 ExReleaseResourceLite(&DeviceExt
->FatResource
);
708 /* Now, write the AU of the LastCluster with the value of the newly
710 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
711 *NextCluster
= NewCluster
;
714 ExReleaseResourceLite(&DeviceExt
->FatResource
);