1 /** @file 2 File IO routines inspired by Streams with an EFI flavor 3 4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> 5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> 6 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this distribution. The full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 Basic support for opening files on different device types. The device string 16 is in the form of DevType:Path. Current DevType is required as there is no 17 current mounted device concept of current working directory concept implement 18 by this library. 19 20 Device names are case insensitive and only check the leading characters for 21 unique matches. Thus the following are all the same: 22 LoadFile0: 23 l0: 24 L0: 25 Lo0: 26 27 Supported Device Names: 28 A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes 29 l1: - EFI LoadFile device one. 30 B0: - EFI BlockIo zero. 31 fs3: - EFI Simple File System device 3 32 Fv2: - EFI Firmware VOlume device 2 33 10.0.1.102: - TFTP service IP followed by the file name 34 **/ 35 36 #include <PiDxe.h> 37 #include <Protocol/BlockIo.h> 38 #include <Protocol/DiskIo.h> 39 #include <Protocol/SimpleFileSystem.h> 40 #include <Protocol/FirmwareVolume2.h> 41 #include <Protocol/LoadFile.h> 42 #include <Protocol/FirmwareVolumeBlock.h> 43 44 #include <Guid/FileInfo.h> 45 #include <Guid/ZeroGuid.h> 46 47 #include <Library/BaseLib.h> 48 #include <Library/MemoryAllocationLib.h> 49 #include <Library/DevicePathLib.h> 50 #include <Library/PrintLib.h> 51 #include <Library/BaseMemoryLib.h> 52 #include <Library/UefiLib.h> 53 #include <Library/UefiBootServicesTableLib.h> 54 #include <Library/UefiRuntimeServicesTableLib.h> 55 #include <Library/DebugLib.h> 56 #include <Library/EfiFileLib.h> 57 #include <Library/PcdLib.h> 58 #include <Library/EblNetworkLib.h> 59 60 61 CHAR8 *gCwd = NULL; 62 63 #define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641 64 #define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56 65 66 // Need to defend against this overflowing 67 #define MAX_CMD_LINE 0x200 68 69 typedef struct { 70 UINT32 Header; 71 EFI_OPEN_FILE File; 72 UINT32 Footer; 73 } EFI_OPEN_FILE_GUARD; 74 75 76 // globals to store current open device info 77 EFI_HANDLE *mBlkIo = NULL; 78 UINTN mBlkIoCount = 0; 79 80 EFI_HANDLE *mFs = NULL; 81 UINTN mFsCount = 0; 82 // mFsInfo[] array entries must match mFs[] handles 83 EFI_FILE_SYSTEM_INFO **mFsInfo = NULL; 84 85 EFI_HANDLE *mFv = NULL; 86 UINTN mFvCount = 0; 87 EFI_HANDLE *mLoadFile = NULL; 88 UINTN mLoadFileCount = 0; 89 90 91 92 /** 93 Internal worker function to validate a File handle. 94 95 @param File Open File Handle 96 97 @return TRUE File is valid 98 @return FALSE File is not valid 99 100 101 **/ 102 BOOLEAN 103 FileHandleValid ( 104 IN EFI_OPEN_FILE *File 105 ) 106 { 107 EFI_OPEN_FILE_GUARD *GuardFile; 108 109 // Look right before and after file structure for the correct signatures 110 GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File); 111 if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) || 112 (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) { 113 return FALSE; 114 } 115 116 return TRUE; 117 } 118 119 /** 120 Internal worker function. If Buffer is not NULL free it. 121 122 @param Buffer Buffer to FreePool() 123 124 **/ 125 VOID 126 EblFreePool ( 127 IN VOID *Buffer 128 ) 129 { 130 if (Buffer != NULL) { 131 FreePool (Buffer); 132 } 133 } 134 135 /** 136 Update Device List Global Variables 137 138 **/ 139 VOID 140 EblUpdateDeviceLists ( 141 VOID 142 ) 143 { 144 EFI_STATUS Status; 145 UINTN Size; 146 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; 147 EFI_FILE_HANDLE Root; 148 UINTN Index; 149 150 if (mBlkIo != NULL) { 151 FreePool (mBlkIo); 152 } 153 gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo); 154 155 156 157 if (mFv != NULL) { 158 FreePool (mFv); 159 } 160 gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv); 161 162 if (mLoadFile != NULL) { 163 FreePool (mLoadFile); 164 } 165 gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile); 166 167 if (mFs != NULL) { 168 FreePool (mFs); 169 } 170 171 if (&mFsInfo[0] != NULL) { 172 // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code 173 for (Index = 0; Index < mFsCount; Index++) { 174 if (mFsInfo[Index] != NULL) { 175 FreePool (mFsInfo[Index]); 176 } 177 } 178 FreePool (mFsInfo); 179 } 180 181 gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs); 182 183 184 mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *)); 185 if (mFsInfo == NULL) { 186 // If we can't do this then we can't support file system entries 187 mFsCount = 0; 188 } else { 189 // Loop through all the file system structures and cache the file system info data 190 for (Index =0; Index < mFsCount; Index++) { 191 Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs); 192 if (!EFI_ERROR (Status)) { 193 Status = Fs->OpenVolume (Fs, &Root); 194 if (!EFI_ERROR (Status)) { 195 // Get information about the volume 196 Size = 0; 197 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]); 198 if (Status == EFI_BUFFER_TOO_SMALL) { 199 mFsInfo[Index] = AllocatePool (Size); 200 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]); 201 } 202 203 Root->Close (Root); 204 } 205 } 206 } 207 } 208 } 209 210 211 /** 212 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\. 213 Return TRUE if the <devce name> prefix of PathName matches a file system 214 Volume Name. MatchIndex is the array index in mFsInfo[] of the match, 215 and it can be used with mFs[] to find the handle that needs to be opened 216 217 @param PathName PathName to check 218 @param FileStart Index of the first character of the <path> 219 @param MatchIndex Index in mFsInfo[] that matches 220 221 @return TRUE PathName matches a Volume Label and MatchIndex is valid 222 @return FALSE PathName does not match a Volume Label MatchIndex undefined 223 224 **/ 225 BOOLEAN 226 EblMatchVolumeName ( 227 IN CHAR8 *PathName, 228 IN UINTN FileStart, 229 OUT UINTN *MatchIndex 230 ) 231 { 232 UINTN Index; 233 UINTN Compare; 234 UINTN VolStrLen; 235 BOOLEAN Match; 236 237 for (Index =0; Index < mFsCount; Index++) { 238 if (mFsInfo[Index] == NULL) { 239 // FsInfo is not valid so skip it 240 continue; 241 } 242 VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel); 243 for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) { 244 if (Compare > VolStrLen) { 245 Match = FALSE; 246 break; 247 } 248 if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) { 249 // If the VolumeLabel has a space allow a _ to match with it in addition to ' ' 250 if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) { 251 Match = FALSE; 252 break; 253 } 254 } 255 } 256 if (Match) { 257 *MatchIndex = Index; 258 return TRUE; 259 } 260 } 261 262 return FALSE; 263 } 264 265 266 /** 267 Return the number of devices of the current type active in the system 268 269 @param Type Device type to check 270 271 @return 0 Invalid type 272 273 **/ 274 UINTN 275 EfiGetDeviceCounts ( 276 IN EFI_OPEN_FILE_TYPE DeviceType 277 ) 278 { 279 switch (DeviceType) { 280 case EfiOpenLoadFile: 281 return mLoadFileCount; 282 case EfiOpenFirmwareVolume: 283 return mFvCount; 284 case EfiOpenFileSystem: 285 return mFsCount; 286 case EfiOpenBlockIo: 287 return mBlkIoCount; 288 default: 289 return 0; 290 } 291 } 292 293 EFI_STATUS 294 ConvertIpStringToEfiIp ( 295 IN CHAR8 *PathName, 296 OUT EFI_IP_ADDRESS *ServerIp 297 ) 298 { 299 CHAR8 *Str; 300 301 Str = PathName; 302 ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str); 303 304 Str = AsciiStrStr (Str, "."); 305 if (Str == NULL) { 306 return EFI_DEVICE_ERROR; 307 } 308 309 ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str); 310 311 Str = AsciiStrStr (Str, "."); 312 if (Str == NULL) { 313 return EFI_DEVICE_ERROR; 314 } 315 316 ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str); 317 318 Str = AsciiStrStr (Str, "."); 319 if (Str == NULL) { 320 return EFI_DEVICE_ERROR; 321 } 322 323 ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str); 324 325 return EFI_SUCCESS; 326 } 327 328 329 /** 330 Internal work function to extract a device number from a string skipping 331 text. Easy way to extract numbers from strings like blk7:. 332 333 @param Str String to extract device number form 334 335 @return -1 Device string is not valid 336 @return Device # 337 338 **/ 339 UINTN 340 EblConvertDevStringToNumber ( 341 IN CHAR8 *Str 342 ) 343 { 344 UINTN Max; 345 UINTN Index; 346 347 348 // Find the first digit 349 Max = AsciiStrLen (Str); 350 for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) { 351 Str++; 352 } 353 if (Index == Max) { 354 return (UINTN)-1; 355 } 356 357 return AsciiStrDecimalToUintn (Str); 358 } 359 360 361 /** 362 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo 363 364 @param File Open file handle 365 @param FileName Name of file after device stripped off 366 367 368 **/ 369 EFI_STATUS 370 EblFileDevicePath ( 371 IN OUT EFI_OPEN_FILE *File, 372 IN CHAR8 *FileName, 373 IN CONST UINT64 OpenMode 374 ) 375 { 376 EFI_STATUS Status; 377 UINTN Size; 378 FILEPATH_DEVICE_PATH *FilePath; 379 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath; 380 CHAR16 UnicodeFileName[MAX_PATHNAME]; 381 EFI_BLOCK_IO_PROTOCOL *BlkIo; 382 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; 383 EFI_FILE_HANDLE Root; 384 385 386 if ( *FileName != 0 ) { 387 AsciiStrToUnicodeStr (FileName, UnicodeFileName); 388 } else { 389 AsciiStrToUnicodeStr ("\\", UnicodeFileName); 390 } 391 392 Size = StrSize (UnicodeFileName); 393 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL)); 394 if (FileDevicePath != NULL) { 395 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath; 396 FilePath->Header.Type = MEDIA_DEVICE_PATH; 397 FilePath->Header.SubType = MEDIA_FILEPATH_DP; 398 CopyMem (&FilePath->PathName, UnicodeFileName, Size); 399 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH); 400 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header)); 401 402 if (File->EfiHandle != NULL) { 403 File->DevicePath = DevicePathFromHandle (File->EfiHandle); 404 } 405 406 File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath); 407 FreePool (FileDevicePath); 408 } 409 410 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo); 411 if (!EFI_ERROR (Status)) { 412 File->FsBlockIoMedia = BlkIo->Media; 413 File->FsBlockIo = BlkIo; 414 415 // If we are not opening the device this will get over written with file info 416 File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize); 417 } 418 419 if (File->Type == EfiOpenFileSystem) { 420 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs); 421 if (!EFI_ERROR (Status)) { 422 Status = Fs->OpenVolume (Fs, &Root); 423 if (!EFI_ERROR (Status)) { 424 // Get information about the volume 425 Size = 0; 426 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo); 427 if (Status == EFI_BUFFER_TOO_SMALL) { 428 File->FsInfo = AllocatePool (Size); 429 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo); 430 } 431 432 // Get information about the file 433 Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0); 434 if (!EFI_ERROR (Status)) { 435 Size = 0; 436 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL); 437 if (Status == EFI_BUFFER_TOO_SMALL) { 438 File->FsFileInfo = AllocatePool (Size); 439 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo); 440 if (!EFI_ERROR (Status)) { 441 File->Size = (UINTN)File->FsFileInfo->FileSize; 442 File->MaxPosition = (UINT64)File->Size; 443 } 444 } 445 } 446 447 Root->Close (Root); 448 } 449 } 450 } else if (File->Type == EfiOpenBlockIo) { 451 File->Size = (UINTN)File->MaxPosition; 452 } 453 454 return Status; 455 } 456 457 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a)) 458 459 EFI_STATUS 460 CompareGuidToString ( 461 IN EFI_GUID *Guid, 462 IN CHAR8 *String 463 ) 464 { 465 CHAR8 AsciiGuid[64]; 466 CHAR8 *StringPtr; 467 CHAR8 *GuidPtr; 468 469 AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid); 470 471 StringPtr = String; 472 GuidPtr = AsciiGuid; 473 474 while ((*StringPtr != '\0') && (*GuidPtr != '\0')) { 475 // Skip dashes 476 if (*StringPtr == '-') { 477 StringPtr++; 478 continue; 479 } 480 481 if (*GuidPtr == '-') { 482 GuidPtr++; 483 continue; 484 } 485 486 if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) { 487 return EFI_NOT_FOUND; 488 } 489 490 StringPtr++; 491 GuidPtr++; 492 } 493 494 return EFI_SUCCESS; 495 } 496 497 498 /** 499 Internal work function to fill in EFI_OPEN_FILE information for the FV 500 501 @param File Open file handle 502 @param FileName Name of file after device stripped off 503 504 505 **/ 506 EFI_STATUS 507 EblFvFileDevicePath ( 508 IN OUT EFI_OPEN_FILE *File, 509 IN CHAR8 *FileName, 510 IN CONST UINT64 OpenMode 511 ) 512 { 513 EFI_STATUS Status; 514 EFI_STATUS GetNextFileStatus; 515 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode; 516 EFI_DEVICE_PATH_PROTOCOL *DevicePath; 517 UINTN Key; 518 UINT32 AuthenticationStatus; 519 CHAR8 AsciiSection[MAX_PATHNAME]; 520 VOID *Section; 521 UINTN SectionSize; 522 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; 523 EFI_LBA Lba; 524 UINTN BlockSize; 525 UINTN NumberOfBlocks; 526 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL; 527 UINTN Index; 528 529 530 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv); 531 if (EFI_ERROR (Status)) { 532 return Status; 533 } 534 535 // Get FVB Info about the handle 536 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb); 537 if (!EFI_ERROR (Status)) { 538 Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart); 539 if (!EFI_ERROR (Status)) { 540 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart; 541 File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER); 542 for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) { 543 File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY); 544 } 545 546 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) { 547 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks); 548 if (EFI_ERROR (Status)) { 549 break; 550 } 551 } 552 } 553 } 554 555 556 DevicePath = DevicePathFromHandle (File->EfiHandle); 557 558 if (*FileName == '\0') { 559 File->DevicePath = DuplicateDevicePath (DevicePath); 560 File->Size = File->FvSize; 561 File->MaxPosition = File->Size; 562 } else { 563 Key = 0; 564 do { 565 File->FvType = EFI_FV_FILETYPE_ALL; 566 GetNextFileStatus = File->Fv->GetNextFile ( 567 File->Fv, 568 &Key, 569 &File->FvType, 570 &File->FvNameGuid, 571 &File->FvAttributes, 572 &File->Size 573 ); 574 if (!EFI_ERROR (GetNextFileStatus)) { 575 // Compare GUID first 576 Status = CompareGuidToString (&File->FvNameGuid, FileName); 577 if (!EFI_ERROR(Status)) { 578 break; 579 } 580 581 Section = NULL; 582 Status = File->Fv->ReadSection ( 583 File->Fv, 584 &File->FvNameGuid, 585 EFI_SECTION_USER_INTERFACE, 586 0, 587 &Section, 588 &SectionSize, 589 &AuthenticationStatus 590 ); 591 if (!EFI_ERROR (Status)) { 592 UnicodeStrToAsciiStr (Section, AsciiSection); 593 if (AsciiStriCmp (FileName, AsciiSection) == 0) { 594 FreePool (Section); 595 break; 596 } 597 FreePool (Section); 598 } 599 } 600 } while (!EFI_ERROR (GetNextFileStatus)); 601 602 if (EFI_ERROR (GetNextFileStatus)) { 603 return GetNextFileStatus; 604 } 605 606 if (OpenMode != EFI_SECTION_ALL) { 607 // Calculate the size of the section we are targeting 608 Section = NULL; 609 File->Size = 0; 610 Status = File->Fv->ReadSection ( 611 File->Fv, 612 &File->FvNameGuid, 613 (EFI_SECTION_TYPE)OpenMode, 614 0, 615 &Section, 616 &File->Size, 617 &AuthenticationStatus 618 ); 619 if (EFI_ERROR (Status)) { 620 return Status; 621 } 622 } 623 624 File->MaxPosition = File->Size; 625 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid); 626 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode); 627 } 628 629 630 // FVB not required if FV was soft loaded... 631 return EFI_SUCCESS; 632 } 633 634 635 636 637 /** 638 Open a device named by PathName. The PathName includes a device name and 639 path separated by a :. See file header for more details on the PathName 640 syntax. There is no checking to prevent a file from being opened more than 641 one type. 642 643 SectionType is only used to open an FV. Each file in an FV contains multiple 644 sections and only the SectionType section is opened. 645 646 For any file that is opened with EfiOpen() must be closed with EfiClose(). 647 648 @param PathName Path to parse to open 649 @param OpenMode Same as EFI_FILE.Open() 650 @param SectionType Section in FV to open. 651 652 @return NULL Open failed 653 @return Valid EFI_OPEN_FILE handle 654 655 **/ 656 EFI_OPEN_FILE * 657 EfiOpen ( 658 IN CHAR8 *PathName, 659 IN CONST UINT64 OpenMode, 660 IN CONST EFI_SECTION_TYPE SectionType 661 ) 662 { 663 EFI_STATUS Status; 664 EFI_OPEN_FILE *File; 665 EFI_OPEN_FILE FileData; 666 UINTN StrLen; 667 UINTN FileStart; 668 UINTN DevNumber = 0; 669 EFI_OPEN_FILE_GUARD *GuardFile; 670 BOOLEAN VolumeNameMatch; 671 EFI_DEVICE_PATH_PROTOCOL *DevicePath; 672 UINTN Size; 673 EFI_IP_ADDRESS Ip; 674 CHAR8 *CwdPlusPathName; 675 UINTN Index; 676 EFI_SECTION_TYPE ModifiedSectionType; 677 678 EblUpdateDeviceLists (); 679 680 File = &FileData; 681 ZeroMem (File, sizeof (EFI_OPEN_FILE)); 682 683 StrLen = AsciiStrSize (PathName); 684 if (StrLen <= 1) { 685 // Smallest valid path is 1 char and a null 686 return NULL; 687 } 688 689 for (FileStart = 0; FileStart < StrLen; FileStart++) { 690 if (PathName[FileStart] == ':') { 691 FileStart++; 692 break; 693 } 694 } 695 696 // 697 // Matching volume name has precedence over handle based names 698 // 699 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber); 700 if (!VolumeNameMatch) { 701 if (FileStart == StrLen) { 702 // No Volume name or device name, so try Current Working Directory 703 if (gCwd == NULL) { 704 // No CWD 705 return NULL; 706 } 707 708 // We could add a current working directory concept 709 CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName)); 710 if (CwdPlusPathName == NULL) { 711 return NULL; 712 } 713 714 if ((PathName[0] == '/') || (PathName[0] == '\\')) { 715 // PathName starts in / so this means we go to the root of the device in the CWD. 716 CwdPlusPathName[0] = '\0'; 717 for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) { 718 CwdPlusPathName[FileStart] = gCwd[FileStart]; 719 if (gCwd[FileStart] == ':') { 720 FileStart++; 721 CwdPlusPathName[FileStart] = '\0'; 722 break; 723 } 724 } 725 } else { 726 AsciiStrCpy (CwdPlusPathName, gCwd); 727 StrLen = AsciiStrLen (gCwd); 728 if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) { 729 AsciiStrCat (CwdPlusPathName, "\\"); 730 } 731 } 732 733 AsciiStrCat (CwdPlusPathName, PathName); 734 if (AsciiStrStr (CwdPlusPathName, ":") == NULL) { 735 // Extra error check to make sure we don't recurse and blow stack 736 return NULL; 737 } 738 739 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType); 740 FreePool (CwdPlusPathName); 741 return File; 742 } 743 744 DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName); 745 } 746 747 File->DeviceName = AllocatePool (StrLen); 748 AsciiStrCpy (File->DeviceName, PathName); 749 File->DeviceName[FileStart - 1] = '\0'; 750 File->FileName = &File->DeviceName[FileStart]; 751 if (File->FileName[0] == '\0') { 752 // if it is just a file name use / as root 753 File->FileName = "\\"; 754 } 755 756 // 757 // Use best match algorithm on the dev names so we only need to look at the 758 // first few charters to match the full device name. Short name forms are 759 // legal from the caller. 760 // 761 Status = EFI_SUCCESS; 762 if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) { 763 if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) { 764 if (DevNumber >= mFsCount) { 765 goto ErrorExit; 766 } 767 File->Type = EfiOpenFileSystem; 768 File->EfiHandle = mFs[DevNumber]; 769 Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode); 770 771 } else if (PathName[1] == 'v' || PathName[1] == 'V') { 772 if (DevNumber >= mFvCount) { 773 goto ErrorExit; 774 } 775 File->Type = EfiOpenFirmwareVolume; 776 File->EfiHandle = mFv[DevNumber]; 777 778 if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) { 779 // Skip leading / as its not really needed for the FV since no directories are supported 780 FileStart++; 781 } 782 783 // Check for 2nd : 784 ModifiedSectionType = SectionType; 785 for (Index = FileStart; PathName[Index] != '\0'; Index++) { 786 if (PathName[Index] == ':') { 787 // Support fv0:\DxeCore:0x10 788 // This means open the PE32 Section of the file 789 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]); 790 PathName[Index] = '\0'; 791 } 792 } 793 File->FvSectionType = ModifiedSectionType; 794 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType); 795 } 796 } else if ((*PathName == 'A') || (*PathName == 'a')) { 797 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE 798 File->Type = EfiOpenMemoryBuffer; 799 // 1st colon is at PathName[FileStart - 1] 800 File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]); 801 802 // Find 2nd colon 803 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { 804 FileStart++; 805 } 806 807 // If we ran out of string, there's no extra data 808 if (PathName[FileStart] == '\0') { 809 File->Size = 0; 810 } else { 811 File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); 812 } 813 814 // if there's no number after the second colon, default 815 // the end of memory 816 if (File->Size == 0) { 817 File->Size = (UINTN)(0 - (UINTN)File->Buffer); 818 } 819 820 File->MaxPosition = File->Size; 821 File->BaseOffset = (UINTN)File->Buffer; 822 823 } else if (*PathName== 'l' || *PathName == 'L') { 824 if (DevNumber >= mLoadFileCount) { 825 goto ErrorExit; 826 } 827 File->Type = EfiOpenLoadFile; 828 File->EfiHandle = mLoadFile[DevNumber]; 829 830 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile); 831 if (EFI_ERROR (Status)) { 832 goto ErrorExit; 833 } 834 835 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); 836 if (EFI_ERROR (Status)) { 837 goto ErrorExit; 838 } 839 File->DevicePath = DuplicateDevicePath (DevicePath); 840 841 } else if (*PathName == 'b' || *PathName == 'B') { 842 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE 843 if (DevNumber >= mBlkIoCount) { 844 goto ErrorExit; 845 } 846 File->Type = EfiOpenBlockIo; 847 File->EfiHandle = mBlkIo[DevNumber]; 848 EblFileDevicePath (File, "", OpenMode); 849 850 // 1st colon is at PathName[FileStart - 1] 851 File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]); 852 853 // Find 2nd colon 854 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { 855 FileStart++; 856 } 857 858 // If we ran out of string, there's no extra data 859 if (PathName[FileStart] == '\0') { 860 Size = 0; 861 } else { 862 Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); 863 } 864 865 // if a zero size is passed in (or the size is left out entirely), 866 // go to the end of the device. 867 if (Size == 0) { 868 File->Size = File->Size - File->DiskOffset; 869 } else { 870 File->Size = Size; 871 } 872 873 File->MaxPosition = File->Size; 874 File->BaseOffset = File->DiskOffset; 875 } else if ((*PathName) >= '0' && (*PathName <= '9')) { 876 877 // Get current IP address 878 Status = EblGetCurrentIpAddress (&Ip); 879 if (EFI_ERROR(Status)) { 880 AsciiPrint("Device IP Address is not configured.\n"); 881 goto ErrorExit; 882 } 883 884 885 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now... 886 File->Type = EfiOpenTftp; 887 File->IsDirty = FALSE; 888 File->IsBufferValid = FALSE; 889 890 Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp); 891 } 892 893 if (EFI_ERROR (Status)) { 894 goto ErrorExit; 895 } 896 897 GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD)); 898 if (GuardFile == NULL) { 899 goto ErrorExit; 900 } 901 902 GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER; 903 CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE)); 904 GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER; 905 906 return &(GuardFile->File); 907 908 ErrorExit: 909 FreePool (File->DeviceName); 910 return NULL; 911 } 912 913 #define FILE_COPY_CHUNK 0x01000000 914 915 EFI_STATUS 916 EfiCopyFile ( 917 IN CHAR8 *DestinationFile, 918 IN CHAR8 *SourceFile 919 ) 920 { 921 EFI_OPEN_FILE *Source = NULL; 922 EFI_OPEN_FILE *Destination = NULL; 923 EFI_STATUS Status = EFI_SUCCESS; 924 VOID *Buffer = NULL; 925 UINTN Size; 926 UINTN Offset; 927 UINTN Chunk = FILE_COPY_CHUNK; 928 929 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0); 930 if (Source == NULL) { 931 AsciiPrint("Source file open error.\n"); 932 Status = EFI_NOT_FOUND; 933 goto Exit; 934 } 935 936 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); 937 if (Destination == NULL) { 938 AsciiPrint("Destination file open error.\n"); 939 Status = EFI_NOT_FOUND; 940 goto Exit; 941 } 942 943 Buffer = AllocatePool(FILE_COPY_CHUNK); 944 if (Buffer == NULL) { 945 Status = EFI_OUT_OF_RESOURCES; 946 goto Exit; 947 } 948 949 Size = EfiTell(Source, NULL); 950 951 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) { 952 Chunk = FILE_COPY_CHUNK; 953 954 Status = EfiRead(Source, Buffer, &Chunk); 955 if (EFI_ERROR(Status)) { 956 AsciiPrint("Read file error %r\n", Status); 957 goto Exit; 958 } 959 960 Status = EfiWrite(Destination, Buffer, &Chunk); 961 if (EFI_ERROR(Status)) { 962 AsciiPrint("Write file error %r\n", Status); 963 goto Exit; 964 } 965 } 966 967 // Any left over? 968 if (Offset < Size) { 969 Chunk = Size - Offset; 970 971 Status = EfiRead(Source, Buffer, &Chunk); 972 if (EFI_ERROR(Status)) { 973 AsciiPrint("Read file error\n"); 974 goto Exit; 975 } 976 977 Status = EfiWrite(Destination, Buffer, &Chunk); 978 if (EFI_ERROR(Status)) { 979 AsciiPrint("Write file error\n"); 980 goto Exit; 981 } 982 } 983 984 Exit: 985 if (Source != NULL) { 986 Status = EfiClose(Source); 987 if (EFI_ERROR(Status)) { 988 AsciiPrint("Source close error"); 989 } 990 } 991 992 if (Destination != NULL) { 993 Status = EfiClose(Destination); 994 if (EFI_ERROR(Status)) { 995 AsciiPrint("Destination close error"); 996 } 997 } 998 999 if (Buffer != NULL) { 1000 FreePool(Buffer); 1001 } 1002 1003 return Status; 1004 } 1005 1006 /** 1007 Use DeviceType and Index to form a valid PathName and try and open it. 1008 1009 @param DeviceType Device type to open 1010 @param Index Device Index to use. Zero relative. 1011 1012 @return NULL Open failed 1013 @return Valid EFI_OPEN_FILE handle 1014 1015 **/ 1016 EFI_OPEN_FILE * 1017 EfiDeviceOpenByType ( 1018 IN EFI_OPEN_FILE_TYPE DeviceType, 1019 IN UINTN Index 1020 ) 1021 { 1022 CHAR8 *DevStr; 1023 CHAR8 Path[MAX_CMD_LINE]; 1024 1025 switch (DeviceType) { 1026 case EfiOpenLoadFile: 1027 DevStr = "loadfile%d:"; 1028 break; 1029 case EfiOpenFirmwareVolume: 1030 DevStr = "fv%d:"; 1031 break; 1032 case EfiOpenFileSystem: 1033 DevStr = "fs%d:"; 1034 break; 1035 case EfiOpenBlockIo: 1036 DevStr = "blk%d:"; 1037 break; 1038 case EfiOpenMemoryBuffer: 1039 DevStr = "a%d:"; 1040 break; 1041 default: 1042 return NULL; 1043 } 1044 1045 AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index); 1046 1047 return EfiOpen (Path, EFI_FILE_MODE_READ, 0); 1048 } 1049 1050 1051 /** 1052 Close a file handle opened by EfiOpen() and free all resources allocated by 1053 EfiOpen(). 1054 1055 @param Stream Open File Handle 1056 1057 @return EFI_INVALID_PARAMETER Stream is not an Open File 1058 @return EFI_SUCCESS Steam closed 1059 1060 **/ 1061 EFI_STATUS 1062 EfiClose ( 1063 IN EFI_OPEN_FILE *File 1064 ) 1065 { 1066 EFI_STATUS Status; 1067 UINT64 TftpBufferSize; 1068 1069 if (!FileHandleValid (File)) { 1070 return EFI_INVALID_PARAMETER; 1071 } 1072 1073 //Write the buffer contents to TFTP file. 1074 if ((File->Type == EfiOpenTftp) && (File->IsDirty)) { 1075 1076 TftpBufferSize = File->Size; 1077 Status = EblMtftp ( 1078 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, 1079 File->Buffer, 1080 TRUE, 1081 &TftpBufferSize, 1082 NULL, 1083 &File->ServerIp, 1084 (UINT8 *)File->FileName, 1085 NULL, 1086 FALSE 1087 ); 1088 if (EFI_ERROR(Status)) { 1089 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status); 1090 return Status; 1091 } 1092 } 1093 1094 if ((File->Type == EfiOpenLoadFile) || 1095 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) || 1096 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) { 1097 EblFreePool(File->Buffer); 1098 } 1099 1100 EblFreePool (File->DevicePath); 1101 EblFreePool (File->DeviceName); 1102 EblFreePool (File->FsFileInfo); 1103 EblFreePool (File->FsInfo); 1104 1105 if (File->FsFileHandle != NULL) { 1106 File->FsFileHandle->Close (File->FsFileHandle); 1107 } 1108 1109 // Need to free File and it's Guard structures 1110 EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File)); 1111 return EFI_SUCCESS; 1112 } 1113 1114 1115 /** 1116 Return the size of the file represented by Stream. Also return the current 1117 Seek position. Opening a file will enable a valid file size to be returned. 1118 LoadFile is an exception as a load file size is set to zero. 1119 1120 @param Stream Open File Handle 1121 1122 @return 0 Stream is not an Open File or a valid LoadFile handle 1123 1124 **/ 1125 UINTN 1126 EfiTell ( 1127 IN EFI_OPEN_FILE *File, 1128 OUT EFI_LBA *CurrentPosition OPTIONAL 1129 ) 1130 { 1131 EFI_STATUS Status; 1132 UINT64 BufferSize = 0; 1133 1134 if (!FileHandleValid (File)) { 1135 return 0; 1136 } 1137 1138 if (CurrentPosition != NULL) { 1139 *CurrentPosition = File->CurrentPosition; 1140 } 1141 1142 if (File->Type == EfiOpenLoadFile) { 1143 // Figure out the File->Size 1144 File->Buffer = NULL; 1145 File->Size = 0; 1146 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer); 1147 if (Status != EFI_BUFFER_TOO_SMALL) { 1148 return 0; 1149 } 1150 1151 File->MaxPosition = (UINT64)File->Size; 1152 } else if (File->Type == EfiOpenTftp) { 1153 1154 Status = EblMtftp ( 1155 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, 1156 NULL, 1157 FALSE, 1158 &BufferSize, 1159 NULL, 1160 &File->ServerIp, 1161 (UINT8 *)File->FileName, 1162 NULL, 1163 TRUE 1164 ); 1165 if (EFI_ERROR(Status)) { 1166 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status); 1167 return 0; 1168 } 1169 1170 File->Size = (UINTN)BufferSize; 1171 File->MaxPosition = File->Size; 1172 } 1173 1174 return File->Size; 1175 } 1176 1177 1178 /** 1179 Seek to the Offset location in the file. LoadFile and FV device types do 1180 not support EfiSeek(). It is not possible to grow the file size using 1181 EfiSeek(). 1182 1183 SeekType defines how use Offset to calculate the new file position: 1184 EfiSeekStart : Position = Offset 1185 EfiSeekCurrent: Position is Offset bytes from the current position 1186 EfiSeekEnd : Only supported if Offset is zero to seek to end of file. 1187 1188 @param Stream Open File Handle 1189 @param Offset Offset to seek too. 1190 @param SeekType Type of seek to perform 1191 1192 1193 @return EFI_INVALID_PARAMETER Stream is not an Open File 1194 @return EFI_UNSUPPORTED LoadFile and FV do not support Seek 1195 @return EFI_NOT_FOUND Seek past the end of the file. 1196 @return EFI_SUCCESS Steam closed 1197 1198 **/ 1199 EFI_STATUS 1200 EfiSeek ( 1201 IN EFI_OPEN_FILE *File, 1202 IN EFI_LBA Offset, 1203 IN EFI_SEEK_TYPE SeekType 1204 ) 1205 { 1206 EFI_STATUS Status; 1207 UINT64 CurrentPosition; 1208 1209 if (!FileHandleValid (File)) { 1210 return EFI_INVALID_PARAMETER; 1211 } 1212 1213 if (File->Type == EfiOpenLoadFile) { 1214 // LoadFile does not support Seek 1215 return EFI_UNSUPPORTED; 1216 } 1217 1218 CurrentPosition = File->CurrentPosition; 1219 switch (SeekType) { 1220 case EfiSeekStart: 1221 if (Offset > File->MaxPosition) { 1222 return EFI_NOT_FOUND; 1223 } 1224 CurrentPosition = Offset; 1225 break; 1226 1227 case EfiSeekCurrent: 1228 if ((File->CurrentPosition + Offset) > File->MaxPosition) { 1229 return EFI_NOT_FOUND; 1230 } 1231 CurrentPosition += Offset; 1232 break; 1233 1234 case EfiSeekEnd: 1235 if (Offset != 0) { 1236 // We don't support growing file size via seeking past end of file 1237 return EFI_UNSUPPORTED; 1238 } 1239 CurrentPosition = File->MaxPosition; 1240 break; 1241 1242 default: 1243 return EFI_NOT_FOUND; 1244 } 1245 1246 Status = EFI_SUCCESS; 1247 if (File->FsFileHandle != NULL) { 1248 Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition); 1249 } 1250 1251 if (!EFI_ERROR (Status)) { 1252 File->CurrentPosition = CurrentPosition; 1253 } 1254 1255 return Status; 1256 } 1257 1258 EFI_STATUS 1259 CacheTftpFile ( 1260 IN OUT EFI_OPEN_FILE *File 1261 ) 1262 { 1263 EFI_STATUS Status; 1264 UINT64 TftpBufferSize; 1265 1266 if (File->IsBufferValid) { 1267 return EFI_SUCCESS; 1268 } 1269 1270 // Make sure the file size is set. 1271 EfiTell (File, NULL); 1272 1273 //Allocate a buffer to hold the whole file. 1274 File->Buffer = AllocatePool(File->Size); 1275 if (File->Buffer == NULL) { 1276 return EFI_OUT_OF_RESOURCES; 1277 } 1278 1279 TftpBufferSize = File->Size; 1280 1281 Status = EblMtftp ( 1282 EFI_PXE_BASE_CODE_TFTP_READ_FILE, 1283 File->Buffer, 1284 FALSE, 1285 &TftpBufferSize, 1286 NULL, 1287 &File->ServerIp, 1288 (UINT8 *)File->FileName, 1289 NULL, 1290 FALSE); 1291 if (EFI_ERROR(Status)) { 1292 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status); 1293 FreePool(File->Buffer); 1294 return Status; 1295 } 1296 1297 // Set the buffer valid flag. 1298 File->IsBufferValid = TRUE; 1299 1300 return Status; 1301 } 1302 1303 /** 1304 Read BufferSize bytes from the current location in the file. For load file, 1305 FV, and TFTP case you must read the entire file. 1306 1307 @param Stream Open File Handle 1308 @param Buffer Caller allocated buffer. 1309 @param BufferSize Size of buffer in bytes. 1310 1311 1312 @return EFI_SUCCESS Stream is not an Open File 1313 @return EFI_END_OF_FILE Tried to read past the end of the file 1314 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1315 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1316 @return "other" Error returned from device read 1317 1318 **/ 1319 EFI_STATUS 1320 EfiRead ( 1321 IN EFI_OPEN_FILE *File, 1322 OUT VOID *Buffer, 1323 OUT UINTN *BufferSize 1324 ) 1325 { 1326 EFI_STATUS Status; 1327 UINT32 AuthenticationStatus; 1328 EFI_DISK_IO_PROTOCOL *DiskIo; 1329 1330 if (!FileHandleValid (File)) { 1331 return EFI_INVALID_PARAMETER; 1332 } 1333 1334 // Don't read past the end of the file. 1335 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1336 return EFI_END_OF_FILE; 1337 } 1338 1339 switch (File->Type) { 1340 case EfiOpenLoadFile: 1341 // Figure out the File->Size 1342 EfiTell (File, NULL); 1343 1344 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer); 1345 break; 1346 1347 case EfiOpenFirmwareVolume: 1348 if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) { 1349 // This is the entire FV device, so treat like a memory buffer 1350 CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize); 1351 File->CurrentPosition += *BufferSize; 1352 Status = EFI_SUCCESS; 1353 } else { 1354 if (File->Buffer == NULL) { 1355 if (File->FvSectionType == EFI_SECTION_ALL) { 1356 Status = File->Fv->ReadFile ( 1357 File->Fv, 1358 &File->FvNameGuid, 1359 (VOID **)&File->Buffer, 1360 &File->Size, 1361 &File->FvType, 1362 &File->FvAttributes, 1363 &AuthenticationStatus 1364 ); 1365 } else { 1366 Status = File->Fv->ReadSection ( 1367 File->Fv, 1368 &File->FvNameGuid, 1369 File->FvSectionType, 1370 0, 1371 (VOID **)&File->Buffer, 1372 &File->Size, 1373 &AuthenticationStatus 1374 ); 1375 } 1376 if (EFI_ERROR (Status)) { 1377 return Status; 1378 } 1379 File->IsBufferValid = TRUE; 1380 } 1381 // Operate on the cached buffer so Seek will work 1382 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1383 File->CurrentPosition += *BufferSize; 1384 Status = EFI_SUCCESS; 1385 } 1386 break; 1387 1388 case EfiOpenMemoryBuffer: 1389 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1390 File->CurrentPosition += *BufferSize; 1391 Status = EFI_SUCCESS; 1392 break; 1393 1394 case EfiOpenFileSystem: 1395 Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer); 1396 File->CurrentPosition += *BufferSize; 1397 break; 1398 1399 case EfiOpenBlockIo: 1400 Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo); 1401 if (!EFI_ERROR(Status)) { 1402 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer); 1403 } 1404 File->CurrentPosition += *BufferSize; 1405 break; 1406 1407 case EfiOpenTftp: 1408 // Cache the file if it hasn't been cached yet. 1409 if (File->IsBufferValid == FALSE) { 1410 Status = CacheTftpFile (File); 1411 if (EFI_ERROR (Status)) { 1412 return Status; 1413 } 1414 } 1415 1416 // Copy out the requested data 1417 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1418 File->CurrentPosition += *BufferSize; 1419 1420 Status = EFI_SUCCESS; 1421 break; 1422 1423 default: 1424 return EFI_INVALID_PARAMETER; 1425 }; 1426 1427 return Status; 1428 } 1429 1430 1431 /** 1432 Read the entire file into a buffer. This routine allocates the buffer and 1433 returns it to the user full of the read data. 1434 1435 This is very useful for load file where it's hard to know how big the buffer 1436 must be. 1437 1438 @param Stream Open File Handle 1439 @param Buffer Pointer to buffer to return. 1440 @param BufferSize Pointer to Size of buffer return.. 1441 1442 1443 @return EFI_SUCCESS Stream is not an Open File 1444 @return EFI_END_OF_FILE Tried to read past the end of the file 1445 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1446 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1447 @return "other" Error returned from device read 1448 1449 **/ 1450 EFI_STATUS 1451 EfiReadAllocatePool ( 1452 IN EFI_OPEN_FILE *File, 1453 OUT VOID **Buffer, 1454 OUT UINTN *BufferSize 1455 ) 1456 { 1457 if (!FileHandleValid (File)) { 1458 return EFI_INVALID_PARAMETER; 1459 } 1460 1461 // Loadfile defers file size determination on Open so use tell to find it 1462 EfiTell (File, NULL); 1463 1464 *BufferSize = File->Size; 1465 *Buffer = AllocatePool (*BufferSize); 1466 if (*Buffer == NULL) { 1467 return EFI_NOT_FOUND; 1468 } 1469 1470 return EfiRead (File, *Buffer, BufferSize); 1471 } 1472 1473 1474 /** 1475 Write data back to the file. For TFTP case you must write the entire file. 1476 1477 @param Stream Open File Handle 1478 @param Buffer Pointer to buffer to return. 1479 @param BufferSize Pointer to Size of buffer return.. 1480 1481 1482 @return EFI_SUCCESS Stream is not an Open File 1483 @return EFI_END_OF_FILE Tried to read past the end of the file 1484 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1485 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1486 @return "other" Error returned from device write 1487 1488 **/ 1489 EFI_STATUS 1490 EfiWrite ( 1491 IN EFI_OPEN_FILE *File, 1492 OUT VOID *Buffer, 1493 OUT UINTN *BufferSize 1494 ) 1495 { 1496 EFI_STATUS Status; 1497 EFI_FV_WRITE_FILE_DATA FileData; 1498 EFI_DISK_IO_PROTOCOL *DiskIo; 1499 1500 if (!FileHandleValid (File)) { 1501 return EFI_INVALID_PARAMETER; 1502 } 1503 1504 switch (File->Type) { 1505 case EfiOpenMemoryBuffer: 1506 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1507 return EFI_END_OF_FILE; 1508 } 1509 1510 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize); 1511 File->CurrentPosition += *BufferSize; 1512 Status = EFI_SUCCESS; 1513 1514 case EfiOpenLoadFile: 1515 // LoadFile device is read only be definition 1516 Status = EFI_UNSUPPORTED; 1517 1518 case EfiOpenFirmwareVolume: 1519 if (File->FvSectionType != EFI_SECTION_ALL) { 1520 // Writes not support to a specific section. You have to update entire file 1521 return EFI_UNSUPPORTED; 1522 } 1523 1524 FileData.NameGuid = &(File->FvNameGuid); 1525 FileData.Type = File->FvType; 1526 FileData.FileAttributes = File->FvAttributes; 1527 FileData.Buffer = Buffer; 1528 FileData.BufferSize = (UINT32)*BufferSize; 1529 Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData); 1530 break; 1531 1532 case EfiOpenFileSystem: 1533 Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer); 1534 File->CurrentPosition += *BufferSize; 1535 break; 1536 1537 case EfiOpenBlockIo: 1538 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1539 return EFI_END_OF_FILE; 1540 } 1541 1542 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo); 1543 if (!EFI_ERROR(Status)) { 1544 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer); 1545 } 1546 File->CurrentPosition += *BufferSize; 1547 break; 1548 1549 case EfiOpenTftp: 1550 // Cache the file if it hasn't been cached yet. 1551 if (File->IsBufferValid == FALSE) { 1552 Status = CacheTftpFile(File); 1553 if (EFI_ERROR(Status)) { 1554 return Status; 1555 } 1556 } 1557 1558 // Don't overwrite the buffer 1559 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1560 UINT8 *TempBuffer; 1561 1562 TempBuffer = File->Buffer; 1563 1564 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize)); 1565 if (File->Buffer == NULL) { 1566 return EFI_OUT_OF_RESOURCES; 1567 } 1568 1569 CopyMem (File->Buffer, TempBuffer, File->Size); 1570 1571 FreePool (TempBuffer); 1572 1573 File->Size = (UINTN)(File->CurrentPosition + *BufferSize); 1574 File->MaxPosition = (UINT64)File->Size; 1575 } 1576 1577 // Copy in the requested data 1578 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize); 1579 File->CurrentPosition += *BufferSize; 1580 1581 // Mark the file dirty 1582 File->IsDirty = TRUE; 1583 1584 Status = EFI_SUCCESS; 1585 break; 1586 1587 default: 1588 Status = EFI_INVALID_PARAMETER; 1589 }; 1590 1591 return Status; 1592 } 1593 1594 1595 /** 1596 Given Cwd expand Path to remove .. and replace them with real 1597 directory names. 1598 1599 @param Cwd Current Working Directory 1600 @param Path Path to expand 1601 1602 @return NULL Cwd or Path are not valid 1603 @return 'other' Path with .. expanded 1604 1605 **/ 1606 CHAR8 * 1607 ExpandPath ( 1608 IN CHAR8 *Cwd, 1609 IN CHAR8 *Path 1610 ) 1611 { 1612 CHAR8 *NewPath; 1613 CHAR8 *Work, *Start, *End; 1614 UINTN StrLen; 1615 INTN i; 1616 1617 if (Cwd == NULL || Path == NULL) { 1618 return NULL; 1619 } 1620 1621 StrLen = AsciiStrSize (Cwd); 1622 if (StrLen <= 2) { 1623 // Smallest valid path is 1 char and a null 1624 return NULL; 1625 } 1626 1627 StrLen = AsciiStrSize (Path); 1628 NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1); 1629 if (NewPath == NULL) { 1630 return NULL; 1631 } 1632 AsciiStrCpy (NewPath, Cwd); 1633 1634 End = Path + StrLen; 1635 for (Start = Path ;;) { 1636 Work = AsciiStrStr (Start, "..") ; 1637 if (Work == NULL) { 1638 // Remaining part of Path contains no more .. 1639 break; 1640 } 1641 1642 // append path prior to .. 1643 AsciiStrnCat (NewPath, Start, Work - Start); 1644 StrLen = AsciiStrLen (NewPath); 1645 for (i = StrLen; i >= 0; i--) { 1646 if (NewPath[i] == ':') { 1647 // too many .. 1648 return NULL; 1649 } 1650 if (NewPath[i] == '/' || NewPath[i] == '\\') { 1651 if ((i > 0) && (NewPath[i-1] == ':')) { 1652 // leave the / before a : 1653 NewPath[i+1] = '\0'; 1654 } else { 1655 // replace / will Null to remove trailing file/dir reference 1656 NewPath[i] = '\0'; 1657 } 1658 break; 1659 } 1660 } 1661 1662 Start = Work + 3; 1663 } 1664 1665 // Handle the path that remains after the .. 1666 AsciiStrnCat (NewPath, Start, End - Start); 1667 1668 return NewPath; 1669 } 1670 1671 1672 /** 1673 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and 1674 the path does not contain a device name, The CWD is prepended to the path. 1675 1676 @param Cwd Current Working Directory to set 1677 1678 1679 @return EFI_SUCCESS CWD is set 1680 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path 1681 1682 **/ 1683 EFI_STATUS 1684 EfiSetCwd ( 1685 IN CHAR8 *Cwd 1686 ) 1687 { 1688 EFI_OPEN_FILE *File; 1689 UINTN Len; 1690 CHAR8 *Path; 1691 1692 if (Cwd == NULL) { 1693 return EFI_INVALID_PARAMETER; 1694 } 1695 1696 if (AsciiStrCmp (Cwd, ".") == 0) { 1697 // cd . is a no-op 1698 return EFI_SUCCESS; 1699 } 1700 1701 Path = Cwd; 1702 if (AsciiStrStr (Cwd, "..") != NULL) { 1703 if (gCwd == NULL) { 1704 // no parent 1705 return EFI_SUCCESS; 1706 } 1707 1708 Len = AsciiStrLen (gCwd); 1709 if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) { 1710 // parent is device so nothing to do 1711 return EFI_SUCCESS; 1712 } 1713 1714 // Expand .. in Cwd, given we know current working directory 1715 Path = ExpandPath (gCwd, Cwd); 1716 if (Path == NULL) { 1717 return EFI_NOT_FOUND; 1718 } 1719 } 1720 1721 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0); 1722 if (File == NULL) { 1723 return EFI_INVALID_PARAMETER; 1724 } 1725 1726 if (gCwd != NULL) { 1727 FreePool (gCwd); 1728 } 1729 1730 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be 1731 // relative to the current gCwd or not. 1732 gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10); 1733 if (gCwd == NULL) { 1734 return EFI_INVALID_PARAMETER; 1735 } 1736 1737 AsciiStrCpy (gCwd, File->DeviceName); 1738 if (File->FileName == NULL) { 1739 AsciiStrCat (gCwd, ":\\"); 1740 } else { 1741 AsciiStrCat (gCwd, ":"); 1742 AsciiStrCat (gCwd, File->FileName); 1743 } 1744 1745 1746 EfiClose (File); 1747 if (Path != Cwd) { 1748 FreePool (Path); 1749 } 1750 return EFI_SUCCESS; 1751 } 1752 1753 1754 /** 1755 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and 1756 the path does not contain a device name, The CWD is prepended to the path. 1757 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After 1758 a call to EfiSetCwd() it is not legal to use the pointer returned by 1759 this function. 1760 1761 @param Cwd Current Working Directory 1762 1763 1764 @return "" No CWD set 1765 @return 'other' Returns buffer that contains CWD. 1766 1767 **/ 1768 CHAR8 * 1769 EfiGetCwd ( 1770 VOID 1771 ) 1772 { 1773 if (gCwd == NULL) { 1774 return ""; 1775 } 1776 return gCwd; 1777 } 1778 1779 1780