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
47 ChunkSize = CACHEPAGESIZE(DeviceExt);
48 FATOffset = CurrentCluster * sizeof(ULONG);
49 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
50 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
52 return STATUS_UNSUCCESSFUL;
54 CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff;
55 if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
56 CurrentCluster = 0xffffffff;
58 *NextCluster = CurrentCluster;
59 return (STATUS_SUCCESS);
63 Fat16GetNextCluster(PDEVICE_EXTENSION DeviceExt,
67 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
76 ChunkSize = CACHEPAGESIZE(DeviceExt);
77 FATOffset = CurrentCluster * 2;
78 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
79 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
81 return STATUS_UNSUCCESSFUL;
83 CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
84 if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
85 CurrentCluster = 0xffffffff;
87 *NextCluster = CurrentCluster;
88 return (STATUS_SUCCESS);
92 Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt,
96 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
103 LARGE_INTEGER Offset;
109 if(!CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
111 return STATUS_UNSUCCESSFUL;
113 CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8);
114 if ((CurrentCluster % 2) == 0)
116 Entry = *CBlock & 0x0fff;
120 Entry = *CBlock >> 4;
122 // DPRINT("Entry %x\n",Entry);
123 if (Entry >= 0xff8 && Entry <= 0xfff)
125 // DPRINT("Returning %x\n",Entry);
126 *NextCluster = Entry;
127 CcUnpinData(Context);
128 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
129 return STATUS_SUCCESS;
133 FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt,
136 * FUNCTION: Finds the first available cluster in a FAT16 table
145 LARGE_INTEGER Offset;
148 ChunkSize = CACHEPAGESIZE(DeviceExt);
149 FatLength = (DeviceExt->FatInfo.NumberOfClusters +2 ) * 2;
151 StartCluster = DeviceExt->LastAvailableCluster;
153 for (j = 0; j < 2; j++)
155 for (i = StartCluster * 2; i < FatLength; i += 2, Block++)
157 if ((i % ChunkSize) == 0 || Context == NULL)
159 Offset.QuadPart = ROUND_DOWN(i, ChunkSize);
162 CcUnpinData(Context);
165 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
167 DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
168 return STATUS_UNSUCCESSFUL;
171 Block = (PUSHORT)((char*)BaseAddress + i % ChunkSize);
176 DPRINT("Found available cluster 0x%x\n", i / 2);
177 DeviceExt->LastAvailableCluster = *Cluster = i / 2;
178 CcUnpinData(Context);
179 return(STATUS_SUCCESS);
182 FatLength = StartCluster * 2;
186 CcUnpinData(Context);
190 return(STATUS_DISK_FULL);
194 FAT12FindAvailableCluster(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;
235 CcUnpinData(Context);
236 return(STATUS_SUCCESS);
239 FatLength = StartCluster;
242 CcUnpinData(Context);
243 return (STATUS_DISK_FULL);
247 FAT32FindAvailableCluster (PDEVICE_EXTENSION DeviceExt, PULONG Cluster)
249 * FUNCTION: Finds the first available cluster in a FAT32 table
258 LARGE_INTEGER Offset;
261 ChunkSize = CACHEPAGESIZE(DeviceExt);
262 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2) * 4;
264 StartCluster = DeviceExt->LastAvailableCluster;
266 for (j = 0; j < 2; j++)
268 for (i = StartCluster * 4; i < FatLength; i += 4, Block++)
270 if ((i % ChunkSize) == 0 || Context == NULL)
272 Offset.QuadPart = ROUND_DOWN(i, ChunkSize);
275 CcUnpinData(Context);
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)((char*)BaseAddress + i % ChunkSize);
285 if ((*Block & 0x0fffffff) == 0)
287 DPRINT("Found available cluster 0x%x\n", i / 4);
288 DeviceExt->LastAvailableCluster = *Cluster = i / 4;
289 CcUnpinData(Context);
290 return(STATUS_SUCCESS);
293 FatLength = StartCluster * 4;
297 CcUnpinData(Context);
301 return (STATUS_DISK_FULL);
306 FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
308 * FUNCTION: Counts free cluster in a FAT12 table
315 ULONG numberofclusters;
316 LARGE_INTEGER Offset;
321 if(!CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
323 return STATUS_UNSUCCESSFUL;
326 numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2;
328 for (i = 2; i < numberofclusters; i++)
330 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
333 Entry = *CBlock & 0x0fff;
337 Entry = *CBlock >> 4;
343 CcUnpinData(Context);
344 DeviceExt->AvailableClusters = ulCount;
345 DeviceExt->AvailableClustersValid = TRUE;
347 return(STATUS_SUCCESS);
352 FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
354 * FUNCTION: Counts free clusters in a FAT16 table
358 PVOID BaseAddress = NULL;
362 PVOID Context = NULL;
363 LARGE_INTEGER Offset;
366 ChunkSize = CACHEPAGESIZE(DeviceExt);
367 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2) * 2;
369 for (i = 4; i< FatLength; i += 2, Block++)
371 if ((i % ChunkSize) == 0 || Context == NULL)
376 CcUnpinData(Context);
378 Offset.QuadPart = ROUND_DOWN(i, ChunkSize);
379 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
381 return STATUS_UNSUCCESSFUL;
383 Block = (PUSHORT)((char*)BaseAddress + i % ChunkSize);
389 DeviceExt->AvailableClusters = ulCount;
390 DeviceExt->AvailableClustersValid = TRUE;
391 CcUnpinData(Context);
393 return(STATUS_SUCCESS);
398 FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
400 * FUNCTION: Counts free clusters in a FAT32 table
404 PVOID BaseAddress = NULL;
408 PVOID Context = NULL;
409 LARGE_INTEGER Offset;
412 ChunkSize = CACHEPAGESIZE(DeviceExt);
413 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2) * 4;
415 for (i = 8; i< FatLength; i += 4, Block++)
417 if ((i % ChunkSize) == 0 || Context == NULL)
421 CcUnpinData(Context);
423 Offset.QuadPart = ROUND_DOWN(i, ChunkSize);
424 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
426 return STATUS_UNSUCCESSFUL;
428 Block = (PULONG)((char*)BaseAddress + i % ChunkSize);
430 if ((*Block & 0x0fffffff) == 0)
434 DeviceExt->AvailableClusters = ulCount;
435 DeviceExt->AvailableClustersValid = TRUE;
436 CcUnpinData(Context);
438 return(STATUS_SUCCESS);
442 CountAvailableClusters(PDEVICE_EXTENSION DeviceExt,
443 PLARGE_INTEGER Clusters)
445 NTSTATUS Status = STATUS_SUCCESS;
446 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
447 if (!DeviceExt->AvailableClustersValid)
449 if (DeviceExt->FatInfo.FatType == FAT12)
450 Status = FAT12CountAvailableClusters(DeviceExt);
451 else if (DeviceExt->FatInfo.FatType == FAT16)
452 Status = FAT16CountAvailableClusters(DeviceExt);
454 Status = FAT32CountAvailableClusters(DeviceExt);
456 Clusters->QuadPart = DeviceExt->AvailableClusters;
457 ExReleaseResourceLite (&DeviceExt->FatResource);
467 FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt,
468 ULONG ClusterToWrite,
472 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
480 LARGE_INTEGER Offset;
483 if(!CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
485 return STATUS_UNSUCCESSFUL;
487 CBlock = (PUCHAR)BaseAddress;
489 FATOffset = (ClusterToWrite * 12) / 8;
490 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
491 NewValue, ClusterToWrite, FATOffset);
492 if ((ClusterToWrite % 2) == 0)
494 *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8);
495 CBlock[FATOffset] = (UCHAR)NewValue;
496 CBlock[FATOffset + 1] &= 0xf0;
497 CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8;
501 *OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4);
502 CBlock[FATOffset] &= 0x0f;
503 CBlock[FATOffset] |= (NewValue & 0xf) << 4;
504 CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4);
506 /* Write the changed FAT sector(s) to disk */
507 FATsector = FATOffset / DeviceExt->FatInfo.BytesPerSector;
508 CcSetDirtyPinnedData(Context, NULL);
509 CcUnpinData(Context);
510 return(STATUS_SUCCESS);
514 FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt,
515 ULONG ClusterToWrite,
519 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
526 LARGE_INTEGER Offset;
529 ChunkSize = CACHEPAGESIZE(DeviceExt);
530 FATOffset = ClusterToWrite * 2;
531 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
532 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
534 return STATUS_UNSUCCESSFUL;
536 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
538 Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
539 *OldValue = *Cluster;
540 *Cluster = (USHORT)NewValue;
541 CcSetDirtyPinnedData(Context, NULL);
542 CcUnpinData(Context);
543 return(STATUS_SUCCESS);
547 FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt,
548 ULONG ClusterToWrite,
552 * FUNCTION: Writes a cluster to the FAT32 physical tables
559 LARGE_INTEGER Offset;
562 ChunkSize = CACHEPAGESIZE(DeviceExt);
564 FATOffset = (ClusterToWrite * 4);
565 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
566 if(!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
568 return STATUS_UNSUCCESSFUL;
570 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
572 Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize)));
573 *OldValue = *Cluster & 0x0fffffff;
574 *Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff);
576 CcSetDirtyPinnedData(Context, NULL);
577 CcUnpinData(Context);
579 return(STATUS_SUCCESS);
584 WriteCluster(PDEVICE_EXTENSION DeviceExt,
585 ULONG ClusterToWrite,
588 * FUNCTION: Write a changed FAT entry
593 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
594 if (DeviceExt->FatInfo.FatType == FAT16)
596 Status = FAT16WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
598 else if (DeviceExt->FatInfo.FatType == FAT32)
600 Status = FAT32WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
604 Status = FAT12WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
606 if (DeviceExt->AvailableClustersValid)
608 if (OldValue && NewValue == 0)
609 InterlockedIncrement(&DeviceExt->AvailableClusters);
610 else if (OldValue == 0 && NewValue)
611 InterlockedDecrement(&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,
636 * FUNCTION: Retrieve the next cluster depending on the FAT type
641 DPRINT ("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
642 DeviceExt, CurrentCluster);
646 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
650 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
654 * If the file hasn't any clusters allocated then we need special
657 if (CurrentCluster == 0 && Extend)
661 if (DeviceExt->FatInfo.FatType == FAT16)
664 Status = FAT16FindAvailableCluster(DeviceExt, &NewCluster);
666 if (!NT_SUCCESS(Status))
668 ExReleaseResourceLite(&DeviceExt->FatResource);
672 else if (DeviceExt->FatInfo.FatType == FAT32)
674 Status = FAT32FindAvailableCluster(DeviceExt, &NewCluster);
675 if (!NT_SUCCESS(Status))
677 ExReleaseResourceLite(&DeviceExt->FatResource);
683 Status = FAT12FindAvailableCluster(DeviceExt, &NewCluster);
684 if (!NT_SUCCESS(Status))
686 ExReleaseResourceLite(&DeviceExt->FatResource);
690 /* Mark the new AU as the EOF */
691 WriteCluster (DeviceExt, NewCluster, 0xFFFFFFFF);
692 *NextCluster = NewCluster;
693 ExReleaseResourceLite(&DeviceExt->FatResource);
694 return(STATUS_SUCCESS);
696 else if (CurrentCluster == 0)
698 ExReleaseResourceLite(&DeviceExt->FatResource);
699 return(STATUS_UNSUCCESSFUL);
702 if (DeviceExt->FatInfo.FatType == FAT16)
704 Status = Fat16GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
706 else if (DeviceExt->FatInfo.FatType == FAT32)
708 Status = Fat32GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
712 Status = Fat12GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
714 if (Extend && (*NextCluster) == 0xFFFFFFFF)
718 /* We are after last existing cluster, we must add one to file */
719 /* Firstly, find the next available open allocation unit */
720 if (DeviceExt->FatInfo.FatType == FAT16)
722 Status = FAT16FindAvailableCluster(DeviceExt, &NewCluster);
723 if (!NT_SUCCESS(Status))
725 ExReleaseResourceLite(&DeviceExt->FatResource);
729 else if (DeviceExt->FatInfo.FatType == FAT32)
731 Status = FAT32FindAvailableCluster(DeviceExt, &NewCluster);
732 if (!NT_SUCCESS(Status))
734 ExReleaseResourceLite(&DeviceExt->FatResource);
740 Status = FAT12FindAvailableCluster(DeviceExt, &NewCluster);
741 if (!NT_SUCCESS(Status))
743 ExReleaseResourceLite(&DeviceExt->FatResource);
747 /* Mark the new AU as the EOF */
748 WriteCluster(DeviceExt, NewCluster, 0xFFFFFFFF);
749 /* Now, write the AU of the LastCluster with the value of the newly
751 WriteCluster(DeviceExt, CurrentCluster, NewCluster);
752 *NextCluster = NewCluster;
755 ExReleaseResourceLite(&DeviceExt->FatResource);