2 * $Id: fat.c,v 1.44 2003/10/11 17:51:56 hbirr Exp $
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS kernel
6 * FILE: drivers/fs/vfat/fat.c
7 * PURPOSE: VFAT Filesystem
8 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
13 /* INCLUDES *****************************************************************/
15 #include <ddk/ntddk.h>
17 #include <ntos/minmax.h>
24 /* GLOBALS ******************************************************************/
26 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
28 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
29 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
31 /* FUNCTIONS ****************************************************************/
34 Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
38 * 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
)((char*)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
77 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
78 FATOffset
= CurrentCluster
* 2;
79 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
80 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
82 return STATUS_UNSUCCESSFUL
;
84 CurrentCluster
= *((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
85 if (CurrentCluster
>= 0xfff8 && CurrentCluster
<= 0xffff)
86 CurrentCluster
= 0xffffffff;
88 *NextCluster
= CurrentCluster
;
89 return (STATUS_SUCCESS
);
93 Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
97 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
104 LARGE_INTEGER Offset
;
110 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
112 return STATUS_UNSUCCESSFUL
;
114 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (CurrentCluster
* 12) / 8);
115 if ((CurrentCluster
% 2) == 0)
117 Entry
= *CBlock
& 0x0fff;
121 Entry
= *CBlock
>> 4;
123 // DPRINT("Entry %x\n",Entry);
124 if (Entry
>= 0xff8 && Entry
<= 0xfff)
126 // DPRINT("Returning %x\n",Entry);
127 *NextCluster
= Entry
;
128 CcUnpinData(Context
);
129 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
130 return STATUS_SUCCESS
;
134 FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt
,
137 * FUNCTION: Finds the first available cluster in a FAT16 table
146 LARGE_INTEGER Offset
;
147 PUSHORT Block
= NULL
;
149 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
150 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+2 ) * 2;
152 StartCluster
= DeviceExt
->LastAvailableCluster
;
154 for (j
= 0; j
< 2; j
++)
156 for (i
= StartCluster
* 2; i
< FatLength
; i
+= 2, Block
++)
158 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
160 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
163 CcUnpinData(Context
);
166 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
168 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
169 return STATUS_UNSUCCESSFUL
;
172 Block
= (PUSHORT
)((char*)BaseAddress
+ i
% ChunkSize
);
177 DPRINT("Found available cluster 0x%x\n", i
/ 2);
178 DeviceExt
->LastAvailableCluster
= *Cluster
= i
/ 2;
179 CcUnpinData(Context
);
180 return(STATUS_SUCCESS
);
183 FatLength
= StartCluster
* 2;
187 CcUnpinData(Context
);
191 return(STATUS_DISK_FULL
);
195 FAT12FindAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
197 * FUNCTION: Finds the first available cluster in a FAT12 table
207 LARGE_INTEGER Offset
;
209 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
211 StartCluster
= DeviceExt
->LastAvailableCluster
;
213 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
215 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
216 return STATUS_UNSUCCESSFUL
;
219 for (j
= 0; j
< 2; j
++)
221 for (i
= StartCluster
; i
< FatLength
; i
++)
223 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
226 Entry
= *CBlock
& 0xfff;
230 Entry
= *CBlock
>> 4;
234 DPRINT("Found available cluster 0x%x\n", i
);
235 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
236 CcUnpinData(Context
);
237 return(STATUS_SUCCESS
);
240 FatLength
= StartCluster
;
243 CcUnpinData(Context
);
244 return (STATUS_DISK_FULL
);
248 FAT32FindAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
250 * FUNCTION: Finds the first available cluster in a FAT32 table
259 LARGE_INTEGER Offset
;
262 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
263 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 4;
265 StartCluster
= DeviceExt
->LastAvailableCluster
;
267 for (j
= 0; j
< 2; j
++)
269 for (i
= StartCluster
* 4; i
< FatLength
; i
+= 4, Block
++)
271 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
273 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
276 CcUnpinData(Context
);
278 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
280 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
281 return STATUS_UNSUCCESSFUL
;
283 Block
= (PULONG
)((char*)BaseAddress
+ i
% ChunkSize
);
286 if ((*Block
& 0x0fffffff) == 0)
288 DPRINT("Found available cluster 0x%x\n", i
/ 4);
289 DeviceExt
->LastAvailableCluster
= *Cluster
= i
/ 4;
290 CcUnpinData(Context
);
291 return(STATUS_SUCCESS
);
294 FatLength
= StartCluster
* 4;
298 CcUnpinData(Context
);
302 return (STATUS_DISK_FULL
);
307 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
309 * FUNCTION: Counts free cluster in a FAT12 table
316 ULONG numberofclusters
;
317 LARGE_INTEGER Offset
;
322 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
324 return STATUS_UNSUCCESSFUL
;
327 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
329 for (i
= 2; i
< numberofclusters
; i
++)
331 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
334 Entry
= *CBlock
& 0x0fff;
338 Entry
= *CBlock
>> 4;
344 CcUnpinData(Context
);
345 DeviceExt
->AvailableClusters
= ulCount
;
346 DeviceExt
->AvailableClustersValid
= TRUE
;
348 return(STATUS_SUCCESS
);
353 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
355 * FUNCTION: Counts free clusters in a FAT16 table
358 PUSHORT Block
= NULL
;
359 PVOID BaseAddress
= NULL
;
363 PVOID Context
= NULL
;
364 LARGE_INTEGER Offset
;
367 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
368 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 2;
370 for (i
= 4; i
< FatLength
; i
+= 2, Block
++)
372 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
377 CcUnpinData(Context
);
379 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
380 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
382 return STATUS_UNSUCCESSFUL
;
384 Block
= (PUSHORT
)((char*)BaseAddress
+ i
% ChunkSize
);
390 DeviceExt
->AvailableClusters
= ulCount
;
391 DeviceExt
->AvailableClustersValid
= TRUE
;
392 CcUnpinData(Context
);
394 return(STATUS_SUCCESS
);
399 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
401 * FUNCTION: Counts free clusters in a FAT32 table
405 PVOID BaseAddress
= NULL
;
409 PVOID Context
= NULL
;
410 LARGE_INTEGER Offset
;
413 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
414 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2) * 4;
416 for (i
= 8; i
< FatLength
; i
+= 4, Block
++)
418 if ((i
% ChunkSize
) == 0 || Context
== NULL
)
422 CcUnpinData(Context
);
424 Offset
.QuadPart
= ROUND_DOWN(i
, ChunkSize
);
425 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
427 return STATUS_UNSUCCESSFUL
;
429 Block
= (PULONG
)((char*)BaseAddress
+ i
% ChunkSize
);
431 if ((*Block
& 0x0fffffff) == 0)
435 DeviceExt
->AvailableClusters
= ulCount
;
436 DeviceExt
->AvailableClustersValid
= TRUE
;
437 CcUnpinData(Context
);
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
)
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(!CcMapData(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(!CcMapData(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(!CcMapData(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 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
597 Status
= FAT16WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
599 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
601 Status
= FAT32WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
605 Status
= FAT12WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
607 if (DeviceExt
->AvailableClustersValid
)
609 if (OldValue
&& NewValue
== 0)
610 InterlockedIncrement(&DeviceExt
->AvailableClusters
);
611 else if (OldValue
== 0 && NewValue
)
612 InterlockedDecrement(&DeviceExt
->AvailableClusters
);
614 ExReleaseResourceLite(&DeviceExt
->FatResource
);
619 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
622 * FUNCTION: Converts the cluster number to a sector number for this physical
626 return DeviceExt
->FatInfo
.dataStart
+
627 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
632 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
633 ULONG CurrentCluster
,
637 * FUNCTION: Retrieve the next cluster depending on the FAT type
642 DPRINT ("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
643 DeviceExt
, CurrentCluster
);
647 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
651 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
655 * If the file hasn't any clusters allocated then we need special
658 if (CurrentCluster
== 0 && Extend
)
662 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
665 Status
= FAT16FindAvailableCluster(DeviceExt
, &NewCluster
);
667 if (!NT_SUCCESS(Status
))
669 ExReleaseResourceLite(&DeviceExt
->FatResource
);
673 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
675 Status
= FAT32FindAvailableCluster(DeviceExt
, &NewCluster
);
676 if (!NT_SUCCESS(Status
))
678 ExReleaseResourceLite(&DeviceExt
->FatResource
);
684 Status
= FAT12FindAvailableCluster(DeviceExt
, &NewCluster
);
685 if (!NT_SUCCESS(Status
))
687 ExReleaseResourceLite(&DeviceExt
->FatResource
);
691 /* Mark the new AU as the EOF */
692 WriteCluster (DeviceExt
, NewCluster
, 0xFFFFFFFF);
693 *NextCluster
= NewCluster
;
694 ExReleaseResourceLite(&DeviceExt
->FatResource
);
695 return(STATUS_SUCCESS
);
697 else if (CurrentCluster
== 0)
699 ExReleaseResourceLite(&DeviceExt
->FatResource
);
700 return(STATUS_UNSUCCESSFUL
);
703 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
705 Status
= Fat16GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
707 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
709 Status
= Fat32GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
713 Status
= Fat12GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
715 if (Extend
&& (*NextCluster
) == 0xFFFFFFFF)
719 /* We are after last existing cluster, we must add one to file */
720 /* Firstly, find the next available open allocation unit */
721 if (DeviceExt
->FatInfo
.FatType
== FAT16
)
723 Status
= FAT16FindAvailableCluster(DeviceExt
, &NewCluster
);
724 if (!NT_SUCCESS(Status
))
726 ExReleaseResourceLite(&DeviceExt
->FatResource
);
730 else if (DeviceExt
->FatInfo
.FatType
== FAT32
)
732 Status
= FAT32FindAvailableCluster(DeviceExt
, &NewCluster
);
733 if (!NT_SUCCESS(Status
))
735 ExReleaseResourceLite(&DeviceExt
->FatResource
);
741 Status
= FAT12FindAvailableCluster(DeviceExt
, &NewCluster
);
742 if (!NT_SUCCESS(Status
))
744 ExReleaseResourceLite(&DeviceExt
->FatResource
);
748 /* Mark the new AU as the EOF */
749 WriteCluster(DeviceExt
, NewCluster
, 0xFFFFFFFF);
750 /* Now, write the AU of the LastCluster with the value of the newly
752 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
753 *NextCluster
= NewCluster
;
756 ExReleaseResourceLite(&DeviceExt
->FatResource
);