]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c
Fix help command scroll issue. Also add FV space used, and free space to dir command.
[mirror_edk2.git] / EmbeddedPkg / Library / EfiFileLib / EfiFileLib.c
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
58 CHAR8 *gCwd = NULL;
59
60 CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
61
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
68 typedef 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
76 EFI_HANDLE *mBlkIo = NULL;
77 UINTN mBlkIoCount = 0;
78
79 EFI_HANDLE *mFs = NULL;
80 UINTN mFsCount = 0;
81 // mFsInfo[] array entries must match mFs[] handles
82 EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;
83
84 EFI_HANDLE *mFv = NULL;
85 UINTN mFvCount = 0;
86 EFI_HANDLE *mLoadFile = NULL;
87 UINTN 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 **/
101 BOOLEAN
102 FileHandleValid (
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 **/
124 VOID
125 EblFreePool (
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 **/
138 VOID
139 EblUpdateDeviceLists (
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 **/
222 BOOLEAN
223 EblMatchVolumeName (
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 **/
271 UINTN
272 EfiGetDeviceCounts (
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
290 EFI_STATUS
291 ConvertIpStringToEfiIp (
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 **/
336 UINTN
337 EblConvertDevStringToNumber (
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 **/
366 EFI_STATUS
367 EblFileDevicePath (
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
455 EFI_STATUS
456 CompareGuidToString (
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 **/
502 EFI_STATUS
503 EblFvFileDevicePath (
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 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;
523 UINTN Index;
524
525
526 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
527 if (EFI_ERROR (Status)) {
528 return Status;
529 }
530
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)) {
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 }
541
542 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
543 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
544 if (EFI_ERROR (Status)) {
545 break;
546 }
547 }
548 }
549 }
550
551
552 DevicePath = DevicePathFromHandle (File->EfiHandle);
553
554 if (*FileName == '\0') {
555 File->DevicePath = DuplicateDevicePath (DevicePath);
556 File->Size = File->FvSize;
557 File->MaxPosition = File->Size;
558 } else {
559 Key = 0;
560 do {
561 File->FvType = EFI_FV_FILETYPE_ALL;
562 GetNextFileStatus = File->Fv->GetNextFile (
563 File->Fv,
564 &Key,
565 &File->FvType,
566 &File->FvNameGuid,
567 &File->FvAttributes,
568 &File->Size
569 );
570 if (!EFI_ERROR (GetNextFileStatus)) {
571 // Compare GUID first
572 Status = CompareGuidToString (&File->FvNameGuid, FileName);
573 if (!EFI_ERROR(Status)) {
574 break;
575 }
576
577 Section = NULL;
578 Status = File->Fv->ReadSection (
579 File->Fv,
580 &File->FvNameGuid,
581 EFI_SECTION_USER_INTERFACE,
582 0,
583 &Section,
584 &SectionSize,
585 &AuthenticationStatus
586 );
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
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 (
607 File->Fv,
608 &File->FvNameGuid,
609 OpenMode,
610 0,
611 &Section,
612 &File->Size,
613 &AuthenticationStatus
614 );
615 if (EFI_ERROR (Status)) {
616 return Status;
617 }
618 }
619
620 File->MaxPosition = File->Size;
621 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
622 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
623 }
624
625
626 // FVB not required if FV was soft loaded...
627 return EFI_SUCCESS;
628 }
629
630
631
632
633 /**
634 Open a device named by PathName. The PathName includes a device name and
635 path seperated by a :. See file header for more details on the PathName
636 syntax. There is no checking to prevent a file from being opened more than
637 one type.
638
639 SectionType is only used to open an FV. Each file in an FV contains multiple
640 secitons and only the SectionType section is opened.
641
642 For any file that is opened with EfiOpen() must be closed with EfiClose().
643
644 @param PathName Path to parse to open
645 @param OpenMode Same as EFI_FILE.Open()
646 @param SectionType Section in FV to open.
647
648 @return NULL Open failed
649 @return Valid EFI_OPEN_FILE handle
650
651 **/
652 EFI_OPEN_FILE *
653 EfiOpen (
654 IN CHAR8 *PathName,
655 IN CONST UINT64 OpenMode,
656 IN CONST EFI_SECTION_TYPE SectionType
657 )
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;
670 CHAR8 *CwdPlusPathName;
671 UINTN Index;
672 EFI_SECTION_TYPE ModifiedSectionType;
673
674 EblUpdateDeviceLists ();
675
676 File = &FileData;
677 ZeroMem (File, sizeof (EFI_OPEN_FILE));
678
679 StrLen = AsciiStrSize (PathName);
680 if (StrLen <= 1) {
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
692 //
693 // Matching volume name has precedence over handle based names
694 //
695 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
696 if (!VolumeNameMatch) {
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 }
703
704 // We could add a current working diretory concept
705 CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
706 if (CwdPlusPathName == NULL) {
707 return NULL;
708 }
709
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 }
728
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 }
734
735 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
736 FreePool (CwdPlusPathName);
737 return File;
738 }
739
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];
747 if (File->FileName[0] == '\0') {
748 // if it is just a file name use / as root
749 File->FileName = "\\";
750 }
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);
766
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 }
778
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
785 ModifiedSectionType = AsciiStrHexToUintn (&PathName[Index + 1]);
786 PathName[Index] = '\0';
787 }
788 }
789 File->FvSectionType = ModifiedSectionType;
790 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
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 }
802
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 }
809
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 }
815
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];
825
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);
836
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 }
860
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 }
868
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 }
879
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
904 ErrorExit:
905 FreePool (File->DeviceName);
906 return NULL;
907 }
908
909 #define FILE_COPY_CHUNK 0x01000000
910
911 EFI_STATUS
912 EfiCopyFile (
913 IN CHAR8 *DestinationFile,
914 IN CHAR8 *SourceFile
915 )
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;
924
925 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
926 if (Source == NULL) {
927 AsciiPrint("Source file open error.\n");
928 Status = EFI_NOT_FOUND;
929 goto Exit;
930 }
931
932 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
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 }
944
945 Size = EfiTell(Source, NULL);
946
947 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
948 Chunk = FILE_COPY_CHUNK;
949
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 }
962
963 // Any left over?
964 if (Offset < Size) {
965 Chunk = Size - Offset;
966
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
980 Exit:
981 if (Source != NULL) {
982 Status = EfiClose(Source);
983 if (EFI_ERROR(Status)) {
984 AsciiPrint("Source close error");
985 }
986 }
987
988 if (Destination != NULL) {
989 Status = EfiClose(Destination);
990 if (EFI_ERROR(Status)) {
991 AsciiPrint("Destination close error");
992 }
993 }
994
995 if (Buffer != NULL) {
996 FreePool(Buffer);
997 }
998
999 return Status;
1000 }
1001
1002 /**
1003 Use DeviceType and Index to form a valid PathName and try and open it.
1004
1005 @param DeviceType Device type to open
1006 @param Index Device Index to use. Zero relative.
1007
1008 @return NULL Open failed
1009 @return Valid EFI_OPEN_FILE handle
1010
1011 **/
1012 EFI_OPEN_FILE *
1013 EfiDeviceOpenByType (
1014 IN EFI_OPEN_FILE_TYPE DeviceType,
1015 IN UINTN Index
1016 )
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 /**
1048 Close a file handle opened by EfiOpen() and free all resources allocated by
1049 EfiOpen().
1050
1051 @param Stream Open File Handle
1052
1053 @return EFI_INVALID_PARAMETER Stream is not an Open File
1054 @return EFI_SUCCESS Steam closed
1055
1056 **/
1057 EFI_STATUS
1058 EfiClose (
1059 IN EFI_OPEN_FILE *File
1060 )
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 (
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 );
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) ||
1091 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
1092 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
1093 EblFreePool(File->Buffer);
1094 }
1095
1096 EblFreePool (File->DevicePath);
1097 EblFreePool (File->DeviceName);
1098 EblFreePool (File->FsFileInfo);
1099 EblFreePool (File->FsInfo);
1100
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;
1108 }
1109
1110
1111 /**
1112 Return the size of the file represented by Stream. Also return the current
1113 Seek position. Opening a file will enable a valid file size to be returned.
1114 LoadFile is an exception as a load file size is set to zero.
1115
1116 @param Stream Open File Handle
1117
1118 @return 0 Stream is not an Open File or a valid LoadFile handle
1119
1120 **/
1121 UINTN
1122 EfiTell (
1123 IN EFI_OPEN_FILE *File,
1124 OUT EFI_LBA *CurrentPosition OPTIONAL
1125 )
1126 {
1127 EFI_STATUS Status;
1128 UINT64 BufferSize = 0;
1129
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 }
1146
1147 File->MaxPosition = (UINT64)File->Size;
1148 } else if (File->Type == EfiOpenTftp) {
1149
1150 Status = EblMtftp (
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 );
1161 if (EFI_ERROR(Status)) {
1162 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
1163 return 0;
1164 }
1165
1166 File->Size = (UINTN)BufferSize;
1167 File->MaxPosition = File->Size;
1168 }
1169
1170 return File->Size;
1171 }
1172
1173
1174 /**
1175 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1176 not support EfiSeek(). It is not possible to grow the file size using
1177 EfiSeek().
1178
1179 SeekType defines how use Offset to calculate the new file position:
1180 EfiSeekStart : Position = Offset
1181 EfiSeekCurrent: Position is Offset bytes from the current position
1182 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1183
1184 @param Stream Open File Handle
1185 @param Offset Offset to seek too.
1186 @param SeekType Type of seek to perform
1187
1188
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
1193
1194 **/
1195 EFI_STATUS
1196 EfiSeek (
1197 IN EFI_OPEN_FILE *File,
1198 IN EFI_LBA Offset,
1199 IN EFI_SEEK_TYPE SeekType
1200 )
1201 {
1202 EFI_STATUS Status;
1203 UINT64 CurrentPosition;
1204
1205 if (!FileHandleValid (File)) {
1206 return EFI_INVALID_PARAMETER;
1207 }
1208
1209 if (File->Type == EfiOpenLoadFile) {
1210 // LoadFile does not support Seek
1211 return EFI_UNSUPPORTED;
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
1254 EFI_STATUS
1255 CacheTftpFile (
1256 IN OUT EFI_OPEN_FILE *File
1257 )
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 (
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);
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 /**
1300 Read BufferSize bytes from the current locaiton in the file. For load file,
1301 FV, and TFTP case you must read the entire file.
1302
1303 @param Stream Open File Handle
1304 @param Buffer Caller allocated buffer.
1305 @param BufferSize Size of buffer in bytes.
1306
1307
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
1313
1314 **/
1315 EFI_STATUS
1316 EfiRead (
1317 IN EFI_OPEN_FILE *File,
1318 OUT VOID *Buffer,
1319 OUT UINTN *BufferSize
1320 )
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) {
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;
1342
1343 case EfiOpenFirmwareVolume:
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;
1349 } else {
1350 if (File->Buffer == NULL) {
1351 if (File->FvSectionType == EFI_SECTION_ALL) {
1352 Status = File->Fv->ReadFile (
1353 File->Fv,
1354 &File->FvNameGuid,
1355 (VOID **)&File->Buffer,
1356 &File->Size,
1357 &File->FvType,
1358 &File->FvAttributes,
1359 &AuthenticationStatus
1360 );
1361 } else {
1362 Status = File->Fv->ReadSection (
1363 File->Fv,
1364 &File->FvNameGuid,
1365 File->FvSectionType,
1366 0,
1367 (VOID **)&File->Buffer,
1368 &File->Size,
1369 &AuthenticationStatus
1370 );
1371 }
1372 if (EFI_ERROR (Status)) {
1373 return Status;
1374 }
1375 File->IsBufferValid = TRUE;
1376 }
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;
1381 }
1382 break;
1383
1384 case EfiOpenMemoryBuffer:
1385 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1386 File->CurrentPosition += *BufferSize;
1387 Status = EFI_SUCCESS;
1388 break;
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;
1402
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;
1418
1419 default:
1420 return EFI_INVALID_PARAMETER;
1421 };
1422
1423 return Status;
1424 }
1425
1426
1427 /**
1428 Read the entire file into a buffer. This routine allocates the buffer and
1429 returns it to the user full of the read data.
1430
1431 This is very useful for load flie where it's hard to know how big the buffer
1432 must be.
1433
1434 @param Stream Open File Handle
1435 @param Buffer Pointer to buffer to return.
1436 @param BufferSize Pointer to Size of buffer return..
1437
1438
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
1444
1445 **/
1446 EFI_STATUS
1447 EfiReadAllocatePool (
1448 IN EFI_OPEN_FILE *File,
1449 OUT VOID **Buffer,
1450 OUT UINTN *BufferSize
1451 )
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 /**
1471 Write data back to the file. For TFTP case you must write the entire file.
1472
1473 @param Stream Open File Handle
1474 @param Buffer Pointer to buffer to return.
1475 @param BufferSize Pointer to Size of buffer return..
1476
1477
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
1483
1484 **/
1485 EFI_STATUS
1486 EfiWrite (
1487 IN EFI_OPEN_FILE *File,
1488 OUT VOID *Buffer,
1489 OUT UINTN *BufferSize
1490 )
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;
1513
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;
1527
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
1560 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
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
1569 File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
1570 File->MaxPosition = (UINT64)File->Size;
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
1590
1591 /**
1592 Given Cwd expand Path to remove .. and replace them with real
1593 directory names.
1594
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
1600
1601 **/
1602 CHAR8 *
1603 ExpandPath (
1604 IN CHAR8 *Cwd,
1605 IN CHAR8 *Path
1606 )
1607 {
1608 CHAR8 *NewPath;
1609 CHAR8 *Work, *Start, *End;
1610 UINTN StrLen;
1611 UINTN i;
1612
1613 if (Cwd == NULL || Path == NULL) {
1614 return NULL;
1615 }
1616
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);
1629
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 }
1637
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 }
1657
1658 Start = Work + 3;
1659 }
1660
1661 // Handle the path that remains after the ..
1662 AsciiStrnCat (NewPath, Start, End - Start);
1663
1664 return NewPath;
1665 }
1666
1667
1668 /**
1669 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1670 the path does not contain a device name, The CWD is prepended to the path.
1671
1672 @param Cwd Current Working Directory to set
1673
1674
1675 @return EFI_SUCCESS CWD is set
1676 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1677
1678 **/
1679 EFI_STATUS
1680 EfiSetCwd (
1681 IN CHAR8 *Cwd
1682 )
1683 {
1684 EFI_OPEN_FILE *File;
1685 UINTN Len;
1686 CHAR8 *Path;
1687
1688 if (Cwd == NULL) {
1689 return EFI_INVALID_PARAMETER;
1690 }
1691
1692 if (AsciiStrCmp (Cwd, ".") == 0) {
1693 // cd . is a no-op
1694 return EFI_SUCCESS;
1695 }
1696
1697 Path = Cwd;
1698 if (AsciiStrStr (Cwd, "..") != NULL) {
1699 if (gCwd == NULL) {
1700 // no parent
1701 return EFI_SUCCESS;
1702 }
1703
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 }
1709
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 }
1716
1717 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1718 if (File == NULL) {
1719 return EFI_INVALID_PARAMETER;
1720 }
1721
1722 if (gCwd != NULL) {
1723 FreePool (gCwd);
1724 }
1725
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.
1728 gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
1729 if (gCwd == NULL) {
1730 return EFI_INVALID_PARAMETER;
1731 }
1732 AsciiStrCpy (gCwd, File->DeviceName);
1733 if (File->FileName == NULL) {
1734 AsciiStrCat (gCwd, ":\\");
1735 } else {
1736 AsciiStrCat (gCwd, ":");
1737 AsciiStrCat (gCwd, File->FileName);
1738 }
1739
1740 EfiClose (File);
1741 if (Path != Cwd) {
1742 FreePool (Path);
1743 }
1744 return EFI_SUCCESS;
1745 }
1746
1747
1748 /**
1749 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1750 the path does not contain a device name, The CWD is prepended to the path.
1751 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1752 a call to EfiSetCwd() it is not legal to use the pointer returned by
1753 this funciton.
1754
1755 @param Cwd Current Working Directory
1756
1757
1758 @return "" No CWD set
1759 @return 'other' Returns buffer that contains CWD.
1760
1761 **/
1762 CHAR8 *
1763 EfiGetCwd (
1764 VOID
1765 )
1766 {
1767 if (gCwd == NULL) {
1768 return "";
1769 }
1770 return gCwd;
1771 }
1772
1773