2 * $Id: fat.c,v 1.48 2004/12/25 11:18:38 navaraf 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 FAT16FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
,
137 * FUNCTION: Finds the first available cluster in a FAT16 table
146 LARGE_INTEGER Offset
;
150 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
151 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
153 StartCluster
= DeviceExt
->LastAvailableCluster
;
155 for (j
= 0; j
< 2; j
++)
157 for (i
= StartCluster
; i
< FatLength
; )
159 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
160 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
162 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
163 return STATUS_UNSUCCESSFUL
;
165 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
166 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
168 /* Now process the whole block */
169 while (Block
< BlockEnd
&& i
< FatLength
)
173 DPRINT("Found available cluster 0x%x\n", i
);
174 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
176 CcSetDirtyPinnedData(Context
, NULL
);
177 CcUnpinData(Context
);
178 return(STATUS_SUCCESS
);
185 CcUnpinData(Context
);
187 FatLength
= StartCluster
;
190 return(STATUS_DISK_FULL
);
194 FAT12FindAndMarkAvailableCluster(PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
196 * FUNCTION: Finds the first available cluster in a FAT12 table
206 LARGE_INTEGER Offset
;
208 FatLength
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
210 StartCluster
= DeviceExt
->LastAvailableCluster
;
212 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
214 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
);
215 return STATUS_UNSUCCESSFUL
;
218 for (j
= 0; j
< 2; j
++)
220 for (i
= StartCluster
; i
< FatLength
; i
++)
222 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
225 Entry
= *CBlock
& 0xfff;
229 Entry
= *CBlock
>> 4;
233 DPRINT("Found available cluster 0x%x\n", i
);
234 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
236 *CBlock
= (*CBlock
& 0xf000) | 0xfff;
238 *CBlock
= (*CBlock
& 0xf) | 0xfff0;
239 CcSetDirtyPinnedData(Context
, NULL
);
240 CcUnpinData(Context
);
241 return(STATUS_SUCCESS
);
244 FatLength
= StartCluster
;
247 CcUnpinData(Context
);
248 return (STATUS_DISK_FULL
);
252 FAT32FindAndMarkAvailableCluster (PDEVICE_EXTENSION DeviceExt
, PULONG Cluster
)
254 * FUNCTION: Finds the first available cluster in a FAT32 table
263 LARGE_INTEGER Offset
;
267 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
268 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
270 StartCluster
= DeviceExt
->LastAvailableCluster
;
272 for (j
= 0; j
< 2; j
++)
274 for (i
= StartCluster
; i
< FatLength
;)
276 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
277 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
279 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
280 return STATUS_UNSUCCESSFUL
;
282 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
283 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
285 /* Now process the whole block */
286 while (Block
< BlockEnd
&& i
< FatLength
)
288 if ((*Block
& 0x0fffffff) == 0)
290 DPRINT("Found available cluster 0x%x\n", i
);
291 DeviceExt
->LastAvailableCluster
= *Cluster
= i
;
293 CcSetDirtyPinnedData(Context
, NULL
);
294 CcUnpinData(Context
);
295 return(STATUS_SUCCESS
);
302 CcUnpinData(Context
);
304 FatLength
= StartCluster
;
307 return (STATUS_DISK_FULL
);
311 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
313 * FUNCTION: Counts free cluster in a FAT12 table
320 ULONG numberofclusters
;
321 LARGE_INTEGER Offset
;
326 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
328 return STATUS_UNSUCCESSFUL
;
331 numberofclusters
= DeviceExt
->FatInfo
.NumberOfClusters
+ 2;
333 for (i
= 2; i
< numberofclusters
; i
++)
335 CBlock
= (PUSHORT
)((char*)BaseAddress
+ (i
* 12) / 8);
338 Entry
= *CBlock
& 0x0fff;
342 Entry
= *CBlock
>> 4;
348 CcUnpinData(Context
);
349 DeviceExt
->AvailableClusters
= ulCount
;
350 DeviceExt
->AvailableClustersValid
= TRUE
;
352 return(STATUS_SUCCESS
);
357 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
359 * FUNCTION: Counts free clusters in a FAT16 table
364 PVOID BaseAddress
= NULL
;
368 PVOID Context
= NULL
;
369 LARGE_INTEGER Offset
;
372 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
373 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
375 for (i
= 2; i
< FatLength
; )
377 Offset
.QuadPart
= ROUND_DOWN(i
* 2, ChunkSize
);
378 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
380 return STATUS_UNSUCCESSFUL
;
382 Block
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ (i
* 2) % ChunkSize
);
383 BlockEnd
= (PUSHORT
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
385 /* Now process the whole block */
386 while (Block
< BlockEnd
&& i
< FatLength
)
394 CcUnpinData(Context
);
397 DeviceExt
->AvailableClusters
= ulCount
;
398 DeviceExt
->AvailableClustersValid
= TRUE
;
400 return(STATUS_SUCCESS
);
405 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
)
407 * FUNCTION: Counts free clusters in a FAT32 table
412 PVOID BaseAddress
= NULL
;
416 PVOID Context
= NULL
;
417 LARGE_INTEGER Offset
;
420 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
421 FatLength
= (DeviceExt
->FatInfo
.NumberOfClusters
+ 2);
423 for (i
= 2; i
< FatLength
; )
425 Offset
.QuadPart
= ROUND_DOWN(i
* 4, ChunkSize
);
426 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
428 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG
)Offset
.QuadPart
, ChunkSize
);
429 return STATUS_UNSUCCESSFUL
;
431 Block
= (PULONG
)((ULONG_PTR
)BaseAddress
+ (i
* 4) % ChunkSize
);
432 BlockEnd
= (PULONG
)((ULONG_PTR
)BaseAddress
+ ChunkSize
);
434 /* Now process the whole block */
435 while (Block
< BlockEnd
&& i
< FatLength
)
437 if ((*Block
& 0x0fffffff) == 0)
443 CcUnpinData(Context
);
446 DeviceExt
->AvailableClusters
= ulCount
;
447 DeviceExt
->AvailableClustersValid
= TRUE
;
449 return(STATUS_SUCCESS
);
453 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt
,
454 PLARGE_INTEGER Clusters
)
456 NTSTATUS Status
= STATUS_SUCCESS
;
457 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
458 if (!DeviceExt
->AvailableClustersValid
)
460 if (DeviceExt
->FatInfo
.FatType
== FAT12
)
461 Status
= FAT12CountAvailableClusters(DeviceExt
);
462 else if (DeviceExt
->FatInfo
.FatType
== FAT16
|| DeviceExt
->FatInfo
.FatType
== FATX16
)
463 Status
= FAT16CountAvailableClusters(DeviceExt
);
465 Status
= FAT32CountAvailableClusters(DeviceExt
);
467 Clusters
->QuadPart
= DeviceExt
->AvailableClusters
;
468 ExReleaseResourceLite (&DeviceExt
->FatResource
);
478 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt
,
479 ULONG ClusterToWrite
,
483 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
491 LARGE_INTEGER Offset
;
494 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
, 1, &Context
, &BaseAddress
))
496 return STATUS_UNSUCCESSFUL
;
498 CBlock
= (PUCHAR
)BaseAddress
;
500 FATOffset
= (ClusterToWrite
* 12) / 8;
501 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
502 NewValue
, ClusterToWrite
, FATOffset
);
503 if ((ClusterToWrite
% 2) == 0)
505 *OldValue
= CBlock
[FATOffset
] + ((CBlock
[FATOffset
+ 1] & 0x0f) << 8);
506 CBlock
[FATOffset
] = (UCHAR
)NewValue
;
507 CBlock
[FATOffset
+ 1] &= 0xf0;
508 CBlock
[FATOffset
+ 1] |= (NewValue
& 0xf00) >> 8;
512 *OldValue
= (CBlock
[FATOffset
] >> 4) + (CBlock
[FATOffset
+ 1] << 4);
513 CBlock
[FATOffset
] &= 0x0f;
514 CBlock
[FATOffset
] |= (NewValue
& 0xf) << 4;
515 CBlock
[FATOffset
+ 1] = (UCHAR
)(NewValue
>> 4);
517 /* Write the changed FAT sector(s) to disk */
518 FATsector
= FATOffset
/ DeviceExt
->FatInfo
.BytesPerSector
;
519 CcSetDirtyPinnedData(Context
, NULL
);
520 CcUnpinData(Context
);
521 return(STATUS_SUCCESS
);
525 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt
,
526 ULONG ClusterToWrite
,
530 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
537 LARGE_INTEGER Offset
;
540 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
541 FATOffset
= ClusterToWrite
* 2;
542 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
543 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
545 return STATUS_UNSUCCESSFUL
;
547 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
549 Cluster
= ((PUSHORT
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
550 *OldValue
= *Cluster
;
551 *Cluster
= (USHORT
)NewValue
;
552 CcSetDirtyPinnedData(Context
, NULL
);
553 CcUnpinData(Context
);
554 return(STATUS_SUCCESS
);
558 FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt
,
559 ULONG ClusterToWrite
,
563 * FUNCTION: Writes a cluster to the FAT32 physical tables
570 LARGE_INTEGER Offset
;
573 ChunkSize
= CACHEPAGESIZE(DeviceExt
);
575 FATOffset
= (ClusterToWrite
* 4);
576 Offset
.QuadPart
= ROUND_DOWN(FATOffset
, ChunkSize
);
577 if(!CcMapData(DeviceExt
->FATFileObject
, &Offset
, ChunkSize
, 1, &Context
, &BaseAddress
))
579 return STATUS_UNSUCCESSFUL
;
581 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue
, FATOffset
,
583 Cluster
= ((PULONG
)((char*)BaseAddress
+ (FATOffset
% ChunkSize
)));
584 *OldValue
= *Cluster
& 0x0fffffff;
585 *Cluster
= (*Cluster
& 0xf0000000) | (NewValue
& 0x0fffffff);
587 CcSetDirtyPinnedData(Context
, NULL
);
588 CcUnpinData(Context
);
590 return(STATUS_SUCCESS
);
595 WriteCluster(PDEVICE_EXTENSION DeviceExt
,
596 ULONG ClusterToWrite
,
599 * FUNCTION: Write a changed FAT entry
604 ExAcquireResourceExclusiveLite (&DeviceExt
->FatResource
, TRUE
);
605 Status
= DeviceExt
->WriteCluster(DeviceExt
, ClusterToWrite
, NewValue
, &OldValue
);
606 if (DeviceExt
->AvailableClustersValid
)
608 if (OldValue
&& NewValue
== 0)
609 InterlockedIncrement((PLONG
)&DeviceExt
->AvailableClusters
);
610 else if (OldValue
== 0 && NewValue
)
611 InterlockedDecrement((PLONG
)&DeviceExt
->AvailableClusters
);
613 ExReleaseResourceLite(&DeviceExt
->FatResource
);
618 ClusterToSector(PDEVICE_EXTENSION DeviceExt
,
621 * FUNCTION: Converts the cluster number to a sector number for this physical
625 return DeviceExt
->FatInfo
.dataStart
+
626 ((ULONGLONG
)(Cluster
- 2) * DeviceExt
->FatInfo
.SectorsPerCluster
);
631 GetNextCluster(PDEVICE_EXTENSION DeviceExt
,
632 ULONG CurrentCluster
,
635 * FUNCTION: Retrieve the next cluster depending on the FAT type
640 DPRINT ("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
641 DeviceExt
, CurrentCluster
);
643 if (CurrentCluster
== 0)
644 return(STATUS_INVALID_PARAMETER
);
646 ExAcquireResourceSharedLite(&DeviceExt
->FatResource
, TRUE
);
647 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
648 ExReleaseResourceLite(&DeviceExt
->FatResource
);
654 GetNextClusterExtend(PDEVICE_EXTENSION DeviceExt
,
655 ULONG CurrentCluster
,
658 * FUNCTION: Retrieve the next cluster depending on the FAT type
663 DPRINT ("GetNextClusterExtend(DeviceExt %x, CurrentCluster %x)\n",
664 DeviceExt
, CurrentCluster
);
666 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
669 * If the file hasn't any clusters allocated then we need special
672 if (CurrentCluster
== 0)
676 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
677 if (!NT_SUCCESS(Status
))
679 ExReleaseResourceLite(&DeviceExt
->FatResource
);
683 *NextCluster
= NewCluster
;
684 ExReleaseResourceLite(&DeviceExt
->FatResource
);
685 return(STATUS_SUCCESS
);
688 Status
= DeviceExt
->GetNextCluster(DeviceExt
, CurrentCluster
, NextCluster
);
690 if ((*NextCluster
) == 0xFFFFFFFF)
694 /* We are after last existing cluster, we must add one to file */
695 /* Firstly, find the next available open allocation unit and
696 mark it as end of file */
697 Status
= DeviceExt
->FindAndMarkAvailableCluster(DeviceExt
, &NewCluster
);
698 if (!NT_SUCCESS(Status
))
700 ExReleaseResourceLite(&DeviceExt
->FatResource
);
704 /* Now, write the AU of the LastCluster with the value of the newly
706 WriteCluster(DeviceExt
, CurrentCluster
, NewCluster
);
707 *NextCluster
= NewCluster
;
710 ExReleaseResourceLite(&DeviceExt
->FatResource
);