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