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