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