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_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { 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 (LastCluster != 0) { 513 FatSetFatEntry (Volume, LastCluster, NewCluster); 514 } else { 515 OFile->FileCluster = NewCluster; 516 OFile->FileCurrentCluster = NewCluster; 517 } 518 519 LastCluster = NewCluster; 520 CurSize += 1; 521 } 522 // 523 // Terminate the cluster list 524 // 525 FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); 526 OFile->FileLastCluster = LastCluster; 527 } 528 529 OFile->FileSize = (UINTN) NewSizeInBytes; 530 OFile->Dirty = TRUE; 531 return EFI_SUCCESS; 532 533 Done: 534 FatShrinkEof (OFile); 535 return Status; 536 } 537 538 /** 539 540 Seek OFile to requested position, and calculate the number of 541 consecutive clusters from the position in the file 542 543 @param OFile - The open file. 544 @param Position - The file's position which will be accessed. 545 @param PosLimit - The maximum length current reading/writing may access 546 547 @retval EFI_SUCCESS - Set the info successfully. 548 @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt. 549 550 **/ 551 EFI_STATUS 552 FatOFilePosition ( 553 IN FAT_OFILE *OFile, 554 IN UINTN Position, 555 IN UINTN PosLimit 556 ) 557 { 558 FAT_VOLUME *Volume; 559 UINTN ClusterSize; 560 UINTN Cluster; 561 UINTN StartPos; 562 UINTN Run; 563 564 Volume = OFile->Volume; 565 ClusterSize = Volume->ClusterSize; 566 567 ASSERT_VOLUME_LOCKED (Volume); 568 569 // 570 // If this is the fixed root dir, then compute it's position 571 // from it's fixed info in the fat bpb 572 // 573 if (OFile->IsFixedRootDir) { 574 OFile->PosDisk = Volume->RootPos + Position; 575 Run = OFile->FileSize - Position; 576 } else { 577 // 578 // Run the file's cluster chain to find the current position 579 // If possible, run from the current cluster rather than 580 // start from beginning 581 // Assumption: OFile->Position is always consistent with 582 // OFile->FileCurrentCluster. 583 // OFile->Position is not modified outside this function; 584 // OFile->FileCurrentCluster is modified outside this function 585 // to be the same as OFile->FileCluster 586 // when OFile->FileCluster is updated, so make a check of this 587 // and invalidate the original OFile->Position in this case 588 // 589 Cluster = OFile->FileCurrentCluster; 590 StartPos = OFile->Position; 591 if (Position < StartPos || OFile->FileCluster == Cluster) { 592 StartPos = 0; 593 Cluster = OFile->FileCluster; 594 } 595 596 while (StartPos + ClusterSize <= Position) { 597 StartPos += ClusterSize; 598 if (Cluster == FAT_CLUSTER_FREE || (Cluster >= FAT_CLUSTER_SPECIAL)) { 599 DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatOFilePosition:"" cluster chain corrupt\n")); 600 return EFI_VOLUME_CORRUPTED; 601 } 602 603 Cluster = FatGetFatEntry (Volume, Cluster); 604 } 605 606 if (Cluster < FAT_MIN_CLUSTER) { 607 return EFI_VOLUME_CORRUPTED; 608 } 609 610 OFile->PosDisk = Volume->FirstClusterPos + 611 LShiftU64 (Cluster - FAT_MIN_CLUSTER, Volume->ClusterAlignment) + 612 Position - StartPos; 613 OFile->FileCurrentCluster = Cluster; 614 OFile->Position = StartPos; 615 616 // 617 // Compute the number of consecutive clusters in the file 618 // 619 Run = StartPos + ClusterSize - Position; 620 if (!FAT_END_OF_FAT_CHAIN (Cluster)) { 621 while ((FatGetFatEntry (Volume, Cluster) == Cluster + 1) && Run < PosLimit) { 622 Run += ClusterSize; 623 Cluster += 1; 624 } 625 } 626 } 627 628 OFile->PosRem = Run; 629 return EFI_SUCCESS; 630 } 631 632 /** 633 634 Get the size of directory of the open file. 635 636 @param Volume - The File System Volume. 637 @param Cluster - The Starting cluster. 638 639 @return The physical size of the file starting at the input cluster, if there is error in the 640 cluster chain, the return value is 0. 641 642 **/ 643 UINTN 644 FatPhysicalDirSize ( 645 IN FAT_VOLUME *Volume, 646 IN UINTN Cluster 647 ) 648 { 649 UINTN Size; 650 ASSERT_VOLUME_LOCKED (Volume); 651 // 652 // Run the cluster chain for the OFile 653 // 654 Size = 0; 655 // 656 // N.B. ".." directories on some media do not contain a starting 657 // cluster. In the case of "." or ".." we don't need the size anyway. 658 // 659 if (Cluster != 0) { 660 while (!FAT_END_OF_FAT_CHAIN (Cluster)) { 661 if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { 662 DEBUG ( 663 (EFI_D_INIT | EFI_D_ERROR, 664 "FATDirSize: cluster chain corrupt\n") 665 ); 666 return 0; 667 } 668 669 Size += Volume->ClusterSize; 670 Cluster = FatGetFatEntry (Volume, Cluster); 671 } 672 } 673 674 return Size; 675 } 676 677 /** 678 679 Get the physical size of a file on the disk. 680 681 @param Volume - The file system volume. 682 @param RealSize - The real size of a file. 683 684 @return The physical size of a file on the disk. 685 686 **/ 687 UINT64 688 FatPhysicalFileSize ( 689 IN FAT_VOLUME *Volume, 690 IN UINTN RealSize 691 ) 692 { 693 UINTN ClusterSizeMask; 694 UINT64 PhysicalSize; 695 ClusterSizeMask = Volume->ClusterSize - 1; 696 PhysicalSize = (RealSize + ClusterSizeMask) & (~((UINT64) ClusterSizeMask)); 697 return PhysicalSize; 698 } 699 700 /** 701 702 Update the free cluster info of FatInfoSector of the volume. 703 704 @param Volume - FAT file system volume. 705 706 **/ 707 VOID 708 FatComputeFreeInfo ( 709 IN FAT_VOLUME *Volume 710 ) 711 { 712 UINTN Index; 713 714 // 715 // If we don't have valid info, compute it now 716 // 717 if (!Volume->FreeInfoValid) { 718 719 Volume->FreeInfoValid = TRUE; 720 Volume->FatInfoSector.FreeInfo.ClusterCount = 0; 721 for (Index = Volume->MaxCluster + 1; Index >= FAT_MIN_CLUSTER; Index--) { 722 if (Volume->DiskError) { 723 break; 724 } 725 726 if (FatGetFatEntry (Volume, Index) == FAT_CLUSTER_FREE) { 727 Volume->FatInfoSector.FreeInfo.ClusterCount += 1; 728 Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index; 729 } 730 } 731 732 Volume->FatInfoSector.Signature = FAT_INFO_SIGNATURE; 733 Volume->FatInfoSector.InfoBeginSignature = FAT_INFO_BEGIN_SIGNATURE; 734 Volume->FatInfoSector.InfoEndSignature = FAT_INFO_END_SIGNATURE; 735 } 736 } 737