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