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);