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