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