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