2 Routines dealing with disk spaces and FAT table entries.
4 Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
16 Get the FAT entry of the volume, which is identified with the Index.
18 @param Volume - FAT file system volume.
19 @param Index - The index of the FAT entry of the volume.
21 @return The buffer of the FAT entry
27 IN FAT_VOLUME
*Volume
,
34 if (Index
> (Volume
->MaxCluster
+ 1)) {
35 Volume
->FatEntryBuffer
= (UINT32
) -1;
36 return &Volume
->FatEntryBuffer
;
39 // Compute buffer position needed
41 switch (Volume
->FatType
) {
43 Pos
= FAT_POS_FAT12 (Index
);
47 Pos
= FAT_POS_FAT16 (Index
);
51 Pos
= FAT_POS_FAT32 (Index
);
54 // Set the position and read the buffer
56 Volume
->FatEntryPos
= Volume
->FatPos
+ Pos
;
62 &Volume
->FatEntryBuffer
,
65 if (EFI_ERROR (Status
)) {
66 Volume
->FatEntryBuffer
= (UINT32
) -1;
69 return &Volume
->FatEntryBuffer
;
74 Get the FAT entry value of the volume, which is identified with the Index.
76 @param Volume - FAT file system volume.
77 @param Index - The index of the FAT entry of the volume.
79 @return The value of the FAT entry.
85 IN FAT_VOLUME
*Volume
,
95 Pos
= FatLoadFatEntry (Volume
, Index
);
97 if (Index
> (Volume
->MaxCluster
+ 1)) {
101 switch (Volume
->FatType
) {
104 Accum
= En12
[0] | (En12
[1] << 8);
105 Accum
= FAT_ODD_CLUSTER_FAT12 (Index
) ? (Accum
>> 4) : (Accum
& FAT_CLUSTER_MASK_FAT12
);
106 Accum
= Accum
| ((Accum
>= FAT_CLUSTER_SPECIAL_FAT12
) ? FAT_CLUSTER_SPECIAL_EXT
: 0);
112 Accum
= Accum
| ((Accum
>= FAT_CLUSTER_SPECIAL_FAT16
) ? FAT_CLUSTER_SPECIAL_EXT
: 0);
117 Accum
= *En32
& FAT_CLUSTER_MASK_FAT32
;
118 Accum
= Accum
| ((Accum
>= FAT_CLUSTER_SPECIAL_FAT32
) ? FAT_CLUSTER_SPECIAL_EXT
: 0);
126 Set the FAT entry value of the volume, which is identified with the Index.
128 @param Volume - FAT file system volume.
129 @param Index - The index of the FAT entry of the volume.
130 @param Value - The new value of the FAT entry.
132 @retval EFI_SUCCESS - Set the new FAT entry value successfully.
133 @retval EFI_VOLUME_CORRUPTED - The FAT type of the volume is error.
134 @return other - An error occurred when operation the FAT entries.
140 IN FAT_VOLUME
*Volume
,
153 if (Index
< FAT_MIN_CLUSTER
) {
154 return EFI_VOLUME_CORRUPTED
;
157 OriginalVal
= FatGetFatEntry (Volume
, Index
);
158 if (Value
== FAT_CLUSTER_FREE
&& OriginalVal
!= FAT_CLUSTER_FREE
) {
159 Volume
->FatInfoSector
.FreeInfo
.ClusterCount
+= 1;
160 if (Index
< Volume
->FatInfoSector
.FreeInfo
.NextCluster
) {
161 Volume
->FatInfoSector
.FreeInfo
.NextCluster
= (UINT32
) Index
;
163 } else if (Value
!= FAT_CLUSTER_FREE
&& OriginalVal
== FAT_CLUSTER_FREE
) {
164 if (Volume
->FatInfoSector
.FreeInfo
.ClusterCount
!= 0) {
165 Volume
->FatInfoSector
.FreeInfo
.ClusterCount
-= 1;
169 // Make sure the entry is in memory
171 Pos
= FatLoadFatEntry (Volume
, Index
);
176 switch (Volume
->FatType
) {
179 Accum
= En12
[0] | (En12
[1] << 8);
180 Value
= Value
& FAT_CLUSTER_MASK_FAT12
;
182 if (FAT_ODD_CLUSTER_FAT12 (Index
)) {
183 Accum
= (Value
<< 4) | (Accum
& 0xF);
185 Accum
= Value
| (Accum
& FAT_CLUSTER_UNMASK_FAT12
);
188 En12
[0] = (UINT8
) (Accum
& 0xFF);
189 En12
[1] = (UINT8
) (Accum
>> 8);
194 *En16
= (UINT16
) Value
;
199 *En32
= (*En32
& FAT_CLUSTER_UNMASK_FAT32
) | (UINT32
) (Value
& FAT_CLUSTER_MASK_FAT32
);
202 // If the volume's dirty bit is not set, set it now
204 if (!Volume
->FatDirty
&& Volume
->FatType
!= Fat12
) {
205 Volume
->FatDirty
= TRUE
;
206 FatAccessVolumeDirty (Volume
, WriteFat
, &Volume
->DirtyValue
);
209 // Write the updated fat entry value to the volume
210 // The fat is the first fat, and other fat will be in sync
211 // when the FAT cache flush back.
217 Volume
->FatEntrySize
,
218 &Volume
->FatEntryBuffer
,
226 Free the cluster chain.
228 @param Volume - FAT file system volume.
229 @param Cluster - The first cluster of cluster chain.
231 @retval EFI_SUCCESS - The cluster chain is freed successfully.
232 @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters.
238 IN FAT_VOLUME
*Volume
,
244 while (!FAT_END_OF_FAT_CHAIN (Cluster
)) {
245 if (Cluster
== FAT_CLUSTER_FREE
|| Cluster
>= FAT_CLUSTER_SPECIAL
) {
247 DEBUG ((EFI_D_INIT
| EFI_D_ERROR
, "FatShrinkEof: cluster chain corrupt\n"));
248 return EFI_VOLUME_CORRUPTED
;
251 LastCluster
= Cluster
;
252 Cluster
= FatGetFatEntry (Volume
, Cluster
);
253 FatSetFatEntry (Volume
, LastCluster
, FAT_CLUSTER_FREE
);
261 Allocate a free cluster and return the cluster index.
263 @param Volume - FAT file system volume.
265 @return The index of the free cluster
271 IN FAT_VOLUME
*Volume
277 // Start looking at FatFreePos for the next unallocated cluster
279 if (Volume
->DiskError
) {
280 return (UINTN
) FAT_CLUSTER_LAST
;
285 // If the end of the list, return no available cluster
287 if (Volume
->FatInfoSector
.FreeInfo
.NextCluster
> (Volume
->MaxCluster
+ 1)) {
288 if (Volume
->FreeInfoValid
&& 0 < (INT32
) (Volume
->FatInfoSector
.FreeInfo
.ClusterCount
)) {
289 Volume
->FreeInfoValid
= FALSE
;
292 FatComputeFreeInfo (Volume
);
293 if (Volume
->FatInfoSector
.FreeInfo
.NextCluster
> (Volume
->MaxCluster
+ 1)) {
294 return (UINTN
) FAT_CLUSTER_LAST
;
298 Cluster
= FatGetFatEntry (Volume
, Volume
->FatInfoSector
.FreeInfo
.NextCluster
);
299 if (Cluster
== FAT_CLUSTER_FREE
) {
303 // Try the next cluster
305 Volume
->FatInfoSector
.FreeInfo
.NextCluster
+= 1;
308 Cluster
= Volume
->FatInfoSector
.FreeInfo
.NextCluster
;
309 Volume
->FatInfoSector
.FreeInfo
.NextCluster
+= 1;
315 Count the number of clusters given a size.
317 @param Volume - The file system volume.
318 @param Size - The size in bytes.
320 @return The number of the clusters.
326 IN FAT_VOLUME
*Volume
,
332 Clusters
= Size
>> Volume
->ClusterAlignment
;
333 if ((Size
& (Volume
->ClusterSize
- 1)) > 0) {
342 Shrink the end of the open file base on the file size.
344 @param OFile - The open file.
346 @retval EFI_SUCCESS - Shrinked successfully.
347 @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters.
361 Volume
= OFile
->Volume
;
362 ASSERT_VOLUME_LOCKED (Volume
);
364 NewSize
= FatSizeToClusters (Volume
, OFile
->FileSize
);
367 // Find the address of the last cluster
369 Cluster
= OFile
->FileCluster
;
370 LastCluster
= FAT_CLUSTER_FREE
;
374 for (CurSize
= 0; CurSize
< NewSize
; CurSize
++) {
375 if (Cluster
== FAT_CLUSTER_FREE
|| Cluster
>= FAT_CLUSTER_SPECIAL
) {
377 DEBUG ((EFI_D_INIT
| EFI_D_ERROR
, "FatShrinkEof: cluster chain corrupt\n"));
378 return EFI_VOLUME_CORRUPTED
;
381 LastCluster
= Cluster
;
382 Cluster
= FatGetFatEntry (Volume
, Cluster
);
385 FatSetFatEntry (Volume
, LastCluster
, (UINTN
) FAT_CLUSTER_LAST
);
389 // Check to see if the file is already completely truncated
391 if (Cluster
== FAT_CLUSTER_FREE
) {
395 // The file is being completely truncated.
397 OFile
->FileCluster
= FAT_CLUSTER_FREE
;
400 // Set CurrentCluster == FileCluster
401 // to force a recalculation of Position related stuffs
403 OFile
->FileCurrentCluster
= OFile
->FileCluster
;
404 OFile
->FileLastCluster
= LastCluster
;
407 // Free the remaining cluster chain
409 return FatFreeClusters (Volume
, Cluster
);
414 Grow the end of the open file base on the NewSizeInBytes.
416 @param OFile - The open file.
417 @param NewSizeInBytes - The new size in bytes of the open file.
419 @retval EFI_SUCCESS - The file is grown successfully.
420 @retval EFI_UNSUPPORTED - The file size is larger than 4GB.
421 @retval EFI_VOLUME_CORRUPTED - There are errors in the files' clusters.
422 @retval EFI_VOLUME_FULL - The volume is full and can not grow the file.
428 IN UINT64 NewSizeInBytes
441 // For FAT file system, the max file is 4GB.
443 if (NewSizeInBytes
> 0x0FFFFFFFFL
) {
444 return EFI_UNSUPPORTED
;
447 Volume
= OFile
->Volume
;
448 ASSERT_VOLUME_LOCKED (Volume
);
450 // If the file is already large enough, do nothing
452 CurSize
= FatSizeToClusters (Volume
, OFile
->FileSize
);
453 NewSize
= FatSizeToClusters (Volume
, (UINTN
) NewSizeInBytes
);
455 if (CurSize
< NewSize
) {
457 // If we haven't found the files last cluster do it now
459 if ((OFile
->FileCluster
!= 0) && (OFile
->FileLastCluster
== 0)) {
460 Cluster
= OFile
->FileCluster
;
463 while (!FAT_END_OF_FAT_CHAIN (Cluster
)) {
464 if (Cluster
< FAT_MIN_CLUSTER
|| Cluster
> Volume
->MaxCluster
+ 1) {
467 (EFI_D_INIT
| EFI_D_ERROR
,
468 "FatGrowEof: cluster chain corrupt\n")
470 Status
= EFI_VOLUME_CORRUPTED
;
475 OFile
->FileLastCluster
= Cluster
;
476 Cluster
= FatGetFatEntry (Volume
, Cluster
);
479 if (ClusterCount
!= CurSize
) {
481 (EFI_D_INIT
| EFI_D_ERROR
,
482 "FatGrowEof: cluster chain size does not match file size\n")
484 Status
= EFI_VOLUME_CORRUPTED
;
490 // Loop until we've allocated enough space
492 LastCluster
= OFile
->FileLastCluster
;
494 while (CurSize
< NewSize
) {
495 NewCluster
= FatAllocateCluster (Volume
);
496 if (FAT_END_OF_FAT_CHAIN (NewCluster
)) {
497 if (LastCluster
!= FAT_CLUSTER_FREE
) {
498 FatSetFatEntry (Volume
, LastCluster
, (UINTN
) FAT_CLUSTER_LAST
);
499 OFile
->FileLastCluster
= LastCluster
;
502 Status
= EFI_VOLUME_FULL
;
506 if (NewCluster
< FAT_MIN_CLUSTER
|| NewCluster
> Volume
->MaxCluster
+ 1) {
507 Status
= EFI_VOLUME_CORRUPTED
;
511 if (LastCluster
!= 0) {
512 FatSetFatEntry (Volume
, LastCluster
, NewCluster
);
514 OFile
->FileCluster
= NewCluster
;
515 OFile
->FileCurrentCluster
= NewCluster
;
518 LastCluster
= NewCluster
;
522 // Terminate the cluster list
524 // Note that we must do this EVERY time we allocate a cluster, because
525 // FatAllocateCluster scans the FAT looking for a free cluster and
526 // "LastCluster" is no longer free! Usually, FatAllocateCluster will
527 // start looking with the cluster after "LastCluster"; however, when
528 // there is only one free cluster left, it will find "LastCluster"
529 // a second time. There are other, less predictable scenarios
530 // where this could happen, as well.
532 FatSetFatEntry (Volume
, LastCluster
, (UINTN
) FAT_CLUSTER_LAST
);
533 OFile
->FileLastCluster
= LastCluster
;
537 OFile
->FileSize
= (UINTN
) NewSizeInBytes
;
542 FatShrinkEof (OFile
);
548 Seek OFile to requested position, and calculate the number of
549 consecutive clusters from the position in the file
551 @param OFile - The open file.
552 @param Position - The file's position which will be accessed.
553 @param PosLimit - The maximum length current reading/writing may access
555 @retval EFI_SUCCESS - Set the info successfully.
556 @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt.
572 Volume
= OFile
->Volume
;
573 ClusterSize
= Volume
->ClusterSize
;
575 ASSERT_VOLUME_LOCKED (Volume
);
578 // If this is the fixed root dir, then compute its position
579 // from its fixed info in the fat bpb
581 if (OFile
->IsFixedRootDir
) {
582 OFile
->PosDisk
= Volume
->RootPos
+ Position
;
583 Run
= OFile
->FileSize
- Position
;
586 // Run the file's cluster chain to find the current position
587 // If possible, run from the current cluster rather than
588 // start from beginning
589 // Assumption: OFile->Position is always consistent with
590 // OFile->FileCurrentCluster.
591 // OFile->Position is not modified outside this function;
592 // OFile->FileCurrentCluster is modified outside this function
593 // to be the same as OFile->FileCluster
594 // when OFile->FileCluster is updated, so make a check of this
595 // and invalidate the original OFile->Position in this case
597 Cluster
= OFile
->FileCurrentCluster
;
598 StartPos
= OFile
->Position
;
599 if (Position
< StartPos
|| OFile
->FileCluster
== Cluster
) {
601 Cluster
= OFile
->FileCluster
;
604 while (StartPos
+ ClusterSize
<= Position
) {
605 StartPos
+= ClusterSize
;
606 if (Cluster
== FAT_CLUSTER_FREE
|| (Cluster
>= FAT_CLUSTER_SPECIAL
)) {
607 DEBUG ((EFI_D_INIT
| EFI_D_ERROR
, "FatOFilePosition:"" cluster chain corrupt\n"));
608 return EFI_VOLUME_CORRUPTED
;
611 Cluster
= FatGetFatEntry (Volume
, Cluster
);
614 if (Cluster
< FAT_MIN_CLUSTER
|| Cluster
> Volume
->MaxCluster
+ 1) {
615 return EFI_VOLUME_CORRUPTED
;
618 OFile
->PosDisk
= Volume
->FirstClusterPos
+
619 LShiftU64 (Cluster
- FAT_MIN_CLUSTER
, Volume
->ClusterAlignment
) +
621 OFile
->FileCurrentCluster
= Cluster
;
622 OFile
->Position
= StartPos
;
625 // Compute the number of consecutive clusters in the file
627 Run
= StartPos
+ ClusterSize
- Position
;
628 if (!FAT_END_OF_FAT_CHAIN (Cluster
)) {
629 while ((FatGetFatEntry (Volume
, Cluster
) == Cluster
+ 1) && Run
< PosLimit
) {
642 Get the size of directory of the open file.
644 @param Volume - The File System Volume.
645 @param Cluster - The Starting cluster.
647 @return The physical size of the file starting at the input cluster, if there is error in the
648 cluster chain, the return value is 0.
653 IN FAT_VOLUME
*Volume
,
658 ASSERT_VOLUME_LOCKED (Volume
);
660 // Run the cluster chain for the OFile
664 // N.B. ".." directories on some media do not contain a starting
665 // cluster. In the case of "." or ".." we don't need the size anyway.
668 while (!FAT_END_OF_FAT_CHAIN (Cluster
)) {
669 if (Cluster
== FAT_CLUSTER_FREE
|| Cluster
>= FAT_CLUSTER_SPECIAL
) {
671 (EFI_D_INIT
| EFI_D_ERROR
,
672 "FATDirSize: cluster chain corrupt\n")
677 Size
+= Volume
->ClusterSize
;
678 Cluster
= FatGetFatEntry (Volume
, Cluster
);
687 Get the physical size of a file on the disk.
689 @param Volume - The file system volume.
690 @param RealSize - The real size of a file.
692 @return The physical size of a file on the disk.
696 FatPhysicalFileSize (
697 IN FAT_VOLUME
*Volume
,
701 UINTN ClusterSizeMask
;
703 ClusterSizeMask
= Volume
->ClusterSize
- 1;
704 PhysicalSize
= (RealSize
+ ClusterSizeMask
) & (~((UINT64
) ClusterSizeMask
));
710 Update the free cluster info of FatInfoSector of the volume.
712 @param Volume - FAT file system volume.
717 IN FAT_VOLUME
*Volume
723 // If we don't have valid info, compute it now
725 if (!Volume
->FreeInfoValid
) {
727 Volume
->FreeInfoValid
= TRUE
;
728 Volume
->FatInfoSector
.FreeInfo
.ClusterCount
= 0;
729 for (Index
= Volume
->MaxCluster
+ 1; Index
>= FAT_MIN_CLUSTER
; Index
--) {
730 if (Volume
->DiskError
) {
734 if (FatGetFatEntry (Volume
, Index
) == FAT_CLUSTER_FREE
) {
735 Volume
->FatInfoSector
.FreeInfo
.ClusterCount
+= 1;
736 Volume
->FatInfoSector
.FreeInfo
.NextCluster
= (UINT32
) Index
;
740 Volume
->FatInfoSector
.Signature
= FAT_INFO_SIGNATURE
;
741 Volume
->FatInfoSector
.InfoBeginSignature
= FAT_INFO_BEGIN_SIGNATURE
;
742 Volume
->FatInfoSector
.InfoEndSignature
= FAT_INFO_END_SIGNATURE
;