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