]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/EnhancedFatDxe/FileSpace.c
BaseTools: Library hashing fix and optimization for --hash feature
[mirror_edk2.git] / FatPkg / EnhancedFatDxe / FileSpace.c
1 /** @file
2 Routines dealing with disk spaces and FAT table entries.
3
4 Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7
8
9 **/
10
11 #include "Fat.h"
12
13
14 /**
15
16 Get the FAT entry of the volume, which is identified with the Index.
17
18 @param Volume - FAT file system volume.
19 @param Index - The index of the FAT entry of the volume.
20
21 @return The buffer of the FAT entry
22
23 **/
24 STATIC
25 VOID *
26 FatLoadFatEntry (
27 IN FAT_VOLUME *Volume,
28 IN UINTN Index
29 )
30 {
31 UINTN Pos;
32 EFI_STATUS Status;
33
34 if (Index > (Volume->MaxCluster + 1)) {
35 Volume->FatEntryBuffer = (UINT32) -1;
36 return &Volume->FatEntryBuffer;
37 }
38 //
39 // Compute buffer position needed
40 //
41 switch (Volume->FatType) {
42 case Fat12:
43 Pos = FAT_POS_FAT12 (Index);
44 break;
45
46 case Fat16:
47 Pos = FAT_POS_FAT16 (Index);
48 break;
49
50 default:
51 Pos = FAT_POS_FAT32 (Index);
52 }
53 //
54 // Set the position and read the buffer
55 //
56 Volume->FatEntryPos = Volume->FatPos + Pos;
57 Status = FatDiskIo (
58 Volume,
59 ReadFat,
60 Volume->FatEntryPos,
61 Volume->FatEntrySize,
62 &Volume->FatEntryBuffer,
63 NULL
64 );
65 if (EFI_ERROR (Status)) {
66 Volume->FatEntryBuffer = (UINT32) -1;
67 }
68
69 return &Volume->FatEntryBuffer;
70 }
71
72 /**
73
74 Get the FAT entry value of the volume, which is identified with the Index.
75
76 @param Volume - FAT file system volume.
77 @param Index - The index of the FAT entry of the volume.
78
79 @return The value of the FAT entry.
80
81 **/
82 STATIC
83 UINTN
84 FatGetFatEntry (
85 IN FAT_VOLUME *Volume,
86 IN UINTN Index
87 )
88 {
89 VOID *Pos;
90 UINT8 *En12;
91 UINT16 *En16;
92 UINT32 *En32;
93 UINTN Accum;
94
95 Pos = FatLoadFatEntry (Volume, Index);
96
97 if (Index > (Volume->MaxCluster + 1)) {
98 return (UINTN) -1;
99 }
100
101 switch (Volume->FatType) {
102 case Fat12:
103 En12 = Pos;
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);
107 break;
108
109 case Fat16:
110 En16 = Pos;
111 Accum = *En16;
112 Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT16) ? FAT_CLUSTER_SPECIAL_EXT : 0);
113 break;
114
115 default:
116 En32 = Pos;
117 Accum = *En32 & FAT_CLUSTER_MASK_FAT32;
118 Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT32) ? FAT_CLUSTER_SPECIAL_EXT : 0);
119 }
120
121 return Accum;
122 }
123
124 /**
125
126 Set the FAT entry value of the volume, which is identified with the Index.
127
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.
131
132 @retval EFI_SUCCESS - Set the new FAT entry value sucessfully.
133 @retval EFI_VOLUME_CORRUPTED - The FAT type of the volume is error.
134 @return other - An error occurred when operation the FAT entries.
135
136 **/
137 STATIC
138 EFI_STATUS
139 FatSetFatEntry (
140 IN FAT_VOLUME *Volume,
141 IN UINTN Index,
142 IN UINTN Value
143 )
144 {
145 VOID *Pos;
146 UINT8 *En12;
147 UINT16 *En16;
148 UINT32 *En32;
149 UINTN Accum;
150 EFI_STATUS Status;
151 UINTN OriginalVal;
152
153 if (Index < FAT_MIN_CLUSTER) {
154 return EFI_VOLUME_CORRUPTED;
155 }
156
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;
162 }
163 } else if (Value != FAT_CLUSTER_FREE && OriginalVal == FAT_CLUSTER_FREE) {
164 if (Volume->FatInfoSector.FreeInfo.ClusterCount != 0) {
165 Volume->FatInfoSector.FreeInfo.ClusterCount -= 1;
166 }
167 }
168 //
169 // Make sure the entry is in memory
170 //
171 Pos = FatLoadFatEntry (Volume, Index);
172
173 //
174 // Update the value
175 //
176 switch (Volume->FatType) {
177 case Fat12:
178 En12 = Pos;
179 Accum = En12[0] | (En12[1] << 8);
180 Value = Value & FAT_CLUSTER_MASK_FAT12;
181
182 if (FAT_ODD_CLUSTER_FAT12 (Index)) {
183 Accum = (Value << 4) | (Accum & 0xF);
184 } else {
185 Accum = Value | (Accum & FAT_CLUSTER_UNMASK_FAT12);
186 }
187
188 En12[0] = (UINT8) (Accum & 0xFF);
189 En12[1] = (UINT8) (Accum >> 8);
190 break;
191
192 case Fat16:
193 En16 = Pos;
194 *En16 = (UINT16) Value;
195 break;
196
197 default:
198 En32 = Pos;
199 *En32 = (*En32 & FAT_CLUSTER_UNMASK_FAT32) | (UINT32) (Value & FAT_CLUSTER_MASK_FAT32);
200 }
201 //
202 // If the volume's dirty bit is not set, set it now
203 //
204 if (!Volume->FatDirty && Volume->FatType != Fat12) {
205 Volume->FatDirty = TRUE;
206 FatAccessVolumeDirty (Volume, WriteFat, &Volume->DirtyValue);
207 }
208 //
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.
212 //
213 Status = FatDiskIo (
214 Volume,
215 WriteFat,
216 Volume->FatEntryPos,
217 Volume->FatEntrySize,
218 &Volume->FatEntryBuffer,
219 NULL
220 );
221 return Status;
222 }
223
224 /**
225
226 Free the cluster clain.
227
228 @param Volume - FAT file system volume.
229 @param Cluster - The first cluster of cluster chain.
230
231 @retval EFI_SUCCESS - The cluster chain is freed successfully.
232 @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters.
233
234 **/
235 STATIC
236 EFI_STATUS
237 FatFreeClusters (
238 IN FAT_VOLUME *Volume,
239 IN UINTN Cluster
240 )
241 {
242 UINTN LastCluster;
243
244 while (!FAT_END_OF_FAT_CHAIN (Cluster)) {
245 if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) {
246
247 DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n"));
248 return EFI_VOLUME_CORRUPTED;
249 }
250
251 LastCluster = Cluster;
252 Cluster = FatGetFatEntry (Volume, Cluster);
253 FatSetFatEntry (Volume, LastCluster, FAT_CLUSTER_FREE);
254 }
255
256 return EFI_SUCCESS;
257 }
258
259 /**
260
261 Allocate a free cluster and return the cluster index.
262
263 @param Volume - FAT file system volume.
264
265 @return The index of the free cluster
266
267 **/
268 STATIC
269 UINTN
270 FatAllocateCluster (
271 IN FAT_VOLUME *Volume
272 )
273 {
274 UINTN Cluster;
275
276 //
277 // Start looking at FatFreePos for the next unallocated cluster
278 //
279 if (Volume->DiskError) {
280 return (UINTN) FAT_CLUSTER_LAST;
281 }
282
283 for (;;) {
284 //
285 // If the end of the list, return no available cluster
286 //
287 if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) {
288 if (Volume->FreeInfoValid && 0 < (INT32) (Volume->FatInfoSector.FreeInfo.ClusterCount)) {
289 Volume->FreeInfoValid = FALSE;
290 }
291
292 FatComputeFreeInfo (Volume);
293 if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) {
294 return (UINTN) FAT_CLUSTER_LAST;
295 }
296 }
297
298 Cluster = FatGetFatEntry (Volume, Volume->FatInfoSector.FreeInfo.NextCluster);
299 if (Cluster == FAT_CLUSTER_FREE) {
300 break;
301 }
302 //
303 // Try the next cluster
304 //
305 Volume->FatInfoSector.FreeInfo.NextCluster += 1;
306 }
307
308 Cluster = Volume->FatInfoSector.FreeInfo.NextCluster;
309 Volume->FatInfoSector.FreeInfo.NextCluster += 1;
310 return Cluster;
311 }
312
313 /**
314
315 Count the number of clusters given a size.
316
317 @param Volume - The file system volume.
318 @param Size - The size in bytes.
319
320 @return The number of the clusters.
321
322 **/
323 STATIC
324 UINTN
325 FatSizeToClusters (
326 IN FAT_VOLUME *Volume,
327 IN UINTN Size
328 )
329 {
330 UINTN Clusters;
331
332 Clusters = Size >> Volume->ClusterAlignment;
333 if ((Size & (Volume->ClusterSize - 1)) > 0) {
334 Clusters += 1;
335 }
336
337 return Clusters;
338 }
339
340 /**
341
342 Shrink the end of the open file base on the file size.
343
344 @param OFile - The open file.
345
346 @retval EFI_SUCCESS - Shrinked sucessfully.
347 @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters.
348
349 **/
350 EFI_STATUS
351 FatShrinkEof (
352 IN FAT_OFILE *OFile
353 )
354 {
355 FAT_VOLUME *Volume;
356 UINTN NewSize;
357 UINTN CurSize;
358 UINTN Cluster;
359 UINTN LastCluster;
360
361 Volume = OFile->Volume;
362 ASSERT_VOLUME_LOCKED (Volume);
363
364 NewSize = FatSizeToClusters (Volume, OFile->FileSize);
365
366 //
367 // Find the address of the last cluster
368 //
369 Cluster = OFile->FileCluster;
370 LastCluster = FAT_CLUSTER_FREE;
371
372 if (NewSize != 0) {
373
374 for (CurSize = 0; CurSize < NewSize; CurSize++) {
375 if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) {
376
377 DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n"));
378 return EFI_VOLUME_CORRUPTED;
379 }
380
381 LastCluster = Cluster;
382 Cluster = FatGetFatEntry (Volume, Cluster);
383 }
384
385 FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST);
386
387 } else {
388 //
389 // Check to see if the file is already completely truncated
390 //
391 if (Cluster == FAT_CLUSTER_FREE) {
392 return EFI_SUCCESS;
393 }
394 //
395 // The file is being completely truncated.
396 //
397 OFile->FileCluster = FAT_CLUSTER_FREE;
398 }
399 //
400 // Set CurrentCluster == FileCluster
401 // to force a recalculation of Position related stuffs
402 //
403 OFile->FileCurrentCluster = OFile->FileCluster;
404 OFile->FileLastCluster = LastCluster;
405 OFile->Dirty = TRUE;
406 //
407 // Free the remaining cluster chain
408 //
409 return FatFreeClusters (Volume, Cluster);
410 }
411
412 /**
413
414 Grow the end of the open file base on the NewSizeInBytes.
415
416 @param OFile - The open file.
417 @param NewSizeInBytes - The new size in bytes of the open file.
418
419 @retval EFI_SUCCESS - The file is grown sucessfully.
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.
423
424 **/
425 EFI_STATUS
426 FatGrowEof (
427 IN FAT_OFILE *OFile,
428 IN UINT64 NewSizeInBytes
429 )
430 {
431 FAT_VOLUME *Volume;
432 EFI_STATUS Status;
433 UINTN Cluster;
434 UINTN CurSize;
435 UINTN NewSize;
436 UINTN LastCluster;
437 UINTN NewCluster;
438 UINTN ClusterCount;
439
440 //
441 // For FAT file system, the max file is 4GB.
442 //
443 if (NewSizeInBytes > 0x0FFFFFFFFL) {
444 return EFI_UNSUPPORTED;
445 }
446
447 Volume = OFile->Volume;
448 ASSERT_VOLUME_LOCKED (Volume);
449 //
450 // If the file is already large enough, do nothing
451 //
452 CurSize = FatSizeToClusters (Volume, OFile->FileSize);
453 NewSize = FatSizeToClusters (Volume, (UINTN) NewSizeInBytes);
454
455 if (CurSize < NewSize) {
456 //
457 // If we haven't found the files last cluster do it now
458 //
459 if ((OFile->FileCluster != 0) && (OFile->FileLastCluster == 0)) {
460 Cluster = OFile->FileCluster;
461 ClusterCount = 0;
462
463 while (!FAT_END_OF_FAT_CHAIN (Cluster)) {
464 if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) {
465
466 DEBUG (
467 (EFI_D_INIT | EFI_D_ERROR,
468 "FatGrowEof: cluster chain corrupt\n")
469 );
470 Status = EFI_VOLUME_CORRUPTED;
471 goto Done;
472 }
473
474 ClusterCount++;
475 OFile->FileLastCluster = Cluster;
476 Cluster = FatGetFatEntry (Volume, Cluster);
477 }
478
479 if (ClusterCount != CurSize) {
480 DEBUG (
481 (EFI_D_INIT | EFI_D_ERROR,
482 "FatGrowEof: cluster chain size does not match file size\n")
483 );
484 Status = EFI_VOLUME_CORRUPTED;
485 goto Done;
486 }
487
488 }
489 //
490 // Loop until we've allocated enough space
491 //
492 LastCluster = OFile->FileLastCluster;
493
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;
500 }
501
502 Status = EFI_VOLUME_FULL;
503 goto Done;
504 }
505
506 if (NewCluster < FAT_MIN_CLUSTER || NewCluster > Volume->MaxCluster + 1) {
507 Status = EFI_VOLUME_CORRUPTED;
508 goto Done;
509 }
510
511 if (LastCluster != 0) {
512 FatSetFatEntry (Volume, LastCluster, NewCluster);
513 } else {
514 OFile->FileCluster = NewCluster;
515 OFile->FileCurrentCluster = NewCluster;
516 }
517
518 LastCluster = NewCluster;
519 CurSize += 1;
520
521 //
522 // Terminate the cluster list
523 //
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.
531 //
532 FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST);
533 OFile->FileLastCluster = LastCluster;
534 }
535 }
536
537 OFile->FileSize = (UINTN) NewSizeInBytes;
538 OFile->Dirty = TRUE;
539 return EFI_SUCCESS;
540
541 Done:
542 FatShrinkEof (OFile);
543 return Status;
544 }
545
546 /**
547
548 Seek OFile to requested position, and calculate the number of
549 consecutive clusters from the position in the file
550
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
554
555 @retval EFI_SUCCESS - Set the info successfully.
556 @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt.
557
558 **/
559 EFI_STATUS
560 FatOFilePosition (
561 IN FAT_OFILE *OFile,
562 IN UINTN Position,
563 IN UINTN PosLimit
564 )
565 {
566 FAT_VOLUME *Volume;
567 UINTN ClusterSize;
568 UINTN Cluster;
569 UINTN StartPos;
570 UINTN Run;
571
572 Volume = OFile->Volume;
573 ClusterSize = Volume->ClusterSize;
574
575 ASSERT_VOLUME_LOCKED (Volume);
576
577 //
578 // If this is the fixed root dir, then compute it's position
579 // from it's fixed info in the fat bpb
580 //
581 if (OFile->IsFixedRootDir) {
582 OFile->PosDisk = Volume->RootPos + Position;
583 Run = OFile->FileSize - Position;
584 } else {
585 //
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
596 //
597 Cluster = OFile->FileCurrentCluster;
598 StartPos = OFile->Position;
599 if (Position < StartPos || OFile->FileCluster == Cluster) {
600 StartPos = 0;
601 Cluster = OFile->FileCluster;
602 }
603
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;
609 }
610
611 Cluster = FatGetFatEntry (Volume, Cluster);
612 }
613
614 if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) {
615 return EFI_VOLUME_CORRUPTED;
616 }
617
618 OFile->PosDisk = Volume->FirstClusterPos +
619 LShiftU64 (Cluster - FAT_MIN_CLUSTER, Volume->ClusterAlignment) +
620 Position - StartPos;
621 OFile->FileCurrentCluster = Cluster;
622 OFile->Position = StartPos;
623
624 //
625 // Compute the number of consecutive clusters in the file
626 //
627 Run = StartPos + ClusterSize - Position;
628 if (!FAT_END_OF_FAT_CHAIN (Cluster)) {
629 while ((FatGetFatEntry (Volume, Cluster) == Cluster + 1) && Run < PosLimit) {
630 Run += ClusterSize;
631 Cluster += 1;
632 }
633 }
634 }
635
636 OFile->PosRem = Run;
637 return EFI_SUCCESS;
638 }
639
640 /**
641
642 Get the size of directory of the open file.
643
644 @param Volume - The File System Volume.
645 @param Cluster - The Starting cluster.
646
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.
649
650 **/
651 UINTN
652 FatPhysicalDirSize (
653 IN FAT_VOLUME *Volume,
654 IN UINTN Cluster
655 )
656 {
657 UINTN Size;
658 ASSERT_VOLUME_LOCKED (Volume);
659 //
660 // Run the cluster chain for the OFile
661 //
662 Size = 0;
663 //
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.
666 //
667 if (Cluster != 0) {
668 while (!FAT_END_OF_FAT_CHAIN (Cluster)) {
669 if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) {
670 DEBUG (
671 (EFI_D_INIT | EFI_D_ERROR,
672 "FATDirSize: cluster chain corrupt\n")
673 );
674 return 0;
675 }
676
677 Size += Volume->ClusterSize;
678 Cluster = FatGetFatEntry (Volume, Cluster);
679 }
680 }
681
682 return Size;
683 }
684
685 /**
686
687 Get the physical size of a file on the disk.
688
689 @param Volume - The file system volume.
690 @param RealSize - The real size of a file.
691
692 @return The physical size of a file on the disk.
693
694 **/
695 UINT64
696 FatPhysicalFileSize (
697 IN FAT_VOLUME *Volume,
698 IN UINTN RealSize
699 )
700 {
701 UINTN ClusterSizeMask;
702 UINT64 PhysicalSize;
703 ClusterSizeMask = Volume->ClusterSize - 1;
704 PhysicalSize = (RealSize + ClusterSizeMask) & (~((UINT64) ClusterSizeMask));
705 return PhysicalSize;
706 }
707
708 /**
709
710 Update the free cluster info of FatInfoSector of the volume.
711
712 @param Volume - FAT file system volume.
713
714 **/
715 VOID
716 FatComputeFreeInfo (
717 IN FAT_VOLUME *Volume
718 )
719 {
720 UINTN Index;
721
722 //
723 // If we don't have valid info, compute it now
724 //
725 if (!Volume->FreeInfoValid) {
726
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) {
731 break;
732 }
733
734 if (FatGetFatEntry (Volume, Index) == FAT_CLUSTER_FREE) {
735 Volume->FatInfoSector.FreeInfo.ClusterCount += 1;
736 Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index;
737 }
738 }
739
740 Volume->FatInfoSector.Signature = FAT_INFO_SIGNATURE;
741 Volume->FatInfoSector.InfoBeginSignature = FAT_INFO_BEGIN_SIGNATURE;
742 Volume->FatInfoSector.InfoEndSignature = FAT_INFO_END_SIGNATURE;
743 }
744 }