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