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 AsciiStrToUnicodeStrS (FileName, UnicodeFileName, 388 ARRAY_SIZE (UnicodeFileName)); 389 } else { 390 AsciiStrToUnicodeStrS ("\\", UnicodeFileName, ARRAY_SIZE (UnicodeFileName)); 391 } 392 393 Size = StrSize (UnicodeFileName); 394 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL)); 395 if (FileDevicePath != NULL) { 396 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath; 397 FilePath->Header.Type = MEDIA_DEVICE_PATH; 398 FilePath->Header.SubType = MEDIA_FILEPATH_DP; 399 CopyMem (&FilePath->PathName, UnicodeFileName, Size); 400 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH); 401 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header)); 402 403 if (File->EfiHandle != NULL) { 404 File->DevicePath = DevicePathFromHandle (File->EfiHandle); 405 } 406 407 File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath); 408 FreePool (FileDevicePath); 409 } 410 411 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo); 412 if (!EFI_ERROR (Status)) { 413 File->FsBlockIoMedia = BlkIo->Media; 414 File->FsBlockIo = BlkIo; 415 416 // If we are not opening the device this will get over written with file info 417 File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize); 418 } 419 420 if (File->Type == EfiOpenFileSystem) { 421 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs); 422 if (!EFI_ERROR (Status)) { 423 Status = Fs->OpenVolume (Fs, &Root); 424 if (!EFI_ERROR (Status)) { 425 // Get information about the volume 426 Size = 0; 427 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo); 428 if (Status == EFI_BUFFER_TOO_SMALL) { 429 File->FsInfo = AllocatePool (Size); 430 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo); 431 } 432 433 // Get information about the file 434 Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0); 435 if (!EFI_ERROR (Status)) { 436 Size = 0; 437 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL); 438 if (Status == EFI_BUFFER_TOO_SMALL) { 439 File->FsFileInfo = AllocatePool (Size); 440 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo); 441 if (!EFI_ERROR (Status)) { 442 File->Size = (UINTN)File->FsFileInfo->FileSize; 443 File->MaxPosition = (UINT64)File->Size; 444 } 445 } 446 } 447 448 Root->Close (Root); 449 } 450 } 451 } else if (File->Type == EfiOpenBlockIo) { 452 File->Size = (UINTN)File->MaxPosition; 453 } 454 455 return Status; 456 } 457 458 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a)) 459 460 EFI_STATUS 461 CompareGuidToString ( 462 IN EFI_GUID *Guid, 463 IN CHAR8 *String 464 ) 465 { 466 CHAR8 AsciiGuid[64]; 467 CHAR8 *StringPtr; 468 CHAR8 *GuidPtr; 469 470 AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid); 471 472 StringPtr = String; 473 GuidPtr = AsciiGuid; 474 475 while ((*StringPtr != '\0') && (*GuidPtr != '\0')) { 476 // Skip dashes 477 if (*StringPtr == '-') { 478 StringPtr++; 479 continue; 480 } 481 482 if (*GuidPtr == '-') { 483 GuidPtr++; 484 continue; 485 } 486 487 if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) { 488 return EFI_NOT_FOUND; 489 } 490 491 StringPtr++; 492 GuidPtr++; 493 } 494 495 return EFI_SUCCESS; 496 } 497 498 499 /** 500 Internal work function to fill in EFI_OPEN_FILE information for the FV 501 502 @param File Open file handle 503 @param FileName Name of file after device stripped off 504 505 506 **/ 507 EFI_STATUS 508 EblFvFileDevicePath ( 509 IN OUT EFI_OPEN_FILE *File, 510 IN CHAR8 *FileName, 511 IN CONST UINT64 OpenMode 512 ) 513 { 514 EFI_STATUS Status; 515 EFI_STATUS GetNextFileStatus; 516 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode; 517 EFI_DEVICE_PATH_PROTOCOL *DevicePath; 518 UINTN Key; 519 UINT32 AuthenticationStatus; 520 CHAR8 AsciiSection[MAX_PATHNAME]; 521 VOID *Section; 522 UINTN SectionSize; 523 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; 524 EFI_LBA Lba; 525 UINTN BlockSize; 526 UINTN NumberOfBlocks; 527 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL; 528 UINTN Index; 529 530 531 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv); 532 if (EFI_ERROR (Status)) { 533 return Status; 534 } 535 536 // Get FVB Info about the handle 537 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb); 538 if (!EFI_ERROR (Status)) { 539 Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart); 540 if (!EFI_ERROR (Status)) { 541 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart; 542 File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER); 543 for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) { 544 File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY); 545 } 546 547 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) { 548 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks); 549 if (EFI_ERROR (Status)) { 550 break; 551 } 552 } 553 } 554 } 555 556 557 DevicePath = DevicePathFromHandle (File->EfiHandle); 558 559 if (*FileName == '\0') { 560 File->DevicePath = DuplicateDevicePath (DevicePath); 561 File->Size = File->FvSize; 562 File->MaxPosition = File->Size; 563 } else { 564 Key = 0; 565 do { 566 File->FvType = EFI_FV_FILETYPE_ALL; 567 GetNextFileStatus = File->Fv->GetNextFile ( 568 File->Fv, 569 &Key, 570 &File->FvType, 571 &File->FvNameGuid, 572 &File->FvAttributes, 573 &File->Size 574 ); 575 if (!EFI_ERROR (GetNextFileStatus)) { 576 // Compare GUID first 577 Status = CompareGuidToString (&File->FvNameGuid, FileName); 578 if (!EFI_ERROR(Status)) { 579 break; 580 } 581 582 Section = NULL; 583 Status = File->Fv->ReadSection ( 584 File->Fv, 585 &File->FvNameGuid, 586 EFI_SECTION_USER_INTERFACE, 587 0, 588 &Section, 589 &SectionSize, 590 &AuthenticationStatus 591 ); 592 if (!EFI_ERROR (Status)) { 593 UnicodeStrToAsciiStrS (Section, AsciiSection, MAX_PATHNAME); 594 if (AsciiStriCmp (FileName, AsciiSection) == 0) { 595 FreePool (Section); 596 break; 597 } 598 FreePool (Section); 599 } 600 } 601 } while (!EFI_ERROR (GetNextFileStatus)); 602 603 if (EFI_ERROR (GetNextFileStatus)) { 604 return GetNextFileStatus; 605 } 606 607 if (OpenMode != EFI_SECTION_ALL) { 608 // Calculate the size of the section we are targeting 609 Section = NULL; 610 File->Size = 0; 611 Status = File->Fv->ReadSection ( 612 File->Fv, 613 &File->FvNameGuid, 614 (EFI_SECTION_TYPE)OpenMode, 615 0, 616 &Section, 617 &File->Size, 618 &AuthenticationStatus 619 ); 620 if (EFI_ERROR (Status)) { 621 return Status; 622 } 623 } 624 625 File->MaxPosition = File->Size; 626 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid); 627 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode); 628 } 629 630 631 // FVB not required if FV was soft loaded... 632 return EFI_SUCCESS; 633 } 634 635 636 637 638 /** 639 Open a device named by PathName. The PathName includes a device name and 640 path separated by a :. See file header for more details on the PathName 641 syntax. There is no checking to prevent a file from being opened more than 642 one type. 643 644 SectionType is only used to open an FV. Each file in an FV contains multiple 645 sections and only the SectionType section is opened. 646 647 For any file that is opened with EfiOpen() must be closed with EfiClose(). 648 649 @param PathName Path to parse to open 650 @param OpenMode Same as EFI_FILE.Open() 651 @param SectionType Section in FV to open. 652 653 @return NULL Open failed 654 @return Valid EFI_OPEN_FILE handle 655 656 **/ 657 EFI_OPEN_FILE * 658 EfiOpen ( 659 IN CHAR8 *PathName, 660 IN CONST UINT64 OpenMode, 661 IN CONST EFI_SECTION_TYPE SectionType 662 ) 663 { 664 EFI_STATUS Status; 665 EFI_OPEN_FILE *File; 666 EFI_OPEN_FILE FileData; 667 UINTN StrLen; 668 UINTN FileStart; 669 UINTN DevNumber = 0; 670 EFI_OPEN_FILE_GUARD *GuardFile; 671 BOOLEAN VolumeNameMatch; 672 EFI_DEVICE_PATH_PROTOCOL *DevicePath; 673 UINTN Size; 674 EFI_IP_ADDRESS Ip; 675 CHAR8 *CwdPlusPathName; 676 UINTN Index; 677 EFI_SECTION_TYPE ModifiedSectionType; 678 UINTN AsciiLength; 679 680 EblUpdateDeviceLists (); 681 682 File = &FileData; 683 ZeroMem (File, sizeof (EFI_OPEN_FILE)); 684 685 StrLen = AsciiStrSize (PathName); 686 if (StrLen <= 1) { 687 // Smallest valid path is 1 char and a null 688 return NULL; 689 } 690 691 for (FileStart = 0; FileStart < StrLen; FileStart++) { 692 if (PathName[FileStart] == ':') { 693 FileStart++; 694 break; 695 } 696 } 697 698 // 699 // Matching volume name has precedence over handle based names 700 // 701 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber); 702 if (!VolumeNameMatch) { 703 if (FileStart == StrLen) { 704 // No Volume name or device name, so try Current Working Directory 705 if (gCwd == NULL) { 706 // No CWD 707 return NULL; 708 } 709 710 // We could add a current working directory concept 711 AsciiLength = AsciiStrSize (gCwd) + AsciiStrSize (PathName); 712 CwdPlusPathName = AllocatePool (AsciiLength); 713 if (CwdPlusPathName == NULL) { 714 return NULL; 715 } 716 717 if ((PathName[0] == '/') || (PathName[0] == '\\')) { 718 // PathName starts in / so this means we go to the root of the device in the CWD. 719 CwdPlusPathName[0] = '\0'; 720 for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) { 721 CwdPlusPathName[FileStart] = gCwd[FileStart]; 722 if (gCwd[FileStart] == ':') { 723 FileStart++; 724 CwdPlusPathName[FileStart] = '\0'; 725 break; 726 } 727 } 728 } else { 729 AsciiStrCpyS (CwdPlusPathName, AsciiLength, gCwd); 730 StrLen = AsciiStrLen (gCwd); 731 if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) { 732 AsciiStrCatS (CwdPlusPathName, AsciiLength, "\\"); 733 } 734 } 735 736 AsciiStrCatS (CwdPlusPathName, AsciiLength, PathName); 737 if (AsciiStrStr (CwdPlusPathName, ":") == NULL) { 738 // Extra error check to make sure we don't recurse and blow stack 739 return NULL; 740 } 741 742 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType); 743 FreePool (CwdPlusPathName); 744 return File; 745 } 746 747 DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName); 748 } 749 750 File->DeviceName = AllocatePool (StrLen); 751 AsciiStrCpyS (File->DeviceName, StrLen, PathName); 752 File->DeviceName[FileStart - 1] = '\0'; 753 File->FileName = &File->DeviceName[FileStart]; 754 if (File->FileName[0] == '\0') { 755 // if it is just a file name use / as root 756 File->FileName = "\\"; 757 } 758 759 // 760 // Use best match algorithm on the dev names so we only need to look at the 761 // first few charters to match the full device name. Short name forms are 762 // legal from the caller. 763 // 764 Status = EFI_SUCCESS; 765 if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) { 766 if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) { 767 if (DevNumber >= mFsCount) { 768 goto ErrorExit; 769 } 770 File->Type = EfiOpenFileSystem; 771 File->EfiHandle = mFs[DevNumber]; 772 Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode); 773 774 } else if (PathName[1] == 'v' || PathName[1] == 'V') { 775 if (DevNumber >= mFvCount) { 776 goto ErrorExit; 777 } 778 File->Type = EfiOpenFirmwareVolume; 779 File->EfiHandle = mFv[DevNumber]; 780 781 if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) { 782 // Skip leading / as its not really needed for the FV since no directories are supported 783 FileStart++; 784 } 785 786 // Check for 2nd : 787 ModifiedSectionType = SectionType; 788 for (Index = FileStart; PathName[Index] != '\0'; Index++) { 789 if (PathName[Index] == ':') { 790 // Support fv0:\DxeCore:0x10 791 // This means open the PE32 Section of the file 792 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]); 793 PathName[Index] = '\0'; 794 } 795 } 796 File->FvSectionType = ModifiedSectionType; 797 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType); 798 } 799 } else if ((*PathName == 'A') || (*PathName == 'a')) { 800 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE 801 File->Type = EfiOpenMemoryBuffer; 802 // 1st colon is at PathName[FileStart - 1] 803 File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]); 804 805 // Find 2nd colon 806 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { 807 FileStart++; 808 } 809 810 // If we ran out of string, there's no extra data 811 if (PathName[FileStart] == '\0') { 812 File->Size = 0; 813 } else { 814 File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); 815 } 816 817 // if there's no number after the second colon, default 818 // the end of memory 819 if (File->Size == 0) { 820 File->Size = (UINTN)(0 - (UINTN)File->Buffer); 821 } 822 823 File->MaxPosition = File->Size; 824 File->BaseOffset = (UINTN)File->Buffer; 825 826 } else if (*PathName== 'l' || *PathName == 'L') { 827 if (DevNumber >= mLoadFileCount) { 828 goto ErrorExit; 829 } 830 File->Type = EfiOpenLoadFile; 831 File->EfiHandle = mLoadFile[DevNumber]; 832 833 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile); 834 if (EFI_ERROR (Status)) { 835 goto ErrorExit; 836 } 837 838 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); 839 if (EFI_ERROR (Status)) { 840 goto ErrorExit; 841 } 842 File->DevicePath = DuplicateDevicePath (DevicePath); 843 844 } else if (*PathName == 'b' || *PathName == 'B') { 845 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE 846 if (DevNumber >= mBlkIoCount) { 847 goto ErrorExit; 848 } 849 File->Type = EfiOpenBlockIo; 850 File->EfiHandle = mBlkIo[DevNumber]; 851 EblFileDevicePath (File, "", OpenMode); 852 853 // 1st colon is at PathName[FileStart - 1] 854 File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]); 855 856 // Find 2nd colon 857 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { 858 FileStart++; 859 } 860 861 // If we ran out of string, there's no extra data 862 if (PathName[FileStart] == '\0') { 863 Size = 0; 864 } else { 865 Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); 866 } 867 868 // if a zero size is passed in (or the size is left out entirely), 869 // go to the end of the device. 870 if (Size == 0) { 871 File->Size = File->Size - File->DiskOffset; 872 } else { 873 File->Size = Size; 874 } 875 876 File->MaxPosition = File->Size; 877 File->BaseOffset = File->DiskOffset; 878 } else if ((*PathName) >= '0' && (*PathName <= '9')) { 879 880 // Get current IP address 881 Status = EblGetCurrentIpAddress (&Ip); 882 if (EFI_ERROR(Status)) { 883 AsciiPrint("Device IP Address is not configured.\n"); 884 goto ErrorExit; 885 } 886 887 888 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now... 889 File->Type = EfiOpenTftp; 890 File->IsDirty = FALSE; 891 File->IsBufferValid = FALSE; 892 893 Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp); 894 } 895 896 if (EFI_ERROR (Status)) { 897 goto ErrorExit; 898 } 899 900 GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD)); 901 if (GuardFile == NULL) { 902 goto ErrorExit; 903 } 904 905 GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER; 906 CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE)); 907 GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER; 908 909 return &(GuardFile->File); 910 911 ErrorExit: 912 FreePool (File->DeviceName); 913 return NULL; 914 } 915 916 #define FILE_COPY_CHUNK 0x01000000 917 918 EFI_STATUS 919 EfiCopyFile ( 920 IN CHAR8 *DestinationFile, 921 IN CHAR8 *SourceFile 922 ) 923 { 924 EFI_OPEN_FILE *Source = NULL; 925 EFI_OPEN_FILE *Destination = NULL; 926 EFI_STATUS Status = EFI_SUCCESS; 927 VOID *Buffer = NULL; 928 UINTN Size; 929 UINTN Offset; 930 UINTN Chunk = FILE_COPY_CHUNK; 931 932 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0); 933 if (Source == NULL) { 934 AsciiPrint("Source file open error.\n"); 935 Status = EFI_NOT_FOUND; 936 goto Exit; 937 } 938 939 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); 940 if (Destination == NULL) { 941 AsciiPrint("Destination file open error.\n"); 942 Status = EFI_NOT_FOUND; 943 goto Exit; 944 } 945 946 Buffer = AllocatePool(FILE_COPY_CHUNK); 947 if (Buffer == NULL) { 948 Status = EFI_OUT_OF_RESOURCES; 949 goto Exit; 950 } 951 952 Size = EfiTell(Source, NULL); 953 954 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) { 955 Chunk = FILE_COPY_CHUNK; 956 957 Status = EfiRead(Source, Buffer, &Chunk); 958 if (EFI_ERROR(Status)) { 959 AsciiPrint("Read file error %r\n", Status); 960 goto Exit; 961 } 962 963 Status = EfiWrite(Destination, Buffer, &Chunk); 964 if (EFI_ERROR(Status)) { 965 AsciiPrint("Write file error %r\n", Status); 966 goto Exit; 967 } 968 } 969 970 // Any left over? 971 if (Offset < Size) { 972 Chunk = Size - Offset; 973 974 Status = EfiRead(Source, Buffer, &Chunk); 975 if (EFI_ERROR(Status)) { 976 AsciiPrint("Read file error\n"); 977 goto Exit; 978 } 979 980 Status = EfiWrite(Destination, Buffer, &Chunk); 981 if (EFI_ERROR(Status)) { 982 AsciiPrint("Write file error\n"); 983 goto Exit; 984 } 985 } 986 987 Exit: 988 if (Source != NULL) { 989 Status = EfiClose(Source); 990 if (EFI_ERROR(Status)) { 991 AsciiPrint("Source close error"); 992 } 993 } 994 995 if (Destination != NULL) { 996 Status = EfiClose(Destination); 997 if (EFI_ERROR(Status)) { 998 AsciiPrint("Destination close error"); 999 } 1000 } 1001 1002 if (Buffer != NULL) { 1003 FreePool(Buffer); 1004 } 1005 1006 return Status; 1007 } 1008 1009 /** 1010 Use DeviceType and Index to form a valid PathName and try and open it. 1011 1012 @param DeviceType Device type to open 1013 @param Index Device Index to use. Zero relative. 1014 1015 @return NULL Open failed 1016 @return Valid EFI_OPEN_FILE handle 1017 1018 **/ 1019 EFI_OPEN_FILE * 1020 EfiDeviceOpenByType ( 1021 IN EFI_OPEN_FILE_TYPE DeviceType, 1022 IN UINTN Index 1023 ) 1024 { 1025 CHAR8 *DevStr; 1026 CHAR8 Path[MAX_CMD_LINE]; 1027 1028 switch (DeviceType) { 1029 case EfiOpenLoadFile: 1030 DevStr = "loadfile%d:"; 1031 break; 1032 case EfiOpenFirmwareVolume: 1033 DevStr = "fv%d:"; 1034 break; 1035 case EfiOpenFileSystem: 1036 DevStr = "fs%d:"; 1037 break; 1038 case EfiOpenBlockIo: 1039 DevStr = "blk%d:"; 1040 break; 1041 case EfiOpenMemoryBuffer: 1042 DevStr = "a%d:"; 1043 break; 1044 default: 1045 return NULL; 1046 } 1047 1048 AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index); 1049 1050 return EfiOpen (Path, EFI_FILE_MODE_READ, 0); 1051 } 1052 1053 1054 /** 1055 Close a file handle opened by EfiOpen() and free all resources allocated by 1056 EfiOpen(). 1057 1058 @param Stream Open File Handle 1059 1060 @return EFI_INVALID_PARAMETER Stream is not an Open File 1061 @return EFI_SUCCESS Steam closed 1062 1063 **/ 1064 EFI_STATUS 1065 EfiClose ( 1066 IN EFI_OPEN_FILE *File 1067 ) 1068 { 1069 EFI_STATUS Status; 1070 UINT64 TftpBufferSize; 1071 1072 if (!FileHandleValid (File)) { 1073 return EFI_INVALID_PARAMETER; 1074 } 1075 1076 //Write the buffer contents to TFTP file. 1077 if ((File->Type == EfiOpenTftp) && (File->IsDirty)) { 1078 1079 TftpBufferSize = File->Size; 1080 Status = EblMtftp ( 1081 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, 1082 File->Buffer, 1083 TRUE, 1084 &TftpBufferSize, 1085 NULL, 1086 &File->ServerIp, 1087 (UINT8 *)File->FileName, 1088 NULL, 1089 FALSE 1090 ); 1091 if (EFI_ERROR(Status)) { 1092 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status); 1093 return Status; 1094 } 1095 } 1096 1097 if ((File->Type == EfiOpenLoadFile) || 1098 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) || 1099 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) { 1100 EblFreePool(File->Buffer); 1101 } 1102 1103 EblFreePool (File->DevicePath); 1104 EblFreePool (File->DeviceName); 1105 EblFreePool (File->FsFileInfo); 1106 EblFreePool (File->FsInfo); 1107 1108 if (File->FsFileHandle != NULL) { 1109 File->FsFileHandle->Close (File->FsFileHandle); 1110 } 1111 1112 // Need to free File and it's Guard structures 1113 EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File)); 1114 return EFI_SUCCESS; 1115 } 1116 1117 1118 /** 1119 Return the size of the file represented by Stream. Also return the current 1120 Seek position. Opening a file will enable a valid file size to be returned. 1121 LoadFile is an exception as a load file size is set to zero. 1122 1123 @param Stream Open File Handle 1124 1125 @return 0 Stream is not an Open File or a valid LoadFile handle 1126 1127 **/ 1128 UINTN 1129 EfiTell ( 1130 IN EFI_OPEN_FILE *File, 1131 OUT EFI_LBA *CurrentPosition OPTIONAL 1132 ) 1133 { 1134 EFI_STATUS Status; 1135 UINT64 BufferSize = 0; 1136 1137 if (!FileHandleValid (File)) { 1138 return 0; 1139 } 1140 1141 if (CurrentPosition != NULL) { 1142 *CurrentPosition = File->CurrentPosition; 1143 } 1144 1145 if (File->Type == EfiOpenLoadFile) { 1146 // Figure out the File->Size 1147 File->Buffer = NULL; 1148 File->Size = 0; 1149 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer); 1150 if (Status != EFI_BUFFER_TOO_SMALL) { 1151 return 0; 1152 } 1153 1154 File->MaxPosition = (UINT64)File->Size; 1155 } else if (File->Type == EfiOpenTftp) { 1156 1157 Status = EblMtftp ( 1158 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, 1159 NULL, 1160 FALSE, 1161 &BufferSize, 1162 NULL, 1163 &File->ServerIp, 1164 (UINT8 *)File->FileName, 1165 NULL, 1166 TRUE 1167 ); 1168 if (EFI_ERROR(Status)) { 1169 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status); 1170 return 0; 1171 } 1172 1173 File->Size = (UINTN)BufferSize; 1174 File->MaxPosition = File->Size; 1175 } 1176 1177 return File->Size; 1178 } 1179 1180 1181 /** 1182 Seek to the Offset location in the file. LoadFile and FV device types do 1183 not support EfiSeek(). It is not possible to grow the file size using 1184 EfiSeek(). 1185 1186 SeekType defines how use Offset to calculate the new file position: 1187 EfiSeekStart : Position = Offset 1188 EfiSeekCurrent: Position is Offset bytes from the current position 1189 EfiSeekEnd : Only supported if Offset is zero to seek to end of file. 1190 1191 @param Stream Open File Handle 1192 @param Offset Offset to seek too. 1193 @param SeekType Type of seek to perform 1194 1195 1196 @return EFI_INVALID_PARAMETER Stream is not an Open File 1197 @return EFI_UNSUPPORTED LoadFile and FV do not support Seek 1198 @return EFI_NOT_FOUND Seek past the end of the file. 1199 @return EFI_SUCCESS Steam closed 1200 1201 **/ 1202 EFI_STATUS 1203 EfiSeek ( 1204 IN EFI_OPEN_FILE *File, 1205 IN EFI_LBA Offset, 1206 IN EFI_SEEK_TYPE SeekType 1207 ) 1208 { 1209 EFI_STATUS Status; 1210 UINT64 CurrentPosition; 1211 1212 if (!FileHandleValid (File)) { 1213 return EFI_INVALID_PARAMETER; 1214 } 1215 1216 if (File->Type == EfiOpenLoadFile) { 1217 // LoadFile does not support Seek 1218 return EFI_UNSUPPORTED; 1219 } 1220 1221 CurrentPosition = File->CurrentPosition; 1222 switch (SeekType) { 1223 case EfiSeekStart: 1224 if (Offset > File->MaxPosition) { 1225 return EFI_NOT_FOUND; 1226 } 1227 CurrentPosition = Offset; 1228 break; 1229 1230 case EfiSeekCurrent: 1231 if ((File->CurrentPosition + Offset) > File->MaxPosition) { 1232 return EFI_NOT_FOUND; 1233 } 1234 CurrentPosition += Offset; 1235 break; 1236 1237 case EfiSeekEnd: 1238 if (Offset != 0) { 1239 // We don't support growing file size via seeking past end of file 1240 return EFI_UNSUPPORTED; 1241 } 1242 CurrentPosition = File->MaxPosition; 1243 break; 1244 1245 default: 1246 return EFI_NOT_FOUND; 1247 } 1248 1249 Status = EFI_SUCCESS; 1250 if (File->FsFileHandle != NULL) { 1251 Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition); 1252 } 1253 1254 if (!EFI_ERROR (Status)) { 1255 File->CurrentPosition = CurrentPosition; 1256 } 1257 1258 return Status; 1259 } 1260 1261 EFI_STATUS 1262 CacheTftpFile ( 1263 IN OUT EFI_OPEN_FILE *File 1264 ) 1265 { 1266 EFI_STATUS Status; 1267 UINT64 TftpBufferSize; 1268 1269 if (File->IsBufferValid) { 1270 return EFI_SUCCESS; 1271 } 1272 1273 // Make sure the file size is set. 1274 EfiTell (File, NULL); 1275 1276 //Allocate a buffer to hold the whole file. 1277 File->Buffer = AllocatePool(File->Size); 1278 if (File->Buffer == NULL) { 1279 return EFI_OUT_OF_RESOURCES; 1280 } 1281 1282 TftpBufferSize = File->Size; 1283 1284 Status = EblMtftp ( 1285 EFI_PXE_BASE_CODE_TFTP_READ_FILE, 1286 File->Buffer, 1287 FALSE, 1288 &TftpBufferSize, 1289 NULL, 1290 &File->ServerIp, 1291 (UINT8 *)File->FileName, 1292 NULL, 1293 FALSE); 1294 if (EFI_ERROR(Status)) { 1295 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status); 1296 FreePool(File->Buffer); 1297 return Status; 1298 } 1299 1300 // Set the buffer valid flag. 1301 File->IsBufferValid = TRUE; 1302 1303 return Status; 1304 } 1305 1306 /** 1307 Read BufferSize bytes from the current location in the file. For load file, 1308 FV, and TFTP case you must read the entire file. 1309 1310 @param Stream Open File Handle 1311 @param Buffer Caller allocated buffer. 1312 @param BufferSize Size of buffer in bytes. 1313 1314 1315 @return EFI_SUCCESS Stream is not an Open File 1316 @return EFI_END_OF_FILE Tried to read past the end of the file 1317 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1318 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1319 @return "other" Error returned from device read 1320 1321 **/ 1322 EFI_STATUS 1323 EfiRead ( 1324 IN EFI_OPEN_FILE *File, 1325 OUT VOID *Buffer, 1326 OUT UINTN *BufferSize 1327 ) 1328 { 1329 EFI_STATUS Status; 1330 UINT32 AuthenticationStatus; 1331 EFI_DISK_IO_PROTOCOL *DiskIo; 1332 1333 if (!FileHandleValid (File)) { 1334 return EFI_INVALID_PARAMETER; 1335 } 1336 1337 // Don't read past the end of the file. 1338 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1339 return EFI_END_OF_FILE; 1340 } 1341 1342 switch (File->Type) { 1343 case EfiOpenLoadFile: 1344 // Figure out the File->Size 1345 EfiTell (File, NULL); 1346 1347 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer); 1348 break; 1349 1350 case EfiOpenFirmwareVolume: 1351 if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) { 1352 // This is the entire FV device, so treat like a memory buffer 1353 CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize); 1354 File->CurrentPosition += *BufferSize; 1355 Status = EFI_SUCCESS; 1356 } else { 1357 if (File->Buffer == NULL) { 1358 if (File->FvSectionType == EFI_SECTION_ALL) { 1359 Status = File->Fv->ReadFile ( 1360 File->Fv, 1361 &File->FvNameGuid, 1362 (VOID **)&File->Buffer, 1363 &File->Size, 1364 &File->FvType, 1365 &File->FvAttributes, 1366 &AuthenticationStatus 1367 ); 1368 } else { 1369 Status = File->Fv->ReadSection ( 1370 File->Fv, 1371 &File->FvNameGuid, 1372 File->FvSectionType, 1373 0, 1374 (VOID **)&File->Buffer, 1375 &File->Size, 1376 &AuthenticationStatus 1377 ); 1378 } 1379 if (EFI_ERROR (Status)) { 1380 return Status; 1381 } 1382 File->IsBufferValid = TRUE; 1383 } 1384 // Operate on the cached buffer so Seek will work 1385 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1386 File->CurrentPosition += *BufferSize; 1387 Status = EFI_SUCCESS; 1388 } 1389 break; 1390 1391 case EfiOpenMemoryBuffer: 1392 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1393 File->CurrentPosition += *BufferSize; 1394 Status = EFI_SUCCESS; 1395 break; 1396 1397 case EfiOpenFileSystem: 1398 Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer); 1399 File->CurrentPosition += *BufferSize; 1400 break; 1401 1402 case EfiOpenBlockIo: 1403 Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo); 1404 if (!EFI_ERROR(Status)) { 1405 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer); 1406 } 1407 File->CurrentPosition += *BufferSize; 1408 break; 1409 1410 case EfiOpenTftp: 1411 // Cache the file if it hasn't been cached yet. 1412 if (File->IsBufferValid == FALSE) { 1413 Status = CacheTftpFile (File); 1414 if (EFI_ERROR (Status)) { 1415 return Status; 1416 } 1417 } 1418 1419 // Copy out the requested data 1420 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize); 1421 File->CurrentPosition += *BufferSize; 1422 1423 Status = EFI_SUCCESS; 1424 break; 1425 1426 default: 1427 return EFI_INVALID_PARAMETER; 1428 }; 1429 1430 return Status; 1431 } 1432 1433 1434 /** 1435 Read the entire file into a buffer. This routine allocates the buffer and 1436 returns it to the user full of the read data. 1437 1438 This is very useful for load file where it's hard to know how big the buffer 1439 must be. 1440 1441 @param Stream Open File Handle 1442 @param Buffer Pointer to buffer to return. 1443 @param BufferSize Pointer to Size of buffer return.. 1444 1445 1446 @return EFI_SUCCESS Stream is not an Open File 1447 @return EFI_END_OF_FILE Tried to read past the end of the file 1448 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1449 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1450 @return "other" Error returned from device read 1451 1452 **/ 1453 EFI_STATUS 1454 EfiReadAllocatePool ( 1455 IN EFI_OPEN_FILE *File, 1456 OUT VOID **Buffer, 1457 OUT UINTN *BufferSize 1458 ) 1459 { 1460 if (!FileHandleValid (File)) { 1461 return EFI_INVALID_PARAMETER; 1462 } 1463 1464 // Loadfile defers file size determination on Open so use tell to find it 1465 EfiTell (File, NULL); 1466 1467 *BufferSize = File->Size; 1468 *Buffer = AllocatePool (*BufferSize); 1469 if (*Buffer == NULL) { 1470 return EFI_NOT_FOUND; 1471 } 1472 1473 return EfiRead (File, *Buffer, BufferSize); 1474 } 1475 1476 1477 /** 1478 Write data back to the file. For TFTP case you must write the entire file. 1479 1480 @param Stream Open File Handle 1481 @param Buffer Pointer to buffer to return. 1482 @param BufferSize Pointer to Size of buffer return.. 1483 1484 1485 @return EFI_SUCCESS Stream is not an Open File 1486 @return EFI_END_OF_FILE Tried to read past the end of the file 1487 @return EFI_INVALID_PARAMETER Stream is not an open file handle 1488 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read 1489 @return "other" Error returned from device write 1490 1491 **/ 1492 EFI_STATUS 1493 EfiWrite ( 1494 IN EFI_OPEN_FILE *File, 1495 OUT VOID *Buffer, 1496 OUT UINTN *BufferSize 1497 ) 1498 { 1499 EFI_STATUS Status; 1500 EFI_FV_WRITE_FILE_DATA FileData; 1501 EFI_DISK_IO_PROTOCOL *DiskIo; 1502 1503 if (!FileHandleValid (File)) { 1504 return EFI_INVALID_PARAMETER; 1505 } 1506 1507 switch (File->Type) { 1508 case EfiOpenMemoryBuffer: 1509 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1510 return EFI_END_OF_FILE; 1511 } 1512 1513 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize); 1514 File->CurrentPosition += *BufferSize; 1515 Status = EFI_SUCCESS; 1516 1517 case EfiOpenLoadFile: 1518 // LoadFile device is read only be definition 1519 Status = EFI_UNSUPPORTED; 1520 1521 case EfiOpenFirmwareVolume: 1522 if (File->FvSectionType != EFI_SECTION_ALL) { 1523 // Writes not support to a specific section. You have to update entire file 1524 return EFI_UNSUPPORTED; 1525 } 1526 1527 FileData.NameGuid = &(File->FvNameGuid); 1528 FileData.Type = File->FvType; 1529 FileData.FileAttributes = File->FvAttributes; 1530 FileData.Buffer = Buffer; 1531 FileData.BufferSize = (UINT32)*BufferSize; 1532 Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData); 1533 break; 1534 1535 case EfiOpenFileSystem: 1536 Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer); 1537 File->CurrentPosition += *BufferSize; 1538 break; 1539 1540 case EfiOpenBlockIo: 1541 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1542 return EFI_END_OF_FILE; 1543 } 1544 1545 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo); 1546 if (!EFI_ERROR(Status)) { 1547 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer); 1548 } 1549 File->CurrentPosition += *BufferSize; 1550 break; 1551 1552 case EfiOpenTftp: 1553 // Cache the file if it hasn't been cached yet. 1554 if (File->IsBufferValid == FALSE) { 1555 Status = CacheTftpFile(File); 1556 if (EFI_ERROR(Status)) { 1557 return Status; 1558 } 1559 } 1560 1561 // Don't overwrite the buffer 1562 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) { 1563 UINT8 *TempBuffer; 1564 1565 TempBuffer = File->Buffer; 1566 1567 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize)); 1568 if (File->Buffer == NULL) { 1569 return EFI_OUT_OF_RESOURCES; 1570 } 1571 1572 CopyMem (File->Buffer, TempBuffer, File->Size); 1573 1574 FreePool (TempBuffer); 1575 1576 File->Size = (UINTN)(File->CurrentPosition + *BufferSize); 1577 File->MaxPosition = (UINT64)File->Size; 1578 } 1579 1580 // Copy in the requested data 1581 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize); 1582 File->CurrentPosition += *BufferSize; 1583 1584 // Mark the file dirty 1585 File->IsDirty = TRUE; 1586 1587 Status = EFI_SUCCESS; 1588 break; 1589 1590 default: 1591 Status = EFI_INVALID_PARAMETER; 1592 }; 1593 1594 return Status; 1595 } 1596 1597 1598 /** 1599 Given Cwd expand Path to remove .. and replace them with real 1600 directory names. 1601 1602 @param Cwd Current Working Directory 1603 @param Path Path to expand 1604 1605 @return NULL Cwd or Path are not valid 1606 @return 'other' Path with .. expanded 1607 1608 **/ 1609 CHAR8 * 1610 ExpandPath ( 1611 IN CHAR8 *Cwd, 1612 IN CHAR8 *Path 1613 ) 1614 { 1615 CHAR8 *NewPath; 1616 CHAR8 *Work, *Start, *End; 1617 UINTN StrLen, AllocLen; 1618 INTN i; 1619 1620 if (Cwd == NULL || Path == NULL) { 1621 return NULL; 1622 } 1623 1624 StrLen = AsciiStrSize (Cwd); 1625 if (StrLen <= 2) { 1626 // Smallest valid path is 1 char and a null 1627 return NULL; 1628 } 1629 1630 StrLen = AsciiStrSize (Path); 1631 AllocLen = AsciiStrSize (Cwd) + StrLen + 1; 1632 NewPath = AllocatePool (AllocLen); 1633 if (NewPath == NULL) { 1634 return NULL; 1635 } 1636 AsciiStrCpyS (NewPath, AllocLen, Cwd); 1637 1638 End = Path + StrLen; 1639 for (Start = Path ;;) { 1640 Work = AsciiStrStr (Start, "..") ; 1641 if (Work == NULL) { 1642 // Remaining part of Path contains no more .. 1643 break; 1644 } 1645 1646 // append path prior to .. 1647 AsciiStrnCatS (NewPath, AllocLen, Start, Work - Start); 1648 StrLen = AsciiStrLen (NewPath); 1649 for (i = StrLen; i >= 0; i--) { 1650 if (NewPath[i] == ':') { 1651 // too many .. 1652 return NULL; 1653 } 1654 if (NewPath[i] == '/' || NewPath[i] == '\\') { 1655 if ((i > 0) && (NewPath[i-1] == ':')) { 1656 // leave the / before a : 1657 NewPath[i+1] = '\0'; 1658 } else { 1659 // replace / will Null to remove trailing file/dir reference 1660 NewPath[i] = '\0'; 1661 } 1662 break; 1663 } 1664 } 1665 1666 Start = Work + 3; 1667 } 1668 1669 // Handle the path that remains after the .. 1670 AsciiStrnCatS (NewPath, AllocLen, Start, End - Start); 1671 1672 return NewPath; 1673 } 1674 1675 1676 /** 1677 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and 1678 the path does not contain a device name, The CWD is prepended to the path. 1679 1680 @param Cwd Current Working Directory to set 1681 1682 1683 @return EFI_SUCCESS CWD is set 1684 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path 1685 1686 **/ 1687 EFI_STATUS 1688 EfiSetCwd ( 1689 IN CHAR8 *Cwd 1690 ) 1691 { 1692 EFI_OPEN_FILE *File; 1693 UINTN Len, AllocLen; 1694 CHAR8 *Path; 1695 1696 if (Cwd == NULL) { 1697 return EFI_INVALID_PARAMETER; 1698 } 1699 1700 if (AsciiStrCmp (Cwd, ".") == 0) { 1701 // cd . is a no-op 1702 return EFI_SUCCESS; 1703 } 1704 1705 Path = Cwd; 1706 if (AsciiStrStr (Cwd, "..") != NULL) { 1707 if (gCwd == NULL) { 1708 // no parent 1709 return EFI_SUCCESS; 1710 } 1711 1712 Len = AsciiStrLen (gCwd); 1713 if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) { 1714 // parent is device so nothing to do 1715 return EFI_SUCCESS; 1716 } 1717 1718 // Expand .. in Cwd, given we know current working directory 1719 Path = ExpandPath (gCwd, Cwd); 1720 if (Path == NULL) { 1721 return EFI_NOT_FOUND; 1722 } 1723 } 1724 1725 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0); 1726 if (File == NULL) { 1727 return EFI_INVALID_PARAMETER; 1728 } 1729 1730 if (gCwd != NULL) { 1731 FreePool (gCwd); 1732 } 1733 1734 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be 1735 // relative to the current gCwd or not. 1736 AllocLen = AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10; 1737 gCwd = AllocatePool (AllocLen); 1738 if (gCwd == NULL) { 1739 return EFI_INVALID_PARAMETER; 1740 } 1741 1742 AsciiStrCpyS (gCwd, AllocLen, File->DeviceName); 1743 if (File->FileName == NULL) { 1744 AsciiStrCatS (gCwd, AllocLen, ":\\"); 1745 } else { 1746 AsciiStrCatS (gCwd, AllocLen, ":"); 1747 AsciiStrCatS (gCwd, AllocLen, File->FileName); 1748 } 1749 1750 1751 EfiClose (File); 1752 if (Path != Cwd) { 1753 FreePool (Path); 1754 } 1755 return EFI_SUCCESS; 1756 } 1757 1758 1759 /** 1760 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and 1761 the path does not contain a device name, The CWD is prepended to the path. 1762 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After 1763 a call to EfiSetCwd() it is not legal to use the pointer returned by 1764 this function. 1765 1766 @param Cwd Current Working Directory 1767 1768 1769 @return "" No CWD set 1770 @return 'other' Returns buffer that contains CWD. 1771 1772 **/ 1773 CHAR8 * 1774 EfiGetCwd ( 1775 VOID 1776 ) 1777 { 1778 if (gCwd == NULL) { 1779 return ""; 1780 } 1781 return gCwd; 1782 } 1783 1784 1785