1 /** @file 2 Functions for performing directory entry io. 3 4 Copyright (c) 2005 - 2015, 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 #include "Fat.h" 16 17 /** 18 19 Get a directory entry from disk for the Ofile. 20 21 @param Parent - The parent of the OFile which need to update. 22 @param IoMode - Indicate whether to read directory entry or write directroy entry. 23 @param EntryPos - The position of the directory entry to be accessed. 24 @param Entry - The directory entry read or written. 25 26 @retval EFI_SUCCESS - Access the directory entry sucessfully. 27 @return other - An error occurred when reading the directory entry. 28 29 **/ 30 STATIC 31 EFI_STATUS 32 FatAccessEntry ( 33 IN FAT_OFILE *Parent, 34 IN IO_MODE IoMode, 35 IN UINTN EntryPos, 36 IN OUT VOID *Entry 37 ) 38 { 39 UINTN Position; 40 UINTN BufferSize; 41 42 Position = EntryPos * sizeof (FAT_DIRECTORY_ENTRY); 43 if (Position >= Parent->FileSize) { 44 // 45 // End of directory 46 // 47 ASSERT (IoMode == ReadData); 48 ((FAT_DIRECTORY_ENTRY *) Entry)->FileName[0] = EMPTY_ENTRY_MARK; 49 ((FAT_DIRECTORY_ENTRY *) Entry)->Attributes = 0; 50 return EFI_SUCCESS; 51 } 52 53 BufferSize = sizeof (FAT_DIRECTORY_ENTRY); 54 return FatAccessOFile (Parent, IoMode, Position, &BufferSize, Entry, NULL); 55 } 56 57 /** 58 59 Save the directory entry to disk. 60 61 @param OFile - The parent OFile which needs to update. 62 @param DirEnt - The directory entry to be saved. 63 64 @retval EFI_SUCCESS - Store the directory entry successfully. 65 @return other - An error occurred when writing the directory entry. 66 67 **/ 68 EFI_STATUS 69 FatStoreDirEnt ( 70 IN FAT_OFILE *OFile, 71 IN FAT_DIRENT *DirEnt 72 ) 73 { 74 EFI_STATUS Status; 75 FAT_DIRECTORY_LFN LfnEntry; 76 UINTN EntryPos; 77 CHAR16 *LfnBufferPointer; 78 CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1]; 79 UINT8 EntryCount; 80 UINT8 LfnOrdinal; 81 82 EntryPos = DirEnt->EntryPos; 83 EntryCount = DirEnt->EntryCount; 84 // 85 // Write directory entry 86 // 87 Status = FatAccessEntry (OFile, WriteData, EntryPos, &DirEnt->Entry); 88 if (EFI_ERROR (Status)) { 89 return Status; 90 } 91 92 if (--EntryCount > 0) { 93 // 94 // Write LFN directory entry 95 // 96 SetMem (LfnBuffer, sizeof (CHAR16) * LFN_CHAR_TOTAL * EntryCount, 0xff); 97 Status = StrCpyS ( 98 LfnBuffer, 99 ARRAY_SIZE (LfnBuffer), 100 DirEnt->FileString 101 ); 102 if (EFI_ERROR (Status)) { 103 return Status; 104 } 105 106 LfnBufferPointer = LfnBuffer; 107 LfnEntry.Attributes = FAT_ATTRIBUTE_LFN; 108 LfnEntry.Type = 0; 109 LfnEntry.MustBeZero = 0; 110 LfnEntry.Checksum = FatCheckSum (DirEnt->Entry.FileName); 111 for (LfnOrdinal = 1; LfnOrdinal <= EntryCount; LfnOrdinal++) { 112 LfnEntry.Ordinal = LfnOrdinal; 113 if (LfnOrdinal == EntryCount) { 114 LfnEntry.Ordinal |= FAT_LFN_LAST; 115 } 116 117 CopyMem (LfnEntry.Name1, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR1_LEN); 118 LfnBufferPointer += LFN_CHAR1_LEN; 119 CopyMem (LfnEntry.Name2, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR2_LEN); 120 LfnBufferPointer += LFN_CHAR2_LEN; 121 CopyMem (LfnEntry.Name3, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR3_LEN); 122 LfnBufferPointer += LFN_CHAR3_LEN; 123 EntryPos--; 124 if (DirEnt->Invalid) { 125 LfnEntry.Ordinal = DELETE_ENTRY_MARK; 126 } 127 128 Status = FatAccessEntry (OFile, WriteData, EntryPos, &LfnEntry); 129 if (EFI_ERROR (Status)) { 130 return Status; 131 } 132 } 133 } 134 135 return EFI_SUCCESS; 136 } 137 138 /** 139 140 Determine whether the directory entry is "." or ".." entry. 141 142 @param DirEnt - The corresponding directory entry. 143 144 @retval TRUE - The directory entry is "." or ".." directory entry 145 @retval FALSE - The directory entry is not "." or ".." directory entry 146 147 **/ 148 BOOLEAN 149 FatIsDotDirEnt ( 150 IN FAT_DIRENT *DirEnt 151 ) 152 { 153 CHAR16 *FileString; 154 FileString = DirEnt->FileString; 155 if (StrCmp (FileString, L".") == 0 || StrCmp (FileString, L"..") == 0) { 156 return TRUE; 157 } 158 159 return FALSE; 160 } 161 162 /** 163 164 Set the OFile's cluster info in its directory entry. 165 166 @param OFile - The corresponding OFile. 167 168 **/ 169 STATIC 170 VOID 171 FatSetDirEntCluster ( 172 IN FAT_OFILE *OFile 173 ) 174 { 175 UINTN Cluster; 176 FAT_DIRENT *DirEnt; 177 178 DirEnt = OFile->DirEnt; 179 Cluster = OFile->FileCluster; 180 DirEnt->Entry.FileClusterHigh = (UINT16) (Cluster >> 16); 181 DirEnt->Entry.FileCluster = (UINT16) Cluster; 182 } 183 184 /** 185 186 Set the OFile's cluster and size info in its directory entry. 187 188 @param OFile - The corresponding OFile. 189 190 **/ 191 VOID 192 FatUpdateDirEntClusterSizeInfo ( 193 IN FAT_OFILE *OFile 194 ) 195 { 196 ASSERT (OFile->ODir == NULL); 197 OFile->DirEnt->Entry.FileSize = (UINT32) OFile->FileSize; 198 FatSetDirEntCluster (OFile); 199 } 200 201 /** 202 203 Copy all the information of DirEnt2 to DirEnt1 except for 8.3 name. 204 205 @param DirEnt1 - The destination directory entry. 206 @param DirEnt2 - The source directory entry. 207 208 **/ 209 VOID 210 FatCloneDirEnt ( 211 IN FAT_DIRENT *DirEnt1, 212 IN FAT_DIRENT *DirEnt2 213 ) 214 { 215 UINT8 *Entry1; 216 UINT8 *Entry2; 217 Entry1 = (UINT8 *) &DirEnt1->Entry; 218 Entry2 = (UINT8 *) &DirEnt2->Entry; 219 CopyMem ( 220 Entry1 + FAT_ENTRY_INFO_OFFSET, 221 Entry2 + FAT_ENTRY_INFO_OFFSET, 222 sizeof (FAT_DIRECTORY_ENTRY) - FAT_ENTRY_INFO_OFFSET 223 ); 224 } 225 226 /** 227 228 Get the LFN for the directory entry. 229 230 @param Parent - The parent directory. 231 @param DirEnt - The directory entry to get LFN. 232 233 **/ 234 STATIC 235 VOID 236 FatLoadLongNameEntry ( 237 IN FAT_OFILE *Parent, 238 IN FAT_DIRENT *DirEnt 239 ) 240 { 241 CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1]; 242 CHAR16 *LfnBufferPointer; 243 CHAR8 *File8Dot3Name; 244 UINTN EntryPos; 245 UINT8 LfnOrdinal; 246 UINT8 LfnChecksum; 247 FAT_DIRECTORY_LFN LfnEntry; 248 EFI_STATUS Status; 249 250 EntryPos = DirEnt->EntryPos; 251 File8Dot3Name = DirEnt->Entry.FileName; 252 LfnBufferPointer = LfnBuffer; 253 // 254 // Computes checksum for LFN 255 // 256 LfnChecksum = FatCheckSum (File8Dot3Name); 257 LfnOrdinal = 1; 258 do { 259 if (EntryPos == 0) { 260 LfnBufferPointer = LfnBuffer; 261 break; 262 } 263 264 EntryPos--; 265 Status = FatAccessEntry (Parent, ReadData, EntryPos, &LfnEntry); 266 if (EFI_ERROR (Status) || 267 LfnEntry.Attributes != FAT_ATTRIBUTE_LFN || 268 LfnEntry.MustBeZero != 0 || 269 LfnEntry.Checksum != LfnChecksum || 270 (LfnEntry.Ordinal & (~FAT_LFN_LAST)) != LfnOrdinal || 271 LfnOrdinal > MAX_LFN_ENTRIES 272 ) { 273 // 274 // The directory entry does not have a long file name or 275 // some error occurs when loading long file name for a directory entry, 276 // and then we load the long name from short name 277 // 278 LfnBufferPointer = LfnBuffer; 279 break; 280 } 281 282 CopyMem (LfnBufferPointer, LfnEntry.Name1, sizeof (CHAR16) * LFN_CHAR1_LEN); 283 LfnBufferPointer += LFN_CHAR1_LEN; 284 CopyMem (LfnBufferPointer, LfnEntry.Name2, sizeof (CHAR16) * LFN_CHAR2_LEN); 285 LfnBufferPointer += LFN_CHAR2_LEN; 286 CopyMem (LfnBufferPointer, LfnEntry.Name3, sizeof (CHAR16) * LFN_CHAR3_LEN); 287 LfnBufferPointer += LFN_CHAR3_LEN; 288 LfnOrdinal++; 289 } while ((LfnEntry.Ordinal & FAT_LFN_LAST) == 0); 290 DirEnt->EntryCount = LfnOrdinal; 291 // 292 // Terminate current Lfnbuffer 293 // 294 *LfnBufferPointer = 0; 295 if (LfnBufferPointer == LfnBuffer) { 296 // 297 // Fail to get the long file name from long file name entry, 298 // get the file name from short name 299 // 300 FatGetFileNameViaCaseFlag ( 301 DirEnt, 302 LfnBuffer, 303 ARRAY_SIZE (LfnBuffer) 304 ); 305 } 306 307 DirEnt->FileString = AllocateCopyPool (StrSize (LfnBuffer), LfnBuffer); 308 } 309 310 /** 311 312 Add this directory entry node to the list of directory entries and hash table. 313 314 @param ODir - The parent OFile which needs to be updated. 315 @param DirEnt - The directory entry to be added. 316 317 **/ 318 STATIC 319 VOID 320 FatAddDirEnt ( 321 IN FAT_ODIR *ODir, 322 IN FAT_DIRENT *DirEnt 323 ) 324 { 325 if (DirEnt->Link.BackLink == NULL) { 326 DirEnt->Link.BackLink = &ODir->ChildList; 327 } 328 InsertTailList (DirEnt->Link.BackLink, &DirEnt->Link); 329 FatInsertToHashTable (ODir, DirEnt); 330 } 331 332 /** 333 334 Load from disk the next directory entry at current end of directory position. 335 336 @param OFile - The parent OFile. 337 @param PtrDirEnt - The directory entry that is loaded. 338 339 @retval EFI_SUCCESS - Load the directory entry successfully. 340 @retval EFI_OUT_OF_RESOURCES - Out of resource. 341 @return other - An error occurred when reading the directory entries. 342 343 **/ 344 STATIC 345 EFI_STATUS 346 FatLoadNextDirEnt ( 347 IN FAT_OFILE *OFile, 348 OUT FAT_DIRENT **PtrDirEnt 349 ) 350 { 351 EFI_STATUS Status; 352 FAT_DIRENT *DirEnt; 353 FAT_ODIR *ODir; 354 FAT_DIRECTORY_ENTRY Entry; 355 356 ODir = OFile->ODir; 357 // 358 // Make sure the parent's directory has been opened 359 // 360 ASSERT (ODir != NULL); 361 // 362 // Assert we have not reached the end of directory 363 // 364 ASSERT (!ODir->EndOfDir); 365 DirEnt = NULL; 366 367 for (;;) { 368 // 369 // Read the next directory entry until we find a valid directory entry (excluding lfn entry) 370 // 371 Status = FatAccessEntry (OFile, ReadData, ODir->CurrentEndPos, &Entry); 372 if (EFI_ERROR (Status)) { 373 return Status; 374 } 375 376 if (((UINT8) Entry.FileName[0] != DELETE_ENTRY_MARK) && (Entry.Attributes & FAT_ATTRIBUTE_VOLUME_ID) == 0) { 377 // 378 // We get a valid directory entry, then handle it 379 // 380 break; 381 } 382 383 ODir->CurrentEndPos++; 384 } 385 386 if (Entry.FileName[0] != EMPTY_ENTRY_MARK) { 387 // 388 // Although FAT spec states this field is always 0 for FAT12 & FAT16, some applications 389 // might use it for some special usage, it is safer to zero it in memory for FAT12 & FAT16. 390 // 391 if (OFile->Volume->FatType != Fat32) { 392 Entry.FileClusterHigh = 0; 393 } 394 395 // 396 // This is a valid directory entry 397 // 398 DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT)); 399 if (DirEnt == NULL) { 400 return EFI_OUT_OF_RESOURCES; 401 } 402 403 DirEnt->Signature = FAT_DIRENT_SIGNATURE; 404 // 405 // Remember the directory's entry position on disk 406 // 407 DirEnt->EntryPos = (UINT16) ODir->CurrentEndPos; 408 CopyMem (&DirEnt->Entry, &Entry, sizeof (FAT_DIRECTORY_ENTRY)); 409 FatLoadLongNameEntry (OFile, DirEnt); 410 if (DirEnt->FileString == NULL) { 411 Status = EFI_OUT_OF_RESOURCES; 412 goto Done; 413 } 414 // 415 // Add this directory entry to directory 416 // 417 FatAddDirEnt (ODir, DirEnt); 418 // 419 // Point to next directory entry 420 // 421 ODir->CurrentEndPos++; 422 } else { 423 ODir->EndOfDir = TRUE; 424 } 425 426 *PtrDirEnt = DirEnt; 427 return EFI_SUCCESS; 428 429 Done: 430 FatFreeDirEnt (DirEnt); 431 return Status; 432 } 433 434 /** 435 436 Get the directory entry's info into Buffer. 437 438 @param Volume - FAT file system volume. 439 @param DirEnt - The corresponding directory entry. 440 @param BufferSize - Size of Buffer. 441 @param Buffer - Buffer containing file info. 442 443 @retval EFI_SUCCESS - Get the file info successfully. 444 @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. 445 446 **/ 447 EFI_STATUS 448 FatGetDirEntInfo ( 449 IN FAT_VOLUME *Volume, 450 IN FAT_DIRENT *DirEnt, 451 IN OUT UINTN *BufferSize, 452 OUT VOID *Buffer 453 ) 454 { 455 UINTN Size; 456 UINTN NameSize; 457 UINTN ResultSize; 458 UINTN Cluster; 459 EFI_STATUS Status; 460 EFI_FILE_INFO *Info; 461 FAT_DIRECTORY_ENTRY *Entry; 462 FAT_DATE_TIME FatLastAccess; 463 464 ASSERT_VOLUME_LOCKED (Volume); 465 466 Size = SIZE_OF_EFI_FILE_INFO; 467 NameSize = StrSize (DirEnt->FileString); 468 ResultSize = Size + NameSize; 469 470 Status = EFI_BUFFER_TOO_SMALL; 471 if (*BufferSize >= ResultSize) { 472 Status = EFI_SUCCESS; 473 Entry = &DirEnt->Entry; 474 Info = Buffer; 475 Info->Size = ResultSize; 476 if ((Entry->Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) { 477 Cluster = (Entry->FileClusterHigh << 16) | Entry->FileCluster; 478 Info->PhysicalSize = FatPhysicalDirSize (Volume, Cluster); 479 Info->FileSize = Info->PhysicalSize; 480 } else { 481 Info->FileSize = Entry->FileSize; 482 Info->PhysicalSize = FatPhysicalFileSize (Volume, Entry->FileSize); 483 } 484 485 ZeroMem (&FatLastAccess.Time, sizeof (FatLastAccess.Time)); 486 CopyMem (&FatLastAccess.Date, &Entry->FileLastAccess, sizeof (FatLastAccess.Date)); 487 FatFatTimeToEfiTime (&FatLastAccess, &Info->LastAccessTime); 488 FatFatTimeToEfiTime (&Entry->FileCreateTime, &Info->CreateTime); 489 FatFatTimeToEfiTime (&Entry->FileModificationTime, &Info->ModificationTime); 490 Info->Attribute = Entry->Attributes & EFI_FILE_VALID_ATTR; 491 CopyMem ((CHAR8 *) Buffer + Size, DirEnt->FileString, NameSize); 492 } 493 494 *BufferSize = ResultSize; 495 return Status; 496 } 497 498 /** 499 500 Search the directory for the directory entry whose filename is FileNameString. 501 502 @param OFile - The parent OFile whose directory is to be searched. 503 @param FileNameString - The filename to be searched. 504 @param PtrDirEnt - pointer to the directory entry if found. 505 506 @retval EFI_SUCCESS - Find the directory entry or not found. 507 @return other - An error occurred when reading the directory entries. 508 509 **/ 510 STATIC 511 EFI_STATUS 512 FatSearchODir ( 513 IN FAT_OFILE *OFile, 514 IN CHAR16 *FileNameString, 515 OUT FAT_DIRENT **PtrDirEnt 516 ) 517 { 518 BOOLEAN PossibleShortName; 519 CHAR8 File8Dot3Name[FAT_NAME_LEN]; 520 FAT_ODIR *ODir; 521 FAT_DIRENT *DirEnt; 522 EFI_STATUS Status; 523 524 ODir = OFile->ODir; 525 ASSERT (ODir != NULL); 526 // 527 // Check if the file name is a valid short name 528 // 529 PossibleShortName = FatCheckIs8Dot3Name (FileNameString, File8Dot3Name); 530 // 531 // Search the hash table first 532 // 533 DirEnt = *FatLongNameHashSearch (ODir, FileNameString); 534 if (DirEnt == NULL && PossibleShortName) { 535 DirEnt = *FatShortNameHashSearch (ODir, File8Dot3Name); 536 } 537 if (DirEnt == NULL) { 538 // 539 // We fail to get the directory entry from hash table; we then 540 // search the rest directory 541 // 542 while (!ODir->EndOfDir) { 543 Status = FatLoadNextDirEnt (OFile, &DirEnt); 544 if (EFI_ERROR (Status)) { 545 return Status; 546 } 547 548 if (DirEnt != NULL) { 549 if (FatStriCmp (FileNameString, DirEnt->FileString) == 0) { 550 break; 551 } 552 553 if (PossibleShortName && CompareMem (File8Dot3Name, DirEnt->Entry.FileName, FAT_NAME_LEN) == 0) { 554 break; 555 } 556 } 557 } 558 } 559 560 *PtrDirEnt = DirEnt; 561 return EFI_SUCCESS; 562 } 563 564 /** 565 566 Set the OFile's current directory cursor to the list head. 567 568 @param OFile - The directory OFile whose directory cursor is reset. 569 570 **/ 571 VOID 572 FatResetODirCursor ( 573 IN FAT_OFILE *OFile 574 ) 575 { 576 FAT_ODIR *ODir; 577 578 ODir = OFile->ODir; 579 ASSERT (ODir != NULL); 580 ODir->CurrentCursor = &(ODir->ChildList); 581 ODir->CurrentPos = 0; 582 } 583 584 /** 585 586 Set the directory's cursor to the next and get the next directory entry. 587 588 @param OFile - The parent OFile. 589 @param PtrDirEnt - The next directory entry. 590 591 @retval EFI_SUCCESS - We get the next directory entry successfully. 592 @return other - An error occurred when get next directory entry. 593 594 **/ 595 EFI_STATUS 596 FatGetNextDirEnt ( 597 IN FAT_OFILE *OFile, 598 OUT FAT_DIRENT **PtrDirEnt 599 ) 600 { 601 EFI_STATUS Status; 602 FAT_DIRENT *DirEnt; 603 FAT_ODIR *ODir; 604 605 ODir = OFile->ODir; 606 ASSERT (ODir != NULL); 607 if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) { 608 // 609 // End of directory, we will try one more time 610 // 611 if (!ODir->EndOfDir) { 612 // 613 // Read directory from disk 614 // 615 Status = FatLoadNextDirEnt (OFile, &DirEnt); 616 if (EFI_ERROR (Status)) { 617 return Status; 618 } 619 } 620 } 621 622 if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) { 623 // 624 // End of directory, return NULL 625 // 626 DirEnt = NULL; 627 ODir->CurrentPos = ODir->CurrentEndPos; 628 } else { 629 ODir->CurrentCursor = ODir->CurrentCursor->ForwardLink; 630 DirEnt = DIRENT_FROM_LINK (ODir->CurrentCursor); 631 ODir->CurrentPos = DirEnt->EntryPos + 1; 632 } 633 634 *PtrDirEnt = DirEnt; 635 return EFI_SUCCESS; 636 } 637 638 /** 639 640 Set the directory entry count according to the filename. 641 642 @param OFile - The corresponding OFile. 643 @param DirEnt - The directory entry to be set. 644 645 **/ 646 STATIC 647 VOID 648 FatSetEntryCount ( 649 IN FAT_OFILE *OFile, 650 IN FAT_DIRENT *DirEnt 651 ) 652 { 653 CHAR16 *FileString; 654 CHAR8 *File8Dot3Name; 655 656 // 657 // Get new entry count and set the 8.3 name 658 // 659 DirEnt->EntryCount = 1; 660 FileString = DirEnt->FileString; 661 File8Dot3Name = DirEnt->Entry.FileName; 662 SetMem (File8Dot3Name, FAT_NAME_LEN, ' '); 663 if (StrCmp (FileString, L".") == 0) { 664 // 665 // "." entry 666 // 667 File8Dot3Name[0] = '.'; 668 FatCloneDirEnt (DirEnt, OFile->DirEnt); 669 } else if (StrCmp (FileString, L"..") == 0) { 670 // 671 // ".." entry 672 // 673 File8Dot3Name[0] = '.'; 674 File8Dot3Name[1] = '.'; 675 FatCloneDirEnt (DirEnt, OFile->Parent->DirEnt); 676 } else { 677 // 678 // Normal name 679 // 680 if (FatCheckIs8Dot3Name (FileString, File8Dot3Name)) { 681 // 682 // This file name is a valid 8.3 file name, we need to further check its case flag 683 // 684 FatSetCaseFlag (DirEnt); 685 } else { 686 // 687 // The file name is not a valid 8.3 name we need to generate an 8.3 name for it 688 // 689 FatCreate8Dot3Name (OFile, DirEnt); 690 DirEnt->EntryCount = (UINT8)(LFN_ENTRY_NUMBER (StrLen (FileString)) + DirEnt->EntryCount); 691 } 692 } 693 } 694 695 /** 696 697 Append a zero cluster to the current OFile. 698 699 @param OFile - The directory OFile which needs to be updated. 700 701 @retval EFI_SUCCESS - Append a zero cluster to the OFile successfully. 702 @return other - An error occurred when appending the zero cluster. 703 704 **/ 705 STATIC 706 EFI_STATUS 707 FatExpandODir ( 708 IN FAT_OFILE *OFile 709 ) 710 { 711 return FatExpandOFile (OFile, OFile->FileSize + OFile->Volume->ClusterSize); 712 } 713 714 /** 715 716 Search the Root OFile for the possible volume label. 717 718 @param Root - The Root OFile. 719 @param DirEnt - The returned directory entry of volume label. 720 721 @retval EFI_SUCCESS - The search process is completed successfully. 722 @return other - An error occurred when searching volume label. 723 724 **/ 725 STATIC 726 EFI_STATUS 727 FatSeekVolumeId ( 728 IN FAT_OFILE *Root, 729 OUT FAT_DIRENT *DirEnt 730 ) 731 { 732 EFI_STATUS Status; 733 UINTN EntryPos; 734 FAT_DIRECTORY_ENTRY *Entry; 735 736 EntryPos = 0; 737 Entry = &DirEnt->Entry; 738 DirEnt->Invalid = TRUE; 739 do { 740 Status = FatAccessEntry (Root, ReadData, EntryPos, Entry); 741 if (EFI_ERROR (Status)) { 742 return Status; 743 } 744 745 if (((UINT8) Entry->FileName[0] != DELETE_ENTRY_MARK) && (((Entry->Attributes) & (~FAT_ATTRIBUTE_ARCHIVE)) == FAT_ATTRIBUTE_VOLUME_ID)) { 746 DirEnt->EntryPos = (UINT16) EntryPos; 747 DirEnt->EntryCount = 1; 748 DirEnt->Invalid = FALSE; 749 break; 750 } 751 752 EntryPos++; 753 } while (Entry->FileName[0] != EMPTY_ENTRY_MARK); 754 return EFI_SUCCESS; 755 } 756 757 /** 758 759 Use First Fit Algorithm to insert directory entry. 760 Only this function will erase "E5" entries in a directory. 761 In view of safest recovery, this function will only be triggered 762 when maximum directory entry number has reached. 763 764 @param OFile - The corresponding OFile. 765 @param DirEnt - The directory entry to be inserted. 766 767 @retval EFI_SUCCESS - The directory entry has been successfully inserted. 768 @retval EFI_VOLUME_FULL - The directory can not hold more directory entries. 769 @return Others - Some error occurred when inserting new directory entries. 770 771 **/ 772 STATIC 773 EFI_STATUS 774 FatFirstFitInsertDirEnt ( 775 IN FAT_OFILE *OFile, 776 IN FAT_DIRENT *DirEnt 777 ) 778 { 779 EFI_STATUS Status; 780 FAT_ODIR *ODir; 781 LIST_ENTRY *CurrentEntry; 782 FAT_DIRENT *CurrentDirEnt; 783 UINT32 CurrentPos; 784 UINT32 LabelPos; 785 UINT32 NewEntryPos; 786 UINT16 EntryCount; 787 FAT_DIRENT LabelDirEnt; 788 789 LabelPos = 0; 790 if (OFile->Parent == NULL) { 791 Status = FatSeekVolumeId (OFile, &LabelDirEnt); 792 if (EFI_ERROR (Status)) { 793 return Status; 794 } 795 796 if (!LabelDirEnt.Invalid) { 797 LabelPos = LabelDirEnt.EntryPos; 798 } 799 } 800 801 EntryCount = DirEnt->EntryCount; 802 NewEntryPos = EntryCount; 803 CurrentPos = 0; 804 ODir = OFile->ODir; 805 for (CurrentEntry = ODir->ChildList.ForwardLink; 806 CurrentEntry != &ODir->ChildList; 807 CurrentEntry = CurrentEntry->ForwardLink 808 ) { 809 CurrentDirEnt = DIRENT_FROM_LINK (CurrentEntry); 810 if (NewEntryPos + CurrentDirEnt->EntryCount <= CurrentDirEnt->EntryPos) { 811 if (LabelPos > NewEntryPos || LabelPos <= CurrentPos) { 812 // 813 // first fit succeeded 814 // 815 goto Done; 816 } 817 } 818 819 CurrentPos = CurrentDirEnt->EntryPos; 820 NewEntryPos = CurrentPos + EntryCount; 821 } 822 823 if (NewEntryPos >= ODir->CurrentEndPos) { 824 return EFI_VOLUME_FULL; 825 } 826 827 Done: 828 DirEnt->EntryPos = (UINT16) NewEntryPos; 829 DirEnt->Link.BackLink = CurrentEntry; 830 return EFI_SUCCESS; 831 } 832 833 /** 834 835 Find the new directory entry position for the directory entry. 836 837 @param OFile - The corresponding OFile. 838 @param DirEnt - The directory entry whose new position is to be set. 839 840 @retval EFI_SUCCESS - The new directory entry position is successfully found. 841 @retval EFI_VOLUME_FULL - The directory has reach its maximum capacity. 842 @return other - An error occurred when reading the directory entry. 843 844 **/ 845 STATIC 846 EFI_STATUS 847 FatNewEntryPos ( 848 IN FAT_OFILE *OFile, 849 IN FAT_DIRENT *DirEnt 850 ) 851 { 852 EFI_STATUS Status; 853 FAT_ODIR *ODir; 854 FAT_DIRENT *TempDirEnt; 855 UINT32 NewEndPos; 856 857 ODir = OFile->ODir; 858 ASSERT (ODir != NULL); 859 // 860 // Make sure the whole directory has been loaded 861 // 862 while (!ODir->EndOfDir) { 863 Status = FatLoadNextDirEnt (OFile, &TempDirEnt); 864 if (EFI_ERROR (Status)) { 865 return Status; 866 } 867 } 868 // 869 // We will append this entry to the end of directory 870 // 871 FatGetCurrentFatTime (&DirEnt->Entry.FileCreateTime); 872 CopyMem (&DirEnt->Entry.FileModificationTime, &DirEnt->Entry.FileCreateTime, sizeof (FAT_DATE_TIME)); 873 CopyMem (&DirEnt->Entry.FileLastAccess, &DirEnt->Entry.FileCreateTime.Date, sizeof (FAT_DATE)); 874 NewEndPos = ODir->CurrentEndPos + DirEnt->EntryCount; 875 if (NewEndPos * sizeof (FAT_DIRECTORY_ENTRY) > OFile->FileSize) { 876 if (NewEndPos >= (OFile->IsFixedRootDir ? OFile->Volume->RootEntries : FAT_MAX_DIRENTRY_COUNT)) { 877 // 878 // We try to use fist fit algorithm to insert this directory entry 879 // 880 return FatFirstFitInsertDirEnt (OFile, DirEnt); 881 } 882 // 883 // We should allocate a new cluster for this directory 884 // 885 Status = FatExpandODir (OFile); 886 if (EFI_ERROR (Status)) { 887 return Status; 888 } 889 } 890 // 891 // We append our directory entry at the end of directory file 892 // 893 ODir->CurrentEndPos = NewEndPos; 894 DirEnt->EntryPos = (UINT16) (ODir->CurrentEndPos - 1); 895 return EFI_SUCCESS; 896 } 897 898 /** 899 900 Get the directory entry for the volume. 901 902 @param Volume - FAT file system volume. 903 @param Name - The file name of the volume. 904 905 @retval EFI_SUCCESS - Update the volume with the directory entry sucessfully. 906 @return others - An error occurred when getting volume label. 907 908 **/ 909 EFI_STATUS 910 FatGetVolumeEntry ( 911 IN FAT_VOLUME *Volume, 912 IN CHAR16 *Name 913 ) 914 { 915 EFI_STATUS Status; 916 FAT_DIRENT LabelDirEnt; 917 918 *Name = 0; 919 Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt); 920 if (!EFI_ERROR (Status)) { 921 if (!LabelDirEnt.Invalid) { 922 FatNameToStr (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, FALSE, Name); 923 } 924 } 925 926 return Status; 927 } 928 929 /** 930 931 Set the relevant directory entry into disk for the volume. 932 933 @param Volume - FAT file system volume. 934 @param Name - The new file name of the volume. 935 936 @retval EFI_SUCCESS - Update the Volume sucessfully. 937 @retval EFI_UNSUPPORTED - The input label is not a valid volume label. 938 @return other - An error occurred when setting volume label. 939 940 **/ 941 EFI_STATUS 942 FatSetVolumeEntry ( 943 IN FAT_VOLUME *Volume, 944 IN CHAR16 *Name 945 ) 946 { 947 EFI_STATUS Status; 948 FAT_DIRENT LabelDirEnt; 949 FAT_OFILE *Root; 950 951 Root = Volume->Root; 952 Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt); 953 if (EFI_ERROR (Status)) { 954 return Status; 955 } 956 957 if (LabelDirEnt.Invalid) { 958 // 959 // If there is not the relevant directory entry, create a new one 960 // 961 ZeroMem (&LabelDirEnt, sizeof (FAT_DIRENT)); 962 LabelDirEnt.EntryCount = 1; 963 Status = FatNewEntryPos (Root, &LabelDirEnt); 964 if (EFI_ERROR (Status)) { 965 return Status; 966 } 967 968 LabelDirEnt.Entry.Attributes = FAT_ATTRIBUTE_VOLUME_ID; 969 } 970 971 SetMem (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, ' '); 972 if (FatStrToFat (Name, FAT_NAME_LEN, LabelDirEnt.Entry.FileName)) { 973 return EFI_UNSUPPORTED; 974 } 975 976 FatGetCurrentFatTime (&LabelDirEnt.Entry.FileModificationTime); 977 return FatStoreDirEnt (Root, &LabelDirEnt); 978 } 979 980 /** 981 982 Create "." and ".." directory entries in the newly-created parent OFile. 983 984 @param OFile - The parent OFile. 985 986 @retval EFI_SUCCESS - The dot directory entries are successfully created. 987 @return other - An error occurred when creating the directory entry. 988 989 **/ 990 EFI_STATUS 991 FatCreateDotDirEnts ( 992 IN FAT_OFILE *OFile 993 ) 994 { 995 EFI_STATUS Status; 996 FAT_DIRENT *DirEnt; 997 998 Status = FatExpandODir (OFile); 999 if (EFI_ERROR (Status)) { 1000 return Status; 1001 } 1002 1003 FatSetDirEntCluster (OFile); 1004 // 1005 // Create "." 1006 // 1007 Status = FatCreateDirEnt (OFile, L".", FAT_ATTRIBUTE_DIRECTORY, &DirEnt); 1008 if (EFI_ERROR (Status)) { 1009 return Status; 1010 } 1011 // 1012 // Create ".." 1013 // 1014 Status = FatCreateDirEnt (OFile, L"..", FAT_ATTRIBUTE_DIRECTORY, &DirEnt); 1015 return Status; 1016 } 1017 1018 /** 1019 1020 Create a directory entry in the parent OFile. 1021 1022 @param OFile - The parent OFile. 1023 @param FileName - The filename of the newly-created directory entry. 1024 @param Attributes - The attribute of the newly-created directory entry. 1025 @param PtrDirEnt - The pointer to the newly-created directory entry. 1026 1027 @retval EFI_SUCCESS - The directory entry is successfully created. 1028 @retval EFI_OUT_OF_RESOURCES - Not enough memory to create the directory entry. 1029 @return other - An error occurred when creating the directory entry. 1030 1031 **/ 1032 EFI_STATUS 1033 FatCreateDirEnt ( 1034 IN FAT_OFILE *OFile, 1035 IN CHAR16 *FileName, 1036 IN UINT8 Attributes, 1037 OUT FAT_DIRENT **PtrDirEnt 1038 ) 1039 { 1040 FAT_DIRENT *DirEnt; 1041 FAT_ODIR *ODir; 1042 EFI_STATUS Status; 1043 1044 ASSERT (OFile != NULL); 1045 ODir = OFile->ODir; 1046 ASSERT (ODir != NULL); 1047 DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT)); 1048 if (DirEnt == NULL) { 1049 return EFI_OUT_OF_RESOURCES; 1050 } 1051 1052 DirEnt->Signature = FAT_DIRENT_SIGNATURE; 1053 DirEnt->FileString = AllocateCopyPool (StrSize (FileName), FileName); 1054 if (DirEnt->FileString == NULL) { 1055 Status = EFI_OUT_OF_RESOURCES; 1056 goto Done; 1057 } 1058 // 1059 // Determine how many directory entries we need 1060 // 1061 FatSetEntryCount (OFile, DirEnt); 1062 // 1063 // Determine the file's directory entry position 1064 // 1065 Status = FatNewEntryPos (OFile, DirEnt); 1066 if (EFI_ERROR (Status)) { 1067 goto Done; 1068 } 1069 1070 FatAddDirEnt (ODir, DirEnt); 1071 DirEnt->Entry.Attributes = Attributes; 1072 *PtrDirEnt = DirEnt; 1073 DEBUG ((EFI_D_INFO, "FSOpen: Created new directory entry '%S'\n", DirEnt->FileString)); 1074 return FatStoreDirEnt (OFile, DirEnt); 1075 1076 Done: 1077 FatFreeDirEnt (DirEnt); 1078 return Status; 1079 } 1080 1081 /** 1082 1083 Remove this directory entry node from the list of directory entries and hash table. 1084 1085 @param OFile - The parent OFile. 1086 @param DirEnt - The directory entry to be removed. 1087 1088 @retval EFI_SUCCESS - The directory entry is successfully removed. 1089 @return other - An error occurred when removing the directory entry. 1090 1091 **/ 1092 EFI_STATUS 1093 FatRemoveDirEnt ( 1094 IN FAT_OFILE *OFile, 1095 IN FAT_DIRENT *DirEnt 1096 ) 1097 { 1098 FAT_ODIR *ODir; 1099 1100 ODir = OFile->ODir; 1101 if (ODir->CurrentCursor == &DirEnt->Link) { 1102 // 1103 // Move the directory cursor to its previous directory entry 1104 // 1105 ODir->CurrentCursor = ODir->CurrentCursor->BackLink; 1106 } 1107 // 1108 // Remove from directory entry list 1109 // 1110 RemoveEntryList (&DirEnt->Link); 1111 // 1112 // Remove from hash table 1113 // 1114 FatDeleteFromHashTable (ODir, DirEnt); 1115 DirEnt->Entry.FileName[0] = DELETE_ENTRY_MARK; 1116 DirEnt->Invalid = TRUE; 1117 return FatStoreDirEnt (OFile, DirEnt); 1118 } 1119 1120 /** 1121 1122 Open the directory entry to get the OFile. 1123 1124 @param Parent - The parent OFile. 1125 @param DirEnt - The directory entry to be opened. 1126 1127 @retval EFI_SUCCESS - The directory entry is successfully opened. 1128 @retval EFI_OUT_OF_RESOURCES - not enough memory to allocate a new OFile. 1129 @return other - An error occurred when opening the directory entry. 1130 1131 **/ 1132 EFI_STATUS 1133 FatOpenDirEnt ( 1134 IN FAT_OFILE *Parent, 1135 IN FAT_DIRENT *DirEnt 1136 ) 1137 { 1138 FAT_OFILE *OFile; 1139 FAT_VOLUME *Volume; 1140 1141 if (DirEnt->OFile == NULL) { 1142 // 1143 // Open the directory entry 1144 // 1145 OFile = AllocateZeroPool (sizeof (FAT_OFILE)); 1146 if (OFile == NULL) { 1147 return EFI_OUT_OF_RESOURCES; 1148 } 1149 1150 OFile->Signature = FAT_OFILE_SIGNATURE; 1151 InitializeListHead (&OFile->Opens); 1152 InitializeListHead (&OFile->ChildHead); 1153 OFile->Parent = Parent; 1154 OFile->DirEnt = DirEnt; 1155 if (Parent != NULL) { 1156 // 1157 // The newly created OFile is not root 1158 // 1159 Volume = Parent->Volume; 1160 OFile->FullPathLen = Parent->FullPathLen + 1 + StrLen (DirEnt->FileString); 1161 OFile->FileCluster = ((DirEnt->Entry.FileClusterHigh) << 16) | (DirEnt->Entry.FileCluster); 1162 InsertTailList (&Parent->ChildHead, &OFile->ChildLink); 1163 } else { 1164 // 1165 // The newly created OFile is root 1166 // 1167 Volume = VOLUME_FROM_ROOT_DIRENT (DirEnt); 1168 Volume->Root = OFile; 1169 OFile->FileCluster = Volume->RootCluster; 1170 if (Volume->FatType != Fat32) { 1171 OFile->IsFixedRootDir = TRUE; 1172 } 1173 } 1174 1175 OFile->FileCurrentCluster = OFile->FileCluster; 1176 OFile->Volume = Volume; 1177 InsertHeadList (&Volume->CheckRef, &OFile->CheckLink); 1178 1179 OFile->FileSize = DirEnt->Entry.FileSize; 1180 if ((DirEnt->Entry.Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) { 1181 if (OFile->IsFixedRootDir) { 1182 OFile->FileSize = Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY); 1183 } else { 1184 OFile->FileSize = FatPhysicalDirSize (Volume, OFile->FileCluster); 1185 } 1186 1187 FatRequestODir (OFile); 1188 if (OFile->ODir == NULL) { 1189 return EFI_OUT_OF_RESOURCES; 1190 } 1191 } 1192 1193 DirEnt->OFile = OFile; 1194 } 1195 1196 return EFI_SUCCESS; 1197 } 1198 1199 /** 1200 1201 Close the directory entry and free the OFile. 1202 1203 @param DirEnt - The directory entry to be closed. 1204 1205 **/ 1206 VOID 1207 FatCloseDirEnt ( 1208 IN FAT_DIRENT *DirEnt 1209 ) 1210 { 1211 FAT_OFILE *OFile; 1212 FAT_VOLUME *Volume; 1213 1214 OFile = DirEnt->OFile; 1215 ASSERT (OFile != NULL); 1216 Volume = OFile->Volume; 1217 1218 if (OFile->ODir != NULL) { 1219 FatDiscardODir (OFile); 1220 } 1221 1222 if (OFile->Parent == NULL) { 1223 Volume->Root = NULL; 1224 } else { 1225 RemoveEntryList (&OFile->ChildLink); 1226 } 1227 1228 FreePool (OFile); 1229 DirEnt->OFile = NULL; 1230 if (DirEnt->Invalid == TRUE) { 1231 // 1232 // Free directory entry itself 1233 // 1234 FatFreeDirEnt (DirEnt); 1235 } 1236 } 1237 1238 /** 1239 1240 Traverse filename and open all OFiles that can be opened. 1241 Update filename pointer to the component that can't be opened. 1242 If more than one name component remains, returns an error; 1243 otherwise, return the remaining name component so that the caller might choose to create it. 1244 1245 @param PtrOFile - As input, the reference OFile; as output, the located OFile. 1246 @param FileName - The file name relevant to the OFile. 1247 @param Attributes - The attribute of the destination OFile. 1248 @param NewFileName - The remaining file name. 1249 1250 @retval EFI_NOT_FOUND - The file name can't be opened and there is more than one 1251 components within the name left (this means the name can 1252 not be created either). 1253 @retval EFI_INVALID_PARAMETER - The parameter is not valid. 1254 @retval EFI_SUCCESS - Open the file successfully. 1255 @return other - An error occured when locating the OFile. 1256 1257 **/ 1258 EFI_STATUS 1259 FatLocateOFile ( 1260 IN OUT FAT_OFILE **PtrOFile, 1261 IN CHAR16 *FileName, 1262 IN UINT8 Attributes, 1263 OUT CHAR16 *NewFileName 1264 ) 1265 { 1266 EFI_STATUS Status; 1267 FAT_VOLUME *Volume; 1268 CHAR16 ComponentName[EFI_PATH_STRING_LENGTH]; 1269 UINTN FileNameLen; 1270 BOOLEAN DirIntended; 1271 CHAR16 *Next; 1272 FAT_OFILE *OFile; 1273 FAT_DIRENT *DirEnt; 1274 1275 DirEnt = NULL; 1276 1277 FileNameLen = StrLen (FileName); 1278 if (FileNameLen == 0) { 1279 return EFI_INVALID_PARAMETER; 1280 } 1281 1282 OFile = *PtrOFile; 1283 Volume = OFile->Volume; 1284 1285 DirIntended = FALSE; 1286 if (FileName[FileNameLen - 1] == PATH_NAME_SEPARATOR) { 1287 DirIntended = TRUE; 1288 } 1289 // 1290 // If name starts with path name separator, then move to root OFile 1291 // 1292 if (*FileName == PATH_NAME_SEPARATOR) { 1293 OFile = Volume->Root; 1294 FileName++; 1295 FileNameLen--; 1296 } 1297 // 1298 // Per FAT Spec the file name should meet the following criteria: 1299 // C1. Length (FileLongName) <= 255 1300 // C2. Length (X:FileFullPath<NUL>) <= 260 1301 // Here we check C2 first. 1302 // 1303 if (2 + OFile->FullPathLen + 1 + FileNameLen + 1 > EFI_PATH_STRING_LENGTH) { 1304 // 1305 // Full path length can not surpass 256 1306 // 1307 return EFI_INVALID_PARAMETER; 1308 } 1309 // 1310 // Start at current location 1311 // 1312 Next = FileName; 1313 for (;;) { 1314 // 1315 // Get the next component name 1316 // 1317 FileName = Next; 1318 Next = FatGetNextNameComponent (FileName, ComponentName); 1319 1320 // 1321 // If end of the file name, we're done 1322 // 1323 if (ComponentName[0] == 0) { 1324 if (DirIntended && OFile->ODir == NULL) { 1325 return EFI_NOT_FOUND; 1326 } 1327 1328 NewFileName[0] = 0; 1329 break; 1330 } 1331 // 1332 // If "dot", then current 1333 // 1334 if (StrCmp (ComponentName, L".") == 0) { 1335 continue; 1336 } 1337 // 1338 // If "dot dot", then parent 1339 // 1340 if (StrCmp (ComponentName, L"..") == 0) { 1341 if (OFile->Parent == NULL) { 1342 return EFI_INVALID_PARAMETER; 1343 } 1344 OFile = OFile->Parent; 1345 continue; 1346 } 1347 1348 if (!FatFileNameIsValid (ComponentName, NewFileName)) { 1349 return EFI_INVALID_PARAMETER; 1350 } 1351 // 1352 // We have a component name, try to open it 1353 // 1354 if (OFile->ODir == NULL) { 1355 // 1356 // This file isn't a directory, can't open it 1357 // 1358 return EFI_NOT_FOUND; 1359 } 1360 // 1361 // Search the compName in the directory 1362 // 1363 Status = FatSearchODir (OFile, NewFileName, &DirEnt); 1364 if (EFI_ERROR (Status)) { 1365 return Status; 1366 } 1367 1368 if (DirEnt == NULL) { 1369 // 1370 // component name is not found in the directory 1371 // 1372 if (*Next != 0) { 1373 return EFI_NOT_FOUND; 1374 } 1375 1376 if (DirIntended && (Attributes & FAT_ATTRIBUTE_DIRECTORY) == 0) { 1377 return EFI_INVALID_PARAMETER; 1378 } 1379 // 1380 // It's the last component name - return with the open 1381 // path and the remaining name 1382 // 1383 break; 1384 } 1385 1386 Status = FatOpenDirEnt (OFile, DirEnt); 1387 if (EFI_ERROR (Status)) { 1388 return Status; 1389 } 1390 1391 OFile = DirEnt->OFile; 1392 } 1393 1394 *PtrOFile = OFile; 1395 return EFI_SUCCESS; 1396 } 1397 1398