]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ShellProtocol.c
ShellPkg: Make Argv[0] the full file path of the command
[mirror_edk2.git] / ShellPkg / Application / Shell / ShellProtocol.c
1 /** @file
2 Member functions of EFI_SHELL_PROTOCOL and functions for creation,
3 manipulation, and initialization of EFI_SHELL_PROTOCOL.
4
5 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Shell.h"
17
18 /**
19 Close an open file handle.
20
21 This function closes a specified file handle. All "dirty" cached file data is
22 flushed to the device, and the file is closed. In all cases the handle is
23 closed.
24
25 @param[in] FileHandle The file handle to close.
26
27 @retval EFI_SUCCESS The file handle was closed successfully.
28 **/
29 EFI_STATUS
30 EFIAPI
31 EfiShellClose (
32 IN SHELL_FILE_HANDLE FileHandle
33 )
34 {
35 ShellFileHandleRemove(FileHandle);
36 return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));
37 }
38
39 /**
40 Internal worker to determine whether there is a BlockIo somewhere
41 upon the device path specified.
42
43 @param[in] DevicePath The device path to test.
44
45 @retval TRUE gEfiBlockIoProtocolGuid was installed on a handle with this device path
46 @retval FALSE gEfiBlockIoProtocolGuid was not found.
47 **/
48 BOOLEAN
49 EFIAPI
50 InternalShellProtocolIsBlockIoPresent(
51 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
52 )
53 {
54 EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
55 EFI_STATUS Status;
56 EFI_HANDLE Handle;
57
58 Handle = NULL;
59
60 DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
61 Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
62
63 if ((Handle != NULL) && (!EFI_ERROR(Status))) {
64 return (TRUE);
65 }
66 return (FALSE);
67 }
68
69 /**
70 Internal worker to determine whether there is a file system somewhere
71 upon the device path specified.
72
73 @param[in] DevicePath The device path to test.
74
75 @retval TRUE gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
76 @retval FALSE gEfiSimpleFileSystemProtocolGuid was not found.
77 **/
78 BOOLEAN
79 EFIAPI
80 InternalShellProtocolIsSimpleFileSystemPresent(
81 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
82 )
83 {
84 EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
85 EFI_STATUS Status;
86 EFI_HANDLE Handle;
87
88 Handle = NULL;
89
90 DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
91 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
92
93 if ((Handle != NULL) && (!EFI_ERROR(Status))) {
94 return (TRUE);
95 }
96 return (FALSE);
97 }
98
99 /**
100 Internal worker debug helper function to print out maps as they are added.
101
102 @param[in] Mapping string mapping that has been added
103 @param[in] DevicePath pointer to device path that has been mapped.
104
105 @retval EFI_SUCCESS the operation was successful.
106 @return other an error ocurred
107
108 @sa LocateHandle
109 @sa OpenProtocol
110 **/
111 EFI_STATUS
112 EFIAPI
113 InternalShellProtocolDebugPrintMessage (
114 IN CONST CHAR16 *Mapping,
115 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
116 )
117 {
118 EFI_STATUS Status;
119 CHAR16 *Temp;
120
121 Status = EFI_SUCCESS;
122 DEBUG_CODE_BEGIN();
123
124 if (Mapping != NULL) {
125 DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));
126 }
127 Temp = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
128 DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));
129 FreePool(Temp);
130
131 DEBUG_CODE_END();
132 return (Status);
133 }
134
135 /**
136 This function creates a mapping for a device path.
137
138 If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
139
140 @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
141 then the mapping will be deleted.
142 @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
143
144 @retval EFI_SUCCESS Mapping created or deleted successfully.
145 @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
146 boot service function LocateDevicePath().
147 @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
148 @retval EFI_INVALID_PARAMETER Mapping was NULL
149 @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
150 @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
151 @retval EFI_NOT_FOUND There was no mapping found to delete
152 @retval EFI_OUT_OF_RESOURCES Memory allocation failed
153 **/
154 EFI_STATUS
155 EFIAPI
156 EfiShellSetMap(
157 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
158 IN CONST CHAR16 *Mapping
159 )
160 {
161 EFI_STATUS Status;
162 SHELL_MAP_LIST *MapListNode;
163
164 if (Mapping == NULL){
165 return (EFI_INVALID_PARAMETER);
166 }
167
168 if (Mapping[StrLen(Mapping)-1] != ':') {
169 return (EFI_INVALID_PARAMETER);
170 }
171
172 //
173 // Delete the mapping
174 //
175 if (DevicePath == NULL) {
176 if (IsListEmpty(&gShellMapList.Link)) {
177 return (EFI_NOT_FOUND);
178 }
179 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
180 ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
181 ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
182 ){
183 if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
184 RemoveEntryList(&MapListNode->Link);
185 FreePool(MapListNode);
186 return (EFI_SUCCESS);
187 }
188 } // for loop
189
190 //
191 // We didnt find one to delete
192 //
193 return (EFI_NOT_FOUND);
194 }
195
196 //
197 // make sure this is a valid to add device path
198 //
199 ///@todo add BlockIo to this test...
200 if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)
201 && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {
202 return (EFI_INVALID_PARAMETER);
203 }
204
205 //
206 // First make sure there is no old mapping
207 //
208 Status = EfiShellSetMap(NULL, Mapping);
209 if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
210 return (Status);
211 }
212
213 //
214 // now add the new one.
215 //
216 Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
217
218 return(Status);
219 }
220
221 /**
222 Gets the device path from the mapping.
223
224 This function gets the device path associated with a mapping.
225
226 @param Mapping A pointer to the mapping
227
228 @retval !=NULL Pointer to the device path that corresponds to the
229 device mapping. The returned pointer does not need
230 to be freed.
231 @retval NULL There is no device path associated with the
232 specified mapping.
233 **/
234 CONST EFI_DEVICE_PATH_PROTOCOL *
235 EFIAPI
236 EfiShellGetDevicePathFromMap(
237 IN CONST CHAR16 *Mapping
238 )
239 {
240 SHELL_MAP_LIST *MapListItem;
241 CHAR16 *NewName;
242 UINTN Size;
243
244 NewName = NULL;
245 Size = 0;
246
247 StrnCatGrow(&NewName, &Size, Mapping, 0);
248 if (Mapping[StrLen(Mapping)-1] != L':') {
249 StrnCatGrow(&NewName, &Size, L":", 0);
250 }
251
252 MapListItem = ShellCommandFindMapItem(NewName);
253
254 FreePool(NewName);
255
256 if (MapListItem != NULL) {
257 return (MapListItem->DevicePath);
258 }
259 return(NULL);
260 }
261
262 /**
263 Gets the mapping(s) that most closely matches the device path.
264
265 This function gets the mapping which corresponds to the device path *DevicePath. If
266 there is no exact match, then the mapping which most closely matches *DevicePath
267 is returned, and *DevicePath is updated to point to the remaining portion of the
268 device path. If there is an exact match, the mapping is returned and *DevicePath
269 points to the end-of-device-path node.
270
271 If there are multiple map names they will be semi-colon seperated in the
272 NULL-terminated string.
273
274 @param DevicePath On entry, points to a device path pointer. On
275 exit, updates the pointer to point to the
276 portion of the device path after the mapping.
277
278 @retval NULL No mapping was found.
279 @return !=NULL Pointer to NULL-terminated mapping. The buffer
280 is callee allocated and should be freed by the caller.
281 **/
282 CONST CHAR16 *
283 EFIAPI
284 EfiShellGetMapFromDevicePath(
285 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
286 )
287 {
288 SHELL_MAP_LIST *Node;
289 CHAR16 *PathForReturn;
290 UINTN PathSize;
291 // EFI_HANDLE PathHandle;
292 // EFI_HANDLE MapHandle;
293 // EFI_STATUS Status;
294 // EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
295 // EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
296
297 if (DevicePath == NULL || *DevicePath == NULL) {
298 return (NULL);
299 }
300
301 PathForReturn = NULL;
302 PathSize = 0;
303
304 for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
305 ; !IsNull(&gShellMapList.Link, &Node->Link)
306 ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
307 ){
308 //
309 // check for exact match
310 //
311 if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
312 ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
313 if (PathSize != 0) {
314 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
315 }
316 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
317 }
318 }
319 if (PathForReturn != NULL) {
320 while (!IsDevicePathEndType (*DevicePath)) {
321 *DevicePath = NextDevicePathNode (*DevicePath);
322 }
323 SetDevicePathEndNode (*DevicePath);
324 }
325 /*
326 ///@todo finish code for inexact matches.
327 if (PathForReturn == NULL) {
328 PathSize = 0;
329
330 DevicePathCopy = DuplicateDevicePath(*DevicePath);
331 ASSERT(DevicePathCopy != NULL);
332 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
333 ASSERT_EFI_ERROR(Status);
334 //
335 // check each of the device paths we have to get the root of the path for consist mappings
336 //
337 for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
338 ; !IsNull(&gShellMapList.Link, &Node->Link)
339 ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
340 ){
341 if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
342 continue;
343 }
344 MapPathCopy = DuplicateDevicePath(Node->DevicePath);
345 ASSERT(MapPathCopy != NULL);
346 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
347 if (MapHandle == PathHandle) {
348
349 *DevicePath = DevicePathCopy;
350
351 MapPathCopy = NULL;
352 DevicePathCopy = NULL;
353 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
354 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
355 break;
356 }
357 }
358 //
359 // now add on the non-consistent mappings
360 //
361 for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
362 ; !IsNull(&gShellMapList.Link, &Node->Link)
363 ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
364 ){
365 if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
366 continue;
367 }
368 MapPathCopy = Node->DevicePath;
369 ASSERT(MapPathCopy != NULL);
370 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
371 if (MapHandle == PathHandle) {
372 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
373 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
374 break;
375 }
376 }
377 }
378 */
379
380 return (AddBufferToFreeList(PathForReturn));
381 }
382
383 /**
384 Converts a device path to a file system-style path.
385
386 This function converts a device path to a file system path by replacing part, or all, of
387 the device path with the file-system mapping. If there are more than one application
388 file system mappings, the one that most closely matches Path will be used.
389
390 @param Path The pointer to the device path
391
392 @retval NULL the device path could not be found.
393 @return all The pointer of the NULL-terminated file path. The path
394 is callee-allocated and should be freed by the caller.
395 **/
396 CHAR16 *
397 EFIAPI
398 EfiShellGetFilePathFromDevicePath(
399 IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
400 )
401 {
402 EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
403 EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
404 SHELL_MAP_LIST *MapListItem;
405 CHAR16 *PathForReturn;
406 UINTN PathSize;
407 EFI_HANDLE PathHandle;
408 EFI_HANDLE MapHandle;
409 EFI_STATUS Status;
410 FILEPATH_DEVICE_PATH *FilePath;
411 FILEPATH_DEVICE_PATH *AlignedNode;
412
413 PathForReturn = NULL;
414 PathSize = 0;
415
416 DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
417 ASSERT(DevicePathCopy != NULL);
418 if (DevicePathCopy == NULL) {
419 return (NULL);
420 }
421 ///@todo BlockIo?
422 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
423
424 if (EFI_ERROR(Status)) {
425 return (NULL);
426 }
427 //
428 // check each of the device paths we have to get the root of the path
429 //
430 for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
431 ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
432 ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
433 ){
434 MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
435 ASSERT(MapPathCopy != NULL);
436 ///@todo BlockIo?
437 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
438 if (MapHandle == PathHandle) {
439 ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
440 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
441 //
442 // go through all the remaining nodes in the device path
443 //
444 for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
445 ; !IsDevicePathEnd (&FilePath->Header)
446 ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
447 ){
448 //
449 // all the rest should be file path nodes
450 //
451 if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
452 (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
453 FreePool(PathForReturn);
454 PathForReturn = NULL;
455 ASSERT(FALSE);
456 } else {
457 //
458 // append the path part onto the filepath.
459 //
460 ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
461
462 AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);
463
464 // File Path Device Path Nodes 'can optionally add a "\" separator to
465 // the beginning and/or the end of the Path Name string.'
466 // (UEFI Spec 2.4 section 9.3.6.4).
467 // If necessary, add a "\", but otherwise don't
468 // (This is specified in the above section, and also implied by the
469 // UEFI Shell spec section 3.7)
470 if ((PathForReturn[PathSize - 1] != L'\\') &&
471 (AlignedNode->PathName[0] != L'\\')) {
472 PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
473 }
474
475 PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);
476 FreePool(AlignedNode);
477 }
478 } // for loop of remaining nodes
479 }
480 if (PathForReturn != NULL) {
481 break;
482 }
483 } // for loop of paths to check
484 return(PathForReturn);
485 }
486
487 /**
488 Converts a file system style name to a device path.
489
490 This function converts a file system style name to a device path, by replacing any
491 mapping references to the associated device path.
492
493 @param[in] Path The pointer to the path.
494
495 @return The pointer of the file path. The file path is callee
496 allocated and should be freed by the caller.
497 @retval NULL The path could not be found.
498 @retval NULL There was not enough available memory.
499 **/
500 EFI_DEVICE_PATH_PROTOCOL *
501 EFIAPI
502 EfiShellGetDevicePathFromFilePath(
503 IN CONST CHAR16 *Path
504 )
505 {
506 CHAR16 *MapName;
507 CHAR16 *NewPath;
508 CONST CHAR16 *Cwd;
509 UINTN Size;
510 CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath;
511 EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
512 EFI_DEVICE_PATH_PROTOCOL *DevicePathCopyForFree;
513 EFI_DEVICE_PATH_PROTOCOL *DevicePathForReturn;
514 EFI_HANDLE Handle;
515 EFI_STATUS Status;
516
517 if (Path == NULL) {
518 return (NULL);
519 }
520
521 MapName = NULL;
522 NewPath = NULL;
523
524 if (StrStr(Path, L":") == NULL) {
525 Cwd = EfiShellGetCurDir(NULL);
526 if (Cwd == NULL) {
527 return (NULL);
528 }
529 Size = StrSize(Cwd);
530 Size += StrSize(Path);
531 NewPath = AllocateZeroPool(Size);
532 if (NewPath == NULL) {
533 return (NULL);
534 }
535 StrCpy(NewPath, Cwd);
536 if (*Path == L'\\') {
537 Path++;
538 while (PathRemoveLastItem(NewPath)) ;
539 }
540 StrCat(NewPath, Path);
541 DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
542 FreePool(NewPath);
543 return (DevicePathForReturn);
544 }
545
546 Size = 0;
547 //
548 // find the part before (but including) the : for the map name
549 //
550 ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
551 MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
552 if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {
553 return (NULL);
554 }
555
556 //
557 // look up the device path in the map
558 //
559 DevicePath = EfiShellGetDevicePathFromMap(MapName);
560 if (DevicePath == NULL) {
561 //
562 // Must have been a bad Mapname
563 //
564 return (NULL);
565 }
566
567 //
568 // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
569 //
570 DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);
571 if (DevicePathCopy == NULL) {
572 FreePool(MapName);
573 return (NULL);
574 }
575
576 //
577 // get the handle
578 //
579 ///@todo BlockIo?
580 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
581 if (EFI_ERROR(Status)) {
582 if (DevicePathCopyForFree != NULL) {
583 FreePool(DevicePathCopyForFree);
584 }
585 FreePool(MapName);
586 return (NULL);
587 }
588
589 //
590 // build the full device path
591 //
592 if (*(Path+StrLen(MapName)+1) == CHAR_NULL) {
593 DevicePathForReturn = FileDevicePath(Handle, L"\\");
594 } else {
595 DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));
596 }
597
598 FreePool(MapName);
599 if (DevicePathCopyForFree != NULL) {
600 FreePool(DevicePathCopyForFree);
601 }
602
603 return (DevicePathForReturn);
604 }
605
606 /**
607 Gets the name of the device specified by the device handle.
608
609 This function gets the user-readable name of the device specified by the device
610 handle. If no user-readable name could be generated, then *BestDeviceName will be
611 NULL and EFI_NOT_FOUND will be returned.
612
613 If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
614 device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
615 DeviceHandle.
616
617 If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
618 device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
619 If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
620 EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
621 EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
622
623 @param DeviceHandle The handle of the device.
624 @param Flags Determines the possible sources of component names.
625 Valid bits are:
626 EFI_DEVICE_NAME_USE_COMPONENT_NAME
627 EFI_DEVICE_NAME_USE_DEVICE_PATH
628 @param Language A pointer to the language specified for the device
629 name, in the same format as described in the UEFI
630 specification, Appendix M
631 @param BestDeviceName On return, points to the callee-allocated NULL-
632 terminated name of the device. If no device name
633 could be found, points to NULL. The name must be
634 freed by the caller...
635
636 @retval EFI_SUCCESS Get the name successfully.
637 @retval EFI_NOT_FOUND Fail to get the device name.
638 @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
639 @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
640 @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
641 **/
642 EFI_STATUS
643 EFIAPI
644 EfiShellGetDeviceName(
645 IN EFI_HANDLE DeviceHandle,
646 IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
647 IN CHAR8 *Language,
648 OUT CHAR16 **BestDeviceName
649 )
650 {
651 EFI_STATUS Status;
652 EFI_COMPONENT_NAME2_PROTOCOL *CompName2;
653 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
654 EFI_HANDLE *HandleList;
655 UINTN HandleCount;
656 UINTN LoopVar;
657 CHAR16 *DeviceNameToReturn;
658 CHAR8 *Lang;
659 UINTN ParentControllerCount;
660 EFI_HANDLE *ParentControllerBuffer;
661 UINTN ParentDriverCount;
662 EFI_HANDLE *ParentDriverBuffer;
663
664 if (BestDeviceName == NULL ||
665 DeviceHandle == NULL
666 ){
667 return (EFI_INVALID_PARAMETER);
668 }
669
670 //
671 // make sure one of the 2 supported bits is on
672 //
673 if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
674 ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {
675 return (EFI_INVALID_PARAMETER);
676 }
677
678 DeviceNameToReturn = NULL;
679 *BestDeviceName = NULL;
680 HandleList = NULL;
681 HandleCount = 0;
682 Lang = NULL;
683
684 if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
685 Status = ParseHandleDatabaseByRelationship(
686 NULL,
687 DeviceHandle,
688 HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
689 &HandleCount,
690 &HandleList);
691 for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){
692 //
693 // Go through those handles until we get one that passes for GetComponentName
694 //
695 Status = gBS->OpenProtocol(
696 HandleList[LoopVar],
697 &gEfiComponentName2ProtocolGuid,
698 (VOID**)&CompName2,
699 gImageHandle,
700 NULL,
701 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
702 if (EFI_ERROR(Status)) {
703 Status = gBS->OpenProtocol(
704 HandleList[LoopVar],
705 &gEfiComponentNameProtocolGuid,
706 (VOID**)&CompName2,
707 gImageHandle,
708 NULL,
709 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
710 }
711
712 if (EFI_ERROR(Status)) {
713 continue;
714 }
715 Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
716 Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
717 FreePool(Lang);
718 Lang = NULL;
719 if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
720 break;
721 }
722 }
723 if (HandleList != NULL) {
724 FreePool(HandleList);
725 }
726
727 //
728 // Now check the parent controller using this as the child.
729 //
730 if (DeviceNameToReturn == NULL){
731 PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
732 for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {
733 PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
734 for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {
735 //
736 // try using that driver's component name with controller and our driver as the child.
737 //
738 Status = gBS->OpenProtocol(
739 ParentDriverBuffer[HandleCount],
740 &gEfiComponentName2ProtocolGuid,
741 (VOID**)&CompName2,
742 gImageHandle,
743 NULL,
744 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
745 if (EFI_ERROR(Status)) {
746 Status = gBS->OpenProtocol(
747 ParentDriverBuffer[HandleCount],
748 &gEfiComponentNameProtocolGuid,
749 (VOID**)&CompName2,
750 gImageHandle,
751 NULL,
752 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
753 }
754
755 if (EFI_ERROR(Status)) {
756 continue;
757 }
758 Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
759 Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
760 FreePool(Lang);
761 Lang = NULL;
762 if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
763 break;
764 }
765
766
767
768 }
769 SHELL_FREE_NON_NULL(ParentDriverBuffer);
770 if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
771 break;
772 }
773 }
774 SHELL_FREE_NON_NULL(ParentControllerBuffer);
775 }
776 //
777 // dont return on fail since we will try device path if that bit is on
778 //
779 if (DeviceNameToReturn != NULL){
780 ASSERT(BestDeviceName != NULL);
781 StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
782 return (EFI_SUCCESS);
783 }
784 }
785 if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
786 Status = gBS->OpenProtocol(
787 DeviceHandle,
788 &gEfiDevicePathProtocolGuid,
789 (VOID**)&DevicePath,
790 gImageHandle,
791 NULL,
792 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
793 if (!EFI_ERROR(Status)) {
794 //
795 // use device path to text on the device path
796 //
797 *BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
798 return (EFI_SUCCESS);
799 }
800 }
801 //
802 // none of the selected bits worked.
803 //
804 return (EFI_NOT_FOUND);
805 }
806
807 /**
808 Opens the root directory of a device on a handle
809
810 This function opens the root directory of a device and returns a file handle to it.
811
812 @param DeviceHandle The handle of the device that contains the volume.
813 @param FileHandle On exit, points to the file handle corresponding to the root directory on the
814 device.
815
816 @retval EFI_SUCCESS Root opened successfully.
817 @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
818 could not be opened.
819 @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
820 @retval EFI_DEVICE_ERROR The device had an error
821 **/
822 EFI_STATUS
823 EFIAPI
824 EfiShellOpenRootByHandle(
825 IN EFI_HANDLE DeviceHandle,
826 OUT SHELL_FILE_HANDLE *FileHandle
827 )
828 {
829 EFI_STATUS Status;
830 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
831 EFI_FILE_PROTOCOL *RealFileHandle;
832 EFI_DEVICE_PATH_PROTOCOL *DevPath;
833
834 //
835 // get the simple file system interface
836 //
837 Status = gBS->OpenProtocol(DeviceHandle,
838 &gEfiSimpleFileSystemProtocolGuid,
839 (VOID**)&SimpleFileSystem,
840 gImageHandle,
841 NULL,
842 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
843 if (EFI_ERROR(Status)) {
844 return (EFI_NOT_FOUND);
845 }
846
847 Status = gBS->OpenProtocol(DeviceHandle,
848 &gEfiDevicePathProtocolGuid,
849 (VOID**)&DevPath,
850 gImageHandle,
851 NULL,
852 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
853 if (EFI_ERROR(Status)) {
854 return (EFI_NOT_FOUND);
855 }
856 //
857 // Open the root volume now...
858 //
859 Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);
860 *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));
861 return (Status);
862 }
863
864 /**
865 Opens the root directory of a device.
866
867 This function opens the root directory of a device and returns a file handle to it.
868
869 @param DevicePath Points to the device path corresponding to the device where the
870 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
871 @param FileHandle On exit, points to the file handle corresponding to the root directory on the
872 device.
873
874 @retval EFI_SUCCESS Root opened successfully.
875 @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
876 could not be opened.
877 @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
878 @retval EFI_DEVICE_ERROR The device had an error
879 @retval EFI_INVALID_PARAMETER FileHandle is NULL.
880 **/
881 EFI_STATUS
882 EFIAPI
883 EfiShellOpenRoot(
884 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
885 OUT SHELL_FILE_HANDLE *FileHandle
886 )
887 {
888 EFI_STATUS Status;
889 EFI_HANDLE Handle;
890
891 if (FileHandle == NULL) {
892 return (EFI_INVALID_PARAMETER);
893 }
894
895 //
896 // find the handle of the device with that device handle and the file system
897 //
898 ///@todo BlockIo?
899 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
900 &DevicePath,
901 &Handle);
902 if (EFI_ERROR(Status)) {
903 return (EFI_NOT_FOUND);
904 }
905
906 return (EfiShellOpenRootByHandle(Handle, FileHandle));
907 }
908
909 /**
910 Returns whether any script files are currently being processed.
911
912 @retval TRUE There is at least one script file active.
913 @retval FALSE No script files are active now.
914
915 **/
916 BOOLEAN
917 EFIAPI
918 EfiShellBatchIsActive (
919 VOID
920 )
921 {
922 if (ShellCommandGetCurrentScriptFile() == NULL) {
923 return (FALSE);
924 }
925 return (TRUE);
926 }
927
928 /**
929 Worker function to open a file based on a device path. this will open the root
930 of the volume and then traverse down to the file itself.
931
932 @param DevicePath Device Path of the file.
933 @param FileHandle Pointer to the file upon a successful return.
934 @param OpenMode mode to open file in.
935 @param Attributes the File Attributes to use when creating a new file.
936
937 @retval EFI_SUCCESS the file is open and FileHandle is valid
938 @retval EFI_UNSUPPORTED the device path cotained non-path elements
939 @retval other an error ocurred.
940 **/
941 EFI_STATUS
942 EFIAPI
943 InternalOpenFileDevicePath(
944 IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
945 OUT SHELL_FILE_HANDLE *FileHandle,
946 IN UINT64 OpenMode,
947 IN UINT64 Attributes OPTIONAL
948 )
949 {
950 EFI_STATUS Status;
951 FILEPATH_DEVICE_PATH *FilePathNode;
952 EFI_HANDLE Handle;
953 SHELL_FILE_HANDLE ShellHandle;
954 EFI_FILE_PROTOCOL *Handle1;
955 EFI_FILE_PROTOCOL *Handle2;
956 FILEPATH_DEVICE_PATH *AlignedNode;
957
958 if (FileHandle == NULL) {
959 return (EFI_INVALID_PARAMETER);
960 }
961 *FileHandle = NULL;
962 Handle1 = NULL;
963 Handle2 = NULL;
964 Handle = NULL;
965 ShellHandle = NULL;
966 FilePathNode = NULL;
967 AlignedNode = NULL;
968
969 Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
970
971 if (!EFI_ERROR(Status)) {
972 Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
973 if (Handle1 != NULL) {
974 //
975 // chop off the begining part before the file system part...
976 //
977 ///@todo BlockIo?
978 Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
979 &DevicePath,
980 &Handle);
981 if (!EFI_ERROR(Status)) {
982 //
983 // To access as a file system, the file path should only
984 // contain file path components. Follow the file path nodes
985 // and find the target file
986 //
987 for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
988 ; !IsDevicePathEnd (&FilePathNode->Header)
989 ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)
990 ){
991 SHELL_FREE_NON_NULL(AlignedNode);
992 AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);
993 //
994 // For file system access each node should be a file path component
995 //
996 if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
997 DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP
998 ) {
999 Status = EFI_UNSUPPORTED;
1000 break;
1001 }
1002
1003 //
1004 // Open this file path node
1005 //
1006 Handle2 = Handle1;
1007 Handle1 = NULL;
1008
1009 //
1010 // if this is the last node in the DevicePath always create (if that was requested).
1011 //
1012 if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
1013 Status = Handle2->Open (
1014 Handle2,
1015 &Handle1,
1016 AlignedNode->PathName,
1017 OpenMode,
1018 Attributes
1019 );
1020 } else {
1021
1022 //
1023 // This is not the last node and we dont want to 'create' existing
1024 // directory entries...
1025 //
1026
1027 //
1028 // open without letting it create
1029 // prevents error on existing files/directories
1030 //
1031 Status = Handle2->Open (
1032 Handle2,
1033 &Handle1,
1034 AlignedNode->PathName,
1035 OpenMode &~EFI_FILE_MODE_CREATE,
1036 Attributes
1037 );
1038 //
1039 // if above failed now open and create the 'item'
1040 // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
1041 //
1042 if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
1043 Status = Handle2->Open (
1044 Handle2,
1045 &Handle1,
1046 AlignedNode->PathName,
1047 OpenMode,
1048 Attributes
1049 );
1050 }
1051 }
1052 //
1053 // Close the last node
1054 //
1055 ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
1056
1057 //
1058 // If there's been an error, stop
1059 //
1060 if (EFI_ERROR (Status)) {
1061 break;
1062 }
1063 } // for loop
1064 }
1065 }
1066 }
1067 SHELL_FREE_NON_NULL(AlignedNode);
1068 if (EFI_ERROR(Status)) {
1069 if (Handle1 != NULL) {
1070 ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);
1071 }
1072 } else {
1073 *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
1074 }
1075 return (Status);
1076 }
1077
1078 /**
1079 Creates a file or directory by name.
1080
1081 This function creates an empty new file or directory with the specified attributes and
1082 returns the new file's handle. If the file already exists and is read-only, then
1083 EFI_INVALID_PARAMETER will be returned.
1084
1085 If the file already existed, it is truncated and its attributes updated. If the file is
1086 created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
1087
1088 If the file name begins with >v, then the file handle which is returned refers to the
1089 shell environment variable with the specified name. If the shell environment variable
1090 already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
1091
1092 @param FileName Pointer to NULL-terminated file path
1093 @param FileAttribs The new file's attrbiutes. the different attributes are
1094 described in EFI_FILE_PROTOCOL.Open().
1095 @param FileHandle On return, points to the created file handle or directory's handle
1096
1097 @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
1098 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
1099 @retval EFI_UNSUPPORTED could not open the file path
1100 @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not
1101 file the file system on the device.
1102 @retval EFI_NO_MEDIA the device has no medium.
1103 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
1104 longer supported.
1105 @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
1106 the DirName.
1107 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
1108 @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
1109 when the media is write-protected.
1110 @retval EFI_ACCESS_DENIED The service denied access to the file.
1111 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
1112 @retval EFI_VOLUME_FULL The volume is full.
1113 **/
1114 EFI_STATUS
1115 EFIAPI
1116 EfiShellCreateFile(
1117 IN CONST CHAR16 *FileName,
1118 IN UINT64 FileAttribs,
1119 OUT SHELL_FILE_HANDLE *FileHandle
1120 )
1121 {
1122 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1123 EFI_STATUS Status;
1124
1125 //
1126 // Is this for an environment variable
1127 // do we start with >v
1128 //
1129 if (StrStr(FileName, L">v") == FileName) {
1130 if (!IsVolatileEnv(FileName+2)) {
1131 return (EFI_INVALID_PARAMETER);
1132 }
1133 *FileHandle = CreateFileInterfaceEnv(FileName+2);
1134 return (EFI_SUCCESS);
1135 }
1136
1137 //
1138 // We are opening a regular file.
1139 //
1140 DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
1141 if (DevicePath == NULL) {
1142 return (EFI_NOT_FOUND);
1143 }
1144
1145 Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs); // 0 = no specific file attributes
1146 FreePool(DevicePath);
1147
1148 return(Status);
1149 }
1150
1151 /**
1152 Opens a file or a directory by file name.
1153
1154 This function opens the specified file in the specified OpenMode and returns a file
1155 handle.
1156 If the file name begins with >v, then the file handle which is returned refers to the
1157 shell environment variable with the specified name. If the shell environment variable
1158 exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
1159 EFI_INVALID_PARAMETER is returned.
1160
1161 If the file name is >i, then the file handle which is returned refers to the standard
1162 input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
1163 is returned.
1164
1165 If the file name is >o, then the file handle which is returned refers to the standard
1166 output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
1167 is returned.
1168
1169 If the file name is >e, then the file handle which is returned refers to the standard
1170 error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
1171 is returned.
1172
1173 If the file name is NUL, then the file handle that is returned refers to the standard NUL
1174 file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
1175 returned.
1176
1177 If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
1178 FileHandle is NULL.
1179
1180 @param FileName Points to the NULL-terminated UCS-2 encoded file name.
1181 @param FileHandle On return, points to the file handle.
1182 @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
1183 EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
1184 Specification.
1185 @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
1186 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
1187 @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
1188 @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
1189 system could not be found on the device. FileHandle is NULL.
1190 @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
1191 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
1192 longer supported. FileHandle is NULL.
1193 @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
1194 the FileName. FileHandle is NULL.
1195 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
1196 @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
1197 when the media is write-protected. FileHandle is NULL.
1198 @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
1199 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
1200 is NULL.
1201 @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
1202 **/
1203 EFI_STATUS
1204 EFIAPI
1205 EfiShellOpenFileByName(
1206 IN CONST CHAR16 *FileName,
1207 OUT SHELL_FILE_HANDLE *FileHandle,
1208 IN UINT64 OpenMode
1209 )
1210 {
1211 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1212 EFI_STATUS Status;
1213
1214 *FileHandle = NULL;
1215
1216 //
1217 // Is this for StdIn
1218 //
1219 if (StrCmp(FileName, L">i") == 0) {
1220 //
1221 // make sure not writing to StdIn
1222 //
1223 if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
1224 return (EFI_INVALID_PARAMETER);
1225 }
1226 *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
1227 ASSERT(*FileHandle != NULL);
1228 return (EFI_SUCCESS);
1229 }
1230
1231 //
1232 // Is this for StdOut
1233 //
1234 if (StrCmp(FileName, L">o") == 0) {
1235 //
1236 // make sure not writing to StdIn
1237 //
1238 if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
1239 return (EFI_INVALID_PARAMETER);
1240 }
1241 *FileHandle = &FileInterfaceStdOut;
1242 return (EFI_SUCCESS);
1243 }
1244
1245 //
1246 // Is this for NUL file
1247 //
1248 if (StrCmp(FileName, L"NUL") == 0) {
1249 *FileHandle = &FileInterfaceNulFile;
1250 return (EFI_SUCCESS);
1251 }
1252
1253 //
1254 // Is this for StdErr
1255 //
1256 if (StrCmp(FileName, L">e") == 0) {
1257 //
1258 // make sure not writing to StdIn
1259 //
1260 if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
1261 return (EFI_INVALID_PARAMETER);
1262 }
1263 *FileHandle = &FileInterfaceStdErr;
1264 return (EFI_SUCCESS);
1265 }
1266
1267 //
1268 // Is this for an environment variable
1269 // do we start with >v
1270 //
1271 if (StrStr(FileName, L">v") == FileName) {
1272 if (!IsVolatileEnv(FileName+2) &&
1273 ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
1274 return (EFI_INVALID_PARAMETER);
1275 }
1276 *FileHandle = CreateFileInterfaceEnv(FileName+2);
1277 return (EFI_SUCCESS);
1278 }
1279
1280 //
1281 // We are opening a regular file.
1282 //
1283 DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
1284 // DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););
1285 if (DevicePath == NULL) {
1286 return (EFI_NOT_FOUND);
1287 }
1288
1289 //
1290 // Copy the device path, open the file, then free the memory
1291 //
1292 Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
1293 FreePool(DevicePath);
1294
1295 return(Status);
1296 }
1297
1298 /**
1299 Deletes the file specified by the file name.
1300
1301 This function deletes a file.
1302
1303 @param FileName Points to the NULL-terminated file name.
1304
1305 @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
1306 @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
1307 @sa EfiShellCreateFile
1308 **/
1309 EFI_STATUS
1310 EFIAPI
1311 EfiShellDeleteFileByName(
1312 IN CONST CHAR16 *FileName
1313 )
1314 {
1315 SHELL_FILE_HANDLE FileHandle;
1316 EFI_STATUS Status;
1317
1318 //
1319 // get a handle to the file
1320 //
1321 Status = EfiShellCreateFile(FileName,
1322 0,
1323 &FileHandle);
1324 if (EFI_ERROR(Status)) {
1325 return (Status);
1326 }
1327 //
1328 // now delete the file
1329 //
1330 return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
1331 }
1332
1333 /**
1334 Disables the page break output mode.
1335 **/
1336 VOID
1337 EFIAPI
1338 EfiShellDisablePageBreak (
1339 VOID
1340 )
1341 {
1342 ShellInfoObject.PageBreakEnabled = FALSE;
1343 }
1344
1345 /**
1346 Enables the page break output mode.
1347 **/
1348 VOID
1349 EFIAPI
1350 EfiShellEnablePageBreak (
1351 VOID
1352 )
1353 {
1354 ShellInfoObject.PageBreakEnabled = TRUE;
1355 }
1356
1357 /**
1358 internal worker function to load and run an image via device path.
1359
1360 @param ParentImageHandle A handle of the image that is executing the specified
1361 command line.
1362 @param DevicePath device path of the file to execute
1363 @param CommandLine Points to the NULL-terminated UCS-2 encoded string
1364 containing the command line. If NULL then the command-
1365 line will be empty.
1366 @param Environment Points to a NULL-terminated array of environment
1367 variables with the format 'x=y', where x is the
1368 environment variable name and y is the value. If this
1369 is NULL, then the current shell environment is used.
1370
1371 @param[out] ExitDataSize ExitDataSize as returned from gBS->StartImage
1372 @param[out] ExitData ExitData as returned from gBS->StartImage
1373
1374 @retval EFI_SUCCESS The command executed successfully. The status code
1375 returned by the command is pointed to by StatusCode.
1376 @retval EFI_INVALID_PARAMETER The parameters are invalid.
1377 @retval EFI_OUT_OF_RESOURCES Out of resources.
1378 @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
1379 **/
1380 EFI_STATUS
1381 EFIAPI
1382 InternalShellExecuteDevicePath(
1383 IN CONST EFI_HANDLE *ParentImageHandle,
1384 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1385 IN CONST CHAR16 *CommandLine OPTIONAL,
1386 IN CONST CHAR16 **Environment OPTIONAL,
1387 OUT UINTN *ExitDataSize OPTIONAL,
1388 OUT CHAR16 **ExitData OPTIONAL
1389 )
1390 {
1391 EFI_STATUS Status;
1392 EFI_STATUS CleanupStatus;
1393 EFI_HANDLE NewHandle;
1394 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
1395 LIST_ENTRY OrigEnvs;
1396 EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
1397 UINTN InternalExitDataSize;
1398 UINTN *ExitDataSizePtr;
1399 CHAR16 *ImagePath;
1400
1401 // ExitDataSize is not OPTIONAL for gBS->BootServices, provide somewhere for
1402 // it to be dumped if the caller doesn't want it.
1403 if (ExitData == NULL) {
1404 ExitDataSizePtr = &InternalExitDataSize;
1405 } else {
1406 ExitDataSizePtr = ExitDataSize;
1407 }
1408
1409 if (ParentImageHandle == NULL) {
1410 return (EFI_INVALID_PARAMETER);
1411 }
1412
1413 InitializeListHead(&OrigEnvs);
1414
1415 NewHandle = NULL;
1416
1417 //
1418 // Load the image with:
1419 // FALSE - not from boot manager and NULL, 0 being not already in memory
1420 //
1421 Status = gBS->LoadImage(
1422 FALSE,
1423 *ParentImageHandle,
1424 (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
1425 NULL,
1426 0,
1427 &NewHandle);
1428
1429 if (EFI_ERROR(Status)) {
1430 if (NewHandle != NULL) {
1431 gBS->UnloadImage(NewHandle);
1432 }
1433 return (Status);
1434 }
1435 Status = gBS->OpenProtocol(
1436 NewHandle,
1437 &gEfiLoadedImageProtocolGuid,
1438 (VOID**)&LoadedImage,
1439 gImageHandle,
1440 NULL,
1441 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
1442
1443 if (!EFI_ERROR(Status)) {
1444 ASSERT(LoadedImage->LoadOptionsSize == 0);
1445 if (CommandLine != NULL) {
1446 LoadedImage->LoadOptionsSize = (UINT32)StrSize(CommandLine);
1447 LoadedImage->LoadOptions = (VOID*)CommandLine;
1448 }
1449
1450 //
1451 // Save our current environment settings for later restoration if necessary
1452 //
1453 if (Environment != NULL) {
1454 Status = GetEnvironmentVariableList(&OrigEnvs);
1455 if (!EFI_ERROR(Status)) {
1456 Status = SetEnvironmentVariables(Environment);
1457 }
1458 }
1459
1460 //
1461 // Initialize and install a shell parameters protocol on the image.
1462 //
1463 ShellParamsProtocol.StdIn = ShellInfoObject.NewShellParametersProtocol->StdIn;
1464 ShellParamsProtocol.StdOut = ShellInfoObject.NewShellParametersProtocol->StdOut;
1465 ShellParamsProtocol.StdErr = ShellInfoObject.NewShellParametersProtocol->StdErr;
1466 Status = UpdateArgcArgv(&ShellParamsProtocol, CommandLine, NULL, NULL);
1467 ASSERT_EFI_ERROR(Status);
1468 //
1469 // Replace Argv[0] with the full path of the binary we're executing:
1470 // If the command line was "foo", the binary might be called "foo.efi".
1471 // "The first entry in [Argv] is always the full file path of the
1472 // executable" - UEFI Shell Spec section 2.3
1473 //
1474 ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
1475 // The image we're executing isn't necessarily in a filesystem - it might
1476 // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
1477 // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
1478 if (ImagePath != NULL) {
1479 if (ShellParamsProtocol.Argv == NULL) {
1480 // Command line was empty or null.
1481 // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
1482 ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
1483 if (ShellParamsProtocol.Argv == NULL) {
1484 Status = EFI_OUT_OF_RESOURCES;
1485 goto Cleanup;
1486 }
1487 ShellParamsProtocol.Argc = 1;
1488 } else {
1489 // Free the string UpdateArgcArgv put in Argv[0];
1490 FreePool (ShellParamsProtocol.Argv[0]);
1491 }
1492 ShellParamsProtocol.Argv[0] = ImagePath;
1493 }
1494
1495 Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
1496 ASSERT_EFI_ERROR(Status);
1497
1498 ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
1499
1500 //
1501 // now start the image, passing up exit data if the caller requested it
1502 //
1503 if (!EFI_ERROR(Status)) {
1504 Status = gBS->StartImage(
1505 NewHandle,
1506 ExitDataSizePtr,
1507 ExitData
1508 );
1509 }
1510
1511 //
1512 // Cleanup (and dont overwrite errors)
1513 //
1514 if (EFI_ERROR(Status)) {
1515 CleanupStatus = gBS->UninstallProtocolInterface(
1516 NewHandle,
1517 &gEfiShellParametersProtocolGuid,
1518 &ShellParamsProtocol
1519 );
1520 ASSERT_EFI_ERROR(CleanupStatus);
1521 } else {
1522 CleanupStatus = gBS->UninstallProtocolInterface(
1523 NewHandle,
1524 &gEfiShellParametersProtocolGuid,
1525 &ShellParamsProtocol
1526 );
1527 ASSERT_EFI_ERROR(CleanupStatus);
1528 }
1529 }
1530
1531 if (!IsListEmpty(&OrigEnvs)) {
1532 if (EFI_ERROR(Status)) {
1533 CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
1534 ASSERT_EFI_ERROR(CleanupStatus);
1535 } else {
1536 CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
1537 ASSERT_EFI_ERROR (CleanupStatus);
1538 }
1539 }
1540
1541 return(Status);
1542 }
1543 /**
1544 Execute the command line.
1545
1546 This function creates a nested instance of the shell and executes the specified
1547 command (CommandLine) with the specified environment (Environment). Upon return,
1548 the status code returned by the specified command is placed in StatusCode.
1549
1550 If Environment is NULL, then the current environment is used and all changes made
1551 by the commands executed will be reflected in the current environment. If the
1552 Environment is non-NULL, then the changes made will be discarded.
1553
1554 The CommandLine is executed from the current working directory on the current
1555 device.
1556
1557 @param ParentImageHandle A handle of the image that is executing the specified
1558 command line.
1559 @param CommandLine Points to the NULL-terminated UCS-2 encoded string
1560 containing the command line. If NULL then the command-
1561 line will be empty.
1562 @param Environment Points to a NULL-terminated array of environment
1563 variables with the format 'x=y', where x is the
1564 environment variable name and y is the value. If this
1565 is NULL, then the current shell environment is used.
1566 @param StatusCode Points to the status code returned by the command.
1567
1568 @retval EFI_SUCCESS The command executed successfully. The status code
1569 returned by the command is pointed to by StatusCode.
1570 @retval EFI_INVALID_PARAMETER The parameters are invalid.
1571 @retval EFI_OUT_OF_RESOURCES Out of resources.
1572 @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
1573 @retval EFI_UNSUPPORTED The support level required for this function is not present.
1574
1575 @sa InternalShellExecuteDevicePath
1576 **/
1577 EFI_STATUS
1578 EFIAPI
1579 EfiShellExecute(
1580 IN EFI_HANDLE *ParentImageHandle,
1581 IN CHAR16 *CommandLine OPTIONAL,
1582 IN CHAR16 **Environment OPTIONAL,
1583 OUT EFI_STATUS *StatusCode OPTIONAL
1584 )
1585 {
1586 EFI_STATUS Status;
1587 CHAR16 *Temp;
1588 EFI_DEVICE_PATH_PROTOCOL *DevPath;
1589 UINTN Size;
1590 UINTN ExitDataSize;
1591 CHAR16 *ExitData;
1592
1593 if ((PcdGet8(PcdShellSupportLevel) < 1)) {
1594 return (EFI_UNSUPPORTED);
1595 }
1596
1597 DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
1598
1599 DEBUG_CODE_BEGIN();
1600 Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
1601 FreePool(Temp);
1602 Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
1603 FreePool(Temp);
1604 Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE);
1605 FreePool(Temp);
1606 DEBUG_CODE_END();
1607
1608 Temp = NULL;
1609 Size = 0;
1610 ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));
1611 StrnCatGrow(&Temp, &Size, L"Shell.efi -_exit ", 0);
1612 StrnCatGrow(&Temp, &Size, CommandLine, 0);
1613
1614 Status = InternalShellExecuteDevicePath(
1615 ParentImageHandle,
1616 DevPath,
1617 Temp,
1618 (CONST CHAR16**)Environment,
1619 &ExitDataSize,
1620 &ExitData);
1621
1622 if (Status == EFI_ABORTED) {
1623 // If the command exited with an error, the shell should put the exit
1624 // status in ExitData, preceded by a null-terminated string.
1625 ASSERT (ExitDataSize == StrSize (ExitData) + sizeof (SHELL_STATUS));
1626
1627 if (StatusCode != NULL) {
1628 // Skip the null-terminated string
1629 ExitData += StrLen (ExitData) + 1;
1630
1631 // Use CopyMem to avoid alignment faults
1632 CopyMem (StatusCode, ExitData, sizeof (SHELL_STATUS));
1633
1634 // Convert from SHELL_STATUS to EFI_STATUS
1635 // EFI_STATUSes have top bit set when they are errors.
1636 // (See UEFI Spec Appendix D)
1637 if (*StatusCode != SHELL_SUCCESS) {
1638 *StatusCode = (EFI_STATUS) *StatusCode | MAX_BIT;
1639 }
1640 }
1641 FreePool (ExitData);
1642 Status = EFI_SUCCESS;
1643 } else if ((StatusCode != NULL) && !EFI_ERROR(Status)) {
1644 *StatusCode = EFI_SUCCESS;
1645 }
1646
1647 //
1648 // de-allocate and return
1649 //
1650 FreePool(DevPath);
1651 FreePool(Temp);
1652 return(Status);
1653 }
1654
1655 /**
1656 Utility cleanup function for EFI_SHELL_FILE_INFO objects.
1657
1658 1) frees all pointers (non-NULL)
1659 2) Closes the SHELL_FILE_HANDLE
1660
1661 @param FileListNode pointer to the list node to free
1662 **/
1663 VOID
1664 EFIAPI
1665 InternalFreeShellFileInfoNode(
1666 IN EFI_SHELL_FILE_INFO *FileListNode
1667 )
1668 {
1669 if (FileListNode->Info != NULL) {
1670 FreePool((VOID*)FileListNode->Info);
1671 }
1672 if (FileListNode->FileName != NULL) {
1673 FreePool((VOID*)FileListNode->FileName);
1674 }
1675 if (FileListNode->FullName != NULL) {
1676 FreePool((VOID*)FileListNode->FullName);
1677 }
1678 if (FileListNode->Handle != NULL) {
1679 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
1680 }
1681 FreePool(FileListNode);
1682 }
1683 /**
1684 Frees the file list.
1685
1686 This function cleans up the file list and any related data structures. It has no
1687 impact on the files themselves.
1688
1689 @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
1690 defined in OpenFileList()
1691
1692 @retval EFI_SUCCESS Free the file list successfully.
1693 @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
1694 **/
1695 EFI_STATUS
1696 EFIAPI
1697 EfiShellFreeFileList(
1698 IN EFI_SHELL_FILE_INFO **FileList
1699 )
1700 {
1701 EFI_SHELL_FILE_INFO *ShellFileListItem;
1702
1703 if (FileList == NULL || *FileList == NULL) {
1704 return (EFI_INVALID_PARAMETER);
1705 }
1706
1707 for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
1708 ; !IsListEmpty(&(*FileList)->Link)
1709 ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
1710 ){
1711 RemoveEntryList(&ShellFileListItem->Link);
1712 InternalFreeShellFileInfoNode(ShellFileListItem);
1713 }
1714 InternalFreeShellFileInfoNode(*FileList);
1715 *FileList = NULL;
1716 return(EFI_SUCCESS);
1717 }
1718
1719 /**
1720 Deletes the duplicate file names files in the given file list.
1721
1722 This function deletes the reduplicate files in the given file list.
1723
1724 @param FileList A pointer to the first entry in the file list.
1725
1726 @retval EFI_SUCCESS Always success.
1727 @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
1728 **/
1729 EFI_STATUS
1730 EFIAPI
1731 EfiShellRemoveDupInFileList(
1732 IN EFI_SHELL_FILE_INFO **FileList
1733 )
1734 {
1735 EFI_SHELL_FILE_INFO *ShellFileListItem;
1736 EFI_SHELL_FILE_INFO *ShellFileListItem2;
1737 EFI_SHELL_FILE_INFO *TempNode;
1738
1739 if (FileList == NULL || *FileList == NULL) {
1740 return (EFI_INVALID_PARAMETER);
1741 }
1742 for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
1743 ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
1744 ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
1745 ){
1746 for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
1747 ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)
1748 ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)
1749 ){
1750 if (gUnicodeCollation->StriColl(
1751 gUnicodeCollation,
1752 (CHAR16*)ShellFileListItem->FullName,
1753 (CHAR16*)ShellFileListItem2->FullName) == 0
1754 ){
1755 TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode(
1756 &(*FileList)->Link,
1757 &ShellFileListItem2->Link
1758 );
1759 RemoveEntryList(&ShellFileListItem2->Link);
1760 InternalFreeShellFileInfoNode(ShellFileListItem2);
1761 // Set ShellFileListItem2 to PreviousNode so we don't access Freed
1762 // memory in GetNextNode in the loop expression above.
1763 ShellFileListItem2 = TempNode;
1764 }
1765 }
1766 }
1767 return (EFI_SUCCESS);
1768 }
1769 /**
1770 Allocates and duplicates a EFI_SHELL_FILE_INFO node.
1771
1772 @param[in] Node The node to copy from.
1773 @param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise.
1774
1775 @retval NULL a memory allocation error ocurred
1776 @return != NULL a pointer to the new node
1777 **/
1778 EFI_SHELL_FILE_INFO*
1779 EFIAPI
1780 InternalDuplicateShellFileInfo(
1781 IN EFI_SHELL_FILE_INFO *Node,
1782 IN BOOLEAN Save
1783 )
1784 {
1785 EFI_SHELL_FILE_INFO *NewNode;
1786
1787 NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1788 if (NewNode == NULL) {
1789 return (NULL);
1790 }
1791 NewNode->FullName = AllocateZeroPool(StrSize(Node->FullName));
1792
1793 NewNode->FileName = AllocateZeroPool(StrSize(Node->FileName));
1794 NewNode->Info = AllocateZeroPool((UINTN)Node->Info->Size);
1795 if ( NewNode->FullName == NULL
1796 || NewNode->FileName == NULL
1797 || NewNode->Info == NULL
1798 ){
1799 return(NULL);
1800 }
1801 NewNode->Status = Node->Status;
1802 NewNode->Handle = Node->Handle;
1803 if (!Save) {
1804 Node->Handle = NULL;
1805 }
1806 StrCpy((CHAR16*)NewNode->FullName, Node->FullName);
1807 StrCpy((CHAR16*)NewNode->FileName, Node->FileName);
1808 CopyMem(NewNode->Info, Node->Info, (UINTN)Node->Info->Size);
1809
1810 return(NewNode);
1811 }
1812
1813 /**
1814 Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation
1815 failed it will return NULL.
1816
1817 @param[in] BasePath the Path to prepend onto filename for FullPath
1818 @param[in] Status Status member initial value.
1819 @param[in] FileName FileName member initial value.
1820 @param[in] Handle Handle member initial value.
1821 @param[in] Info Info struct to copy.
1822
1823 @retval NULL An error ocurred.
1824 @return a pointer to the newly allocated structure.
1825 **/
1826 EFI_SHELL_FILE_INFO *
1827 EFIAPI
1828 CreateAndPopulateShellFileInfo(
1829 IN CONST CHAR16 *BasePath,
1830 IN CONST EFI_STATUS Status,
1831 IN CONST CHAR16 *FileName,
1832 IN CONST SHELL_FILE_HANDLE Handle,
1833 IN CONST EFI_FILE_INFO *Info
1834 )
1835 {
1836 EFI_SHELL_FILE_INFO *ShellFileListItem;
1837 CHAR16 *TempString;
1838 UINTN Size;
1839
1840 TempString = NULL;
1841 Size = 0;
1842
1843 ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1844 if (ShellFileListItem == NULL) {
1845 return (NULL);
1846 }
1847 if (Info != NULL && Info->Size != 0) {
1848 ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);
1849 if (ShellFileListItem->Info == NULL) {
1850 FreePool(ShellFileListItem);
1851 return (NULL);
1852 }
1853 CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);
1854 } else {
1855 ShellFileListItem->Info = NULL;
1856 }
1857 if (FileName != NULL) {
1858 ASSERT(TempString == NULL);
1859 ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);
1860 if (ShellFileListItem->FileName == NULL) {
1861 FreePool(ShellFileListItem->Info);
1862 FreePool(ShellFileListItem);
1863 return (NULL);
1864 }
1865 } else {
1866 ShellFileListItem->FileName = NULL;
1867 }
1868 Size = 0;
1869 TempString = NULL;
1870 if (BasePath != NULL) {
1871 ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
1872 TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
1873 if (TempString == NULL) {
1874 FreePool((VOID*)ShellFileListItem->FileName);
1875 FreePool(ShellFileListItem->Info);
1876 FreePool(ShellFileListItem);
1877 return (NULL);
1878 }
1879 }
1880 if (ShellFileListItem->FileName != NULL) {
1881 ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
1882 TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);
1883 if (TempString == NULL) {
1884 FreePool((VOID*)ShellFileListItem->FileName);
1885 FreePool(ShellFileListItem->Info);
1886 FreePool(ShellFileListItem);
1887 return (NULL);
1888 }
1889 }
1890
1891 ShellFileListItem->FullName = TempString;
1892 ShellFileListItem->Status = Status;
1893 ShellFileListItem->Handle = Handle;
1894
1895 return (ShellFileListItem);
1896 }
1897
1898 /**
1899 Find all files in a specified directory.
1900
1901 @param FileDirHandle Handle of the directory to search.
1902 @param FileList On return, points to the list of files in the directory
1903 or NULL if there are no files in the directory.
1904
1905 @retval EFI_SUCCESS File information was returned successfully.
1906 @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted.
1907 @retval EFI_DEVICE_ERROR The device reported an error.
1908 @retval EFI_NO_MEDIA The device media is not present.
1909 @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
1910 @return An error from FileHandleGetFileName().
1911 **/
1912 EFI_STATUS
1913 EFIAPI
1914 EfiShellFindFilesInDir(
1915 IN SHELL_FILE_HANDLE FileDirHandle,
1916 OUT EFI_SHELL_FILE_INFO **FileList
1917 )
1918 {
1919 EFI_SHELL_FILE_INFO *ShellFileList;
1920 EFI_SHELL_FILE_INFO *ShellFileListItem;
1921 EFI_FILE_INFO *FileInfo;
1922 EFI_STATUS Status;
1923 BOOLEAN NoFile;
1924 CHAR16 *TempString;
1925 CHAR16 *BasePath;
1926 UINTN Size;
1927 CHAR16 *TempSpot;
1928
1929 Status = FileHandleGetFileName(FileDirHandle, &BasePath);
1930 if (EFI_ERROR(Status)) {
1931 return (Status);
1932 }
1933
1934 if (ShellFileHandleGetPath(FileDirHandle) != NULL) {
1935 TempString = NULL;
1936 Size = 0;
1937 TempString = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);
1938 if (TempString == NULL) {
1939 SHELL_FREE_NON_NULL(BasePath);
1940 return (EFI_OUT_OF_RESOURCES);
1941 }
1942 TempSpot = StrStr(TempString, L";");
1943
1944 if (TempSpot != NULL) {
1945 *TempSpot = CHAR_NULL;
1946 }
1947
1948 TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
1949 if (TempString == NULL) {
1950 SHELL_FREE_NON_NULL(BasePath);
1951 return (EFI_OUT_OF_RESOURCES);
1952 }
1953 SHELL_FREE_NON_NULL(BasePath);
1954 BasePath = TempString;
1955 }
1956
1957 NoFile = FALSE;
1958 ShellFileList = NULL;
1959 ShellFileListItem = NULL;
1960 FileInfo = NULL;
1961 Status = EFI_SUCCESS;
1962
1963
1964 for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)
1965 ; !EFI_ERROR(Status) && !NoFile
1966 ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)
1967 ){
1968 //
1969 // allocate a new EFI_SHELL_FILE_INFO and populate it...
1970 //
1971 ShellFileListItem = CreateAndPopulateShellFileInfo(
1972 BasePath,
1973 EFI_SUCCESS, // success since we didnt fail to open it...
1974 FileInfo->FileName,
1975 NULL, // no handle since not open
1976 FileInfo);
1977
1978 if (ShellFileList == NULL) {
1979 ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1980 ASSERT(ShellFileList != NULL);
1981 InitializeListHead(&ShellFileList->Link);
1982 }
1983 InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
1984 }
1985 if (EFI_ERROR(Status)) {
1986 EfiShellFreeFileList(&ShellFileList);
1987 *FileList = NULL;
1988 } else {
1989 *FileList = ShellFileList;
1990 }
1991 SHELL_FREE_NON_NULL(BasePath);
1992 return(Status);
1993 }
1994
1995 /**
1996 Updates a file name to be preceeded by the mapped drive name
1997
1998 @param[in] BasePath the Mapped drive name to prepend
1999 @param[in, out] Path pointer to pointer to the file name to update.
2000
2001 @retval EFI_SUCCESS
2002 @retval EFI_OUT_OF_RESOURCES
2003 **/
2004 EFI_STATUS
2005 EFIAPI
2006 UpdateFileName(
2007 IN CONST CHAR16 *BasePath,
2008 IN OUT CHAR16 **Path
2009 )
2010 {
2011 CHAR16 *Path2;
2012 UINTN Path2Size;
2013
2014 Path2Size = 0;
2015 Path2 = NULL;
2016
2017 ASSERT(Path != NULL);
2018 ASSERT(*Path != NULL);
2019 ASSERT(BasePath != NULL);
2020
2021 //
2022 // convert a local path to an absolute path
2023 //
2024 if (StrStr(*Path, L":") == NULL) {
2025 ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
2026 StrnCatGrow(&Path2, &Path2Size, BasePath, 0);
2027 if (Path2 == NULL) {
2028 return (EFI_OUT_OF_RESOURCES);
2029 }
2030 ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
2031 StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);
2032 if (Path2 == NULL) {
2033 return (EFI_OUT_OF_RESOURCES);
2034 }
2035 }
2036
2037 FreePool(*Path);
2038 (*Path) = Path2;
2039
2040 return (EFI_SUCCESS);
2041 }
2042
2043 /**
2044 If FileHandle is a directory then the function reads from FileHandle and reads in
2045 each of the FileInfo structures. If one of them matches the Pattern's first
2046 "level" then it opens that handle and calls itself on that handle.
2047
2048 If FileHandle is a file and matches all of the remaining Pattern (which would be
2049 on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
2050
2051 Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
2052 FreeFileList with FileList.
2053
2054 @param[in] FilePattern The FilePattern to check against.
2055 @param[in] UnicodeCollation The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
2056 @param[in] FileHandle The FileHandle to start with
2057 @param[in, out] FileList pointer to pointer to list of found files.
2058 @param[in] ParentNode The node for the parent. Same file as identified by HANDLE.
2059 @param[in] MapName The file system name this file is on.
2060
2061 @retval EFI_SUCCESS all files were found and the FileList contains a list.
2062 @retval EFI_NOT_FOUND no files were found
2063 @retval EFI_OUT_OF_RESOURCES a memory allocation failed
2064 **/
2065 EFI_STATUS
2066 EFIAPI
2067 ShellSearchHandle(
2068 IN CONST CHAR16 *FilePattern,
2069 IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
2070 IN SHELL_FILE_HANDLE FileHandle,
2071 IN OUT EFI_SHELL_FILE_INFO **FileList,
2072 IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL,
2073 IN CONST CHAR16 *MapName
2074 )
2075 {
2076 EFI_STATUS Status;
2077 CONST CHAR16 *NextFilePatternStart;
2078 CHAR16 *CurrentFilePattern;
2079 EFI_SHELL_FILE_INFO *ShellInfo;
2080 EFI_SHELL_FILE_INFO *ShellInfoNode;
2081 EFI_SHELL_FILE_INFO *NewShellNode;
2082 BOOLEAN Directory;
2083 CHAR16 *NewFullName;
2084 UINTN Size;
2085
2086 if ( FilePattern == NULL
2087 || UnicodeCollation == NULL
2088 || FileList == NULL
2089 ){
2090 return (EFI_INVALID_PARAMETER);
2091 }
2092 ShellInfo = NULL;
2093 CurrentFilePattern = NULL;
2094
2095 if (*FilePattern == L'\\') {
2096 FilePattern++;
2097 }
2098
2099 for( NextFilePatternStart = FilePattern
2100 ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
2101 ; NextFilePatternStart++);
2102
2103 CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
2104 ASSERT(CurrentFilePattern != NULL);
2105 StrnCpy(CurrentFilePattern, FilePattern, NextFilePatternStart-FilePattern);
2106
2107 if (CurrentFilePattern[0] == CHAR_NULL
2108 &&NextFilePatternStart[0] == CHAR_NULL
2109 ){
2110 //
2111 // Add the current parameter FileHandle to the list, then end...
2112 //
2113 if (ParentNode == NULL) {
2114 Status = EFI_INVALID_PARAMETER;
2115 } else {
2116 NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
2117 if (NewShellNode == NULL) {
2118 Status = EFI_OUT_OF_RESOURCES;
2119 } else {
2120 NewShellNode->Handle = NULL;
2121 if (*FileList == NULL) {
2122 *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
2123 InitializeListHead(&((*FileList)->Link));
2124 }
2125
2126 //
2127 // Add to the returning to use list
2128 //
2129 InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
2130
2131 Status = EFI_SUCCESS;
2132 }
2133 }
2134 } else {
2135 Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);
2136
2137 if (!EFI_ERROR(Status)){
2138 if (StrStr(NextFilePatternStart, L"\\") != NULL){
2139 Directory = TRUE;
2140 } else {
2141 Directory = FALSE;
2142 }
2143 for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)
2144 ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
2145 ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)
2146 ){
2147 if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){
2148 if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {
2149 Size = StrSize(ShellInfoNode->FullName);
2150 Size += StrSize(MapName) + sizeof(CHAR16);
2151 NewFullName = AllocateZeroPool(Size);
2152 if (NewFullName == NULL) {
2153 Status = EFI_OUT_OF_RESOURCES;
2154 } else {
2155 StrCpy(NewFullName, MapName);
2156 StrCat(NewFullName, ShellInfoNode->FullName+1);
2157 FreePool((VOID*)ShellInfoNode->FullName);
2158 ShellInfoNode->FullName = NewFullName;
2159 }
2160 }
2161 if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){
2162 //
2163 // should be a directory
2164 //
2165
2166 //
2167 // don't open the . and .. directories
2168 //
2169 if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
2170 && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
2171 ){
2172 //
2173 //
2174 //
2175 if (EFI_ERROR(Status)) {
2176 break;
2177 }
2178 //
2179 // Open the directory since we need that handle in the next recursion.
2180 //
2181 ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
2182
2183 //
2184 // recurse with the next part of the pattern
2185 //
2186 Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
2187 }
2188 } else if (!EFI_ERROR(Status)) {
2189 //
2190 // should be a file
2191 //
2192
2193 //
2194 // copy the information we need into a new Node
2195 //
2196 NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
2197 ASSERT(NewShellNode != NULL);
2198 if (NewShellNode == NULL) {
2199 Status = EFI_OUT_OF_RESOURCES;
2200 }
2201 if (*FileList == NULL) {
2202 *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
2203 InitializeListHead(&((*FileList)->Link));
2204 }
2205
2206 //
2207 // Add to the returning to use list
2208 //
2209 InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
2210 }
2211 }
2212 if (EFI_ERROR(Status)) {
2213 break;
2214 }
2215 }
2216 if (EFI_ERROR(Status)) {
2217 EfiShellFreeFileList(&ShellInfo);
2218 } else {
2219 Status = EfiShellFreeFileList(&ShellInfo);
2220 }
2221 }
2222 }
2223
2224 FreePool(CurrentFilePattern);
2225 return (Status);
2226 }
2227
2228 /**
2229 Find files that match a specified pattern.
2230
2231 This function searches for all files and directories that match the specified
2232 FilePattern. The FilePattern can contain wild-card characters. The resulting file
2233 information is placed in the file list FileList.
2234
2235 Wildcards are processed
2236 according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
2237
2238 The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
2239 field is set to NULL.
2240
2241 if *FileList is not NULL then it must be a pre-existing and properly initialized list.
2242
2243 @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
2244 @param FileList On return, points to the start of a file list containing the names
2245 of all matching files or else points to NULL if no matching files
2246 were found. only on a EFI_SUCCESS return will; this be non-NULL.
2247
2248 @retval EFI_SUCCESS Files found. FileList is a valid list.
2249 @retval EFI_NOT_FOUND No files found.
2250 @retval EFI_NO_MEDIA The device has no media
2251 @retval EFI_DEVICE_ERROR The device reported an error
2252 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
2253 **/
2254 EFI_STATUS
2255 EFIAPI
2256 EfiShellFindFiles(
2257 IN CONST CHAR16 *FilePattern,
2258 OUT EFI_SHELL_FILE_INFO **FileList
2259 )
2260 {
2261 EFI_STATUS Status;
2262 CHAR16 *PatternCopy;
2263 CHAR16 *PatternCurrentLocation;
2264 EFI_DEVICE_PATH_PROTOCOL *RootDevicePath;
2265 SHELL_FILE_HANDLE RootFileHandle;
2266 CHAR16 *MapName;
2267 UINTN Count;
2268
2269 if ( FilePattern == NULL
2270 || FileList == NULL
2271 || StrStr(FilePattern, L":") == NULL
2272 ){
2273 return (EFI_INVALID_PARAMETER);
2274 }
2275 Status = EFI_SUCCESS;
2276 RootDevicePath = NULL;
2277 RootFileHandle = NULL;
2278 MapName = NULL;
2279 PatternCopy = AllocateZeroPool(StrSize(FilePattern));
2280 if (PatternCopy == NULL) {
2281 return (EFI_OUT_OF_RESOURCES);
2282 }
2283 StrCpy(PatternCopy, FilePattern);
2284
2285 PatternCopy = PathCleanUpDirectories(PatternCopy);
2286
2287 Count = StrStr(PatternCopy, L":") - PatternCopy;
2288 Count += 2;
2289
2290 ASSERT(MapName == NULL);
2291 MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
2292 if (MapName == NULL) {
2293 Status = EFI_OUT_OF_RESOURCES;
2294 } else {
2295 RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);
2296 if (RootDevicePath == NULL) {
2297 Status = EFI_INVALID_PARAMETER;
2298 } else {
2299 Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);
2300 if (!EFI_ERROR(Status)) {
2301 for ( PatternCurrentLocation = PatternCopy
2302 ; *PatternCurrentLocation != ':'
2303 ; PatternCurrentLocation++);
2304 PatternCurrentLocation++;
2305 Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
2306 }
2307 FreePool(RootDevicePath);
2308 }
2309 }
2310
2311 SHELL_FREE_NON_NULL(PatternCopy);
2312 SHELL_FREE_NON_NULL(MapName);
2313
2314 return(Status);
2315 }
2316
2317 /**
2318 Opens the files that match the path specified.
2319
2320 This function opens all of the files specified by Path. Wildcards are processed
2321 according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
2322 matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
2323
2324 @param Path A pointer to the path string.
2325 @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or
2326 EFI_FILE_MODE_WRITE.
2327 @param FileList Points to the start of a list of files opened.
2328
2329 @retval EFI_SUCCESS Create the file list successfully.
2330 @return Others Can't create the file list.
2331 **/
2332 EFI_STATUS
2333 EFIAPI
2334 EfiShellOpenFileList(
2335 IN CHAR16 *Path,
2336 IN UINT64 OpenMode,
2337 IN OUT EFI_SHELL_FILE_INFO **FileList
2338 )
2339 {
2340 EFI_STATUS Status;
2341 EFI_SHELL_FILE_INFO *ShellFileListItem;
2342 CHAR16 *Path2;
2343 UINTN Path2Size;
2344 CONST CHAR16 *CurDir;
2345 BOOLEAN Found;
2346
2347 PathCleanUpDirectories(Path);
2348
2349 Path2Size = 0;
2350 Path2 = NULL;
2351
2352 if (FileList == NULL || *FileList == NULL) {
2353 return (EFI_INVALID_PARAMETER);
2354 }
2355
2356 if (*Path == L'.' && *(Path+1) == L'\\') {
2357 Path+=2;
2358 }
2359
2360 //
2361 // convert a local path to an absolute path
2362 //
2363 if (StrStr(Path, L":") == NULL) {
2364 CurDir = EfiShellGetCurDir(NULL);
2365 ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
2366 StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
2367 if (*Path == L'\\') {
2368 Path++;
2369 while (PathRemoveLastItem(Path2)) ;
2370 }
2371 ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
2372 StrnCatGrow(&Path2, &Path2Size, Path, 0);
2373 } else {
2374 ASSERT(Path2 == NULL);
2375 StrnCatGrow(&Path2, NULL, Path, 0);
2376 }
2377
2378 PathCleanUpDirectories (Path2);
2379
2380 //
2381 // do the search
2382 //
2383 Status = EfiShellFindFiles(Path2, FileList);
2384
2385 FreePool(Path2);
2386
2387 if (EFI_ERROR(Status)) {
2388 return (Status);
2389 }
2390
2391 Found = FALSE;
2392 //
2393 // We had no errors so open all the files (that are not already opened...)
2394 //
2395 for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
2396 ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
2397 ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
2398 ){
2399 if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {
2400 ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
2401 Found = TRUE;
2402 }
2403 }
2404
2405 if (!Found) {
2406 return (EFI_NOT_FOUND);
2407 }
2408 return(EFI_SUCCESS);
2409 }
2410
2411 /**
2412 This function updated with errata.
2413
2414 Gets either a single or list of environment variables.
2415
2416 If name is not NULL then this function returns the current value of the specified
2417 environment variable.
2418
2419 If Name is NULL, then a list of all environment variable names is returned. Each is a
2420 NULL terminated string with a double NULL terminating the list.
2421
2422 @param Name A pointer to the environment variable name. If
2423 Name is NULL, then the function will return all
2424 of the defined shell environment variables. In
2425 the case where multiple environment variables are
2426 being returned, each variable will be terminated by
2427 a NULL, and the list will be terminated by a double
2428 NULL.
2429
2430 @return !=NULL A pointer to the returned string.
2431 The returned pointer does not need to be freed by the caller.
2432
2433 @retval NULL The environment variable doesn't exist or there are
2434 no environment variables.
2435 **/
2436 CONST CHAR16 *
2437 EFIAPI
2438 EfiShellGetEnv(
2439 IN CONST CHAR16 *Name
2440 )
2441 {
2442 EFI_STATUS Status;
2443 VOID *Buffer;
2444 UINTN Size;
2445 LIST_ENTRY List;
2446 ENV_VAR_LIST *Node;
2447 CHAR16 *CurrentWriteLocation;
2448
2449 Size = 0;
2450 Buffer = NULL;
2451
2452 if (Name == NULL) {
2453 //
2454 // Get all our environment variables
2455 //
2456 InitializeListHead(&List);
2457 Status = GetEnvironmentVariableList(&List);
2458 if (EFI_ERROR(Status)){
2459 return (NULL);
2460 }
2461
2462 //
2463 // Build the semi-colon delimited list. (2 passes)
2464 //
2465 for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
2466 ; !IsNull(&List, &Node->Link)
2467 ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
2468 ){
2469 ASSERT(Node->Key != NULL);
2470 Size += StrSize(Node->Key);
2471 }
2472
2473 Size += 2*sizeof(CHAR16);
2474
2475 Buffer = AllocateZeroPool(Size);
2476 if (Buffer == NULL) {
2477 if (!IsListEmpty (&List)) {
2478 FreeEnvironmentVariableList(&List);
2479 }
2480 return (NULL);
2481 }
2482 CurrentWriteLocation = (CHAR16*)Buffer;
2483
2484 for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
2485 ; !IsNull(&List, &Node->Link)
2486 ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
2487 ){
2488 ASSERT(Node->Key != NULL);
2489 StrCpy(CurrentWriteLocation, Node->Key);
2490 CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
2491 }
2492
2493 //
2494 // Free the list...
2495 //
2496 if (!IsListEmpty (&List)) {
2497 FreeEnvironmentVariableList(&List);
2498 }
2499 } else {
2500 //
2501 // We are doing a specific environment variable
2502 //
2503
2504 //
2505 // get the size we need for this EnvVariable
2506 //
2507 Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);
2508 if (Status == EFI_BUFFER_TOO_SMALL) {
2509 //
2510 // Allocate the space and recall the get function
2511 //
2512 Buffer = AllocateZeroPool(Size);
2513 ASSERT(Buffer != NULL);
2514 Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);
2515 }
2516 //
2517 // we didnt get it (might not exist)
2518 // free the memory if we allocated any and return NULL
2519 //
2520 if (EFI_ERROR(Status)) {
2521 if (Buffer != NULL) {
2522 FreePool(Buffer);
2523 }
2524 return (NULL);
2525 }
2526 }
2527
2528 //
2529 // return the buffer
2530 //
2531 return (AddBufferToFreeList(Buffer));
2532 }
2533
2534 /**
2535 Internal variable setting function. Allows for setting of the read only variables.
2536
2537 @param Name Points to the NULL-terminated environment variable name.
2538 @param Value Points to the NULL-terminated environment variable value. If the value is an
2539 empty string then the environment variable is deleted.
2540 @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
2541
2542 @retval EFI_SUCCESS The environment variable was successfully updated.
2543 **/
2544 EFI_STATUS
2545 EFIAPI
2546 InternalEfiShellSetEnv(
2547 IN CONST CHAR16 *Name,
2548 IN CONST CHAR16 *Value,
2549 IN BOOLEAN Volatile
2550 )
2551 {
2552 if (Value == NULL || StrLen(Value) == 0) {
2553 return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name));
2554 } else {
2555 SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
2556 if (Volatile) {
2557 return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value));
2558 } else {
2559 return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value));
2560 }
2561 }
2562 }
2563
2564 /**
2565 Sets the environment variable.
2566
2567 This function changes the current value of the specified environment variable. If the
2568 environment variable exists and the Value is an empty string, then the environment
2569 variable is deleted. If the environment variable exists and the Value is not an empty
2570 string, then the value of the environment variable is changed. If the environment
2571 variable does not exist and the Value is an empty string, there is no action. If the
2572 environment variable does not exist and the Value is a non-empty string, then the
2573 environment variable is created and assigned the specified value.
2574
2575 For a description of volatile and non-volatile environment variables, see UEFI Shell
2576 2.0 specification section 3.6.1.
2577
2578 @param Name Points to the NULL-terminated environment variable name.
2579 @param Value Points to the NULL-terminated environment variable value. If the value is an
2580 empty string then the environment variable is deleted.
2581 @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
2582
2583 @retval EFI_SUCCESS The environment variable was successfully updated.
2584 **/
2585 EFI_STATUS
2586 EFIAPI
2587 EfiShellSetEnv(
2588 IN CONST CHAR16 *Name,
2589 IN CONST CHAR16 *Value,
2590 IN BOOLEAN Volatile
2591 )
2592 {
2593 if (Name == NULL || *Name == CHAR_NULL) {
2594 return (EFI_INVALID_PARAMETER);
2595 }
2596 //
2597 // Make sure we dont 'set' a predefined read only variable
2598 //
2599 if (gUnicodeCollation->StriColl(
2600 gUnicodeCollation,
2601 (CHAR16*)Name,
2602 L"cwd") == 0
2603 ||gUnicodeCollation->StriColl(
2604 gUnicodeCollation,
2605 (CHAR16*)Name,
2606 L"Lasterror") == 0
2607 ||gUnicodeCollation->StriColl(
2608 gUnicodeCollation,
2609 (CHAR16*)Name,
2610 L"profiles") == 0
2611 ||gUnicodeCollation->StriColl(
2612 gUnicodeCollation,
2613 (CHAR16*)Name,
2614 L"uefishellsupport") == 0
2615 ||gUnicodeCollation->StriColl(
2616 gUnicodeCollation,
2617 (CHAR16*)Name,
2618 L"uefishellversion") == 0
2619 ||gUnicodeCollation->StriColl(
2620 gUnicodeCollation,
2621 (CHAR16*)Name,
2622 L"uefiversion") == 0
2623 ){
2624 return (EFI_INVALID_PARAMETER);
2625 }
2626 return (InternalEfiShellSetEnv(Name, Value, Volatile));
2627 }
2628
2629 /**
2630 Returns the current directory on the specified device.
2631
2632 If FileSystemMapping is NULL, it returns the current working directory. If the
2633 FileSystemMapping is not NULL, it returns the current directory associated with the
2634 FileSystemMapping. In both cases, the returned name includes the file system
2635 mapping (i.e. fs0:\current-dir).
2636
2637 @param FileSystemMapping A pointer to the file system mapping. If NULL,
2638 then the current working directory is returned.
2639
2640 @retval !=NULL The current directory.
2641 @retval NULL Current directory does not exist.
2642 **/
2643 CONST CHAR16 *
2644 EFIAPI
2645 EfiShellGetCurDir(
2646 IN CONST CHAR16 *FileSystemMapping OPTIONAL
2647 )
2648 {
2649 CHAR16 *PathToReturn;
2650 UINTN Size;
2651 SHELL_MAP_LIST *MapListItem;
2652 if (!IsListEmpty(&gShellMapList.Link)) {
2653 //
2654 // if parameter is NULL, use current
2655 //
2656 if (FileSystemMapping == NULL) {
2657 return (EfiShellGetEnv(L"cwd"));
2658 } else {
2659 Size = 0;
2660 PathToReturn = NULL;
2661 MapListItem = ShellCommandFindMapItem(FileSystemMapping);
2662 if (MapListItem != NULL) {
2663 ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
2664 PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
2665 PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
2666 }
2667 }
2668 return (AddBufferToFreeList(PathToReturn));
2669 } else {
2670 return (NULL);
2671 }
2672 }
2673
2674 /**
2675 Changes the current directory on the specified device.
2676
2677 If the FileSystem is NULL, and the directory Dir does not contain a file system's
2678 mapped name, this function changes the current working directory.
2679
2680 If the FileSystem is NULL and the directory Dir contains a mapped name, then the
2681 current file system and the current directory on that file system are changed.
2682
2683 If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
2684 system.
2685
2686 If FileSystem is not NULL and Dir is not NULL, then this function changes the current
2687 directory on the specified file system.
2688
2689 If the current working directory or the current working file system is changed then the
2690 %cwd% environment variable will be updated
2691
2692 @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
2693 directory is changed.
2694 @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
2695
2696 @retval EFI_SUCCESS The operation was sucessful
2697 @retval EFI_NOT_FOUND The file system could not be found
2698 **/
2699 EFI_STATUS
2700 EFIAPI
2701 EfiShellSetCurDir(
2702 IN CONST CHAR16 *FileSystem OPTIONAL,
2703 IN CONST CHAR16 *Dir
2704 )
2705 {
2706 CHAR16 *MapName;
2707 SHELL_MAP_LIST *MapListItem;
2708 UINTN Size;
2709 EFI_STATUS Status;
2710 CHAR16 *TempString;
2711 CHAR16 *DirectoryName;
2712 UINTN TempLen;
2713
2714 Size = 0;
2715 MapName = NULL;
2716 MapListItem = NULL;
2717 TempString = NULL;
2718 DirectoryName = NULL;
2719
2720 if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {
2721 return (EFI_INVALID_PARAMETER);
2722 }
2723
2724 if (IsListEmpty(&gShellMapList.Link)){
2725 return (EFI_NOT_FOUND);
2726 }
2727
2728 DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
2729 ASSERT(DirectoryName != NULL);
2730
2731 PathCleanUpDirectories(DirectoryName);
2732
2733 if (FileSystem == NULL) {
2734 //
2735 // determine the file system mapping to use
2736 //
2737 if (StrStr(DirectoryName, L":") != NULL) {
2738 ASSERT(MapName == NULL);
2739 MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
2740 }
2741 //
2742 // find the file system mapping's entry in the list
2743 // or use current
2744 //
2745 if (MapName != NULL) {
2746 MapListItem = ShellCommandFindMapItem(MapName);
2747
2748 //
2749 // make that the current file system mapping
2750 //
2751 if (MapListItem != NULL) {
2752 gShellCurDir = MapListItem;
2753 }
2754 } else {
2755 MapListItem = gShellCurDir;
2756 }
2757
2758 if (MapListItem == NULL) {
2759 return (EFI_NOT_FOUND);
2760 }
2761
2762 //
2763 // now update the MapListItem's current directory
2764 //
2765 if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
2766 FreePool(MapListItem->CurrentDirectoryPath);
2767 MapListItem->CurrentDirectoryPath = NULL;
2768 }
2769 if (MapName != NULL) {
2770 TempLen = StrLen(MapName);
2771 if (TempLen != StrLen(DirectoryName)) {
2772 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2773 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
2774 }
2775 } else {
2776 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2777 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
2778 }
2779 if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
2780 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2781 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
2782 }
2783 } else {
2784 //
2785 // cant have a mapping in the directory...
2786 //
2787 if (StrStr(DirectoryName, L":") != NULL) {
2788 return (EFI_INVALID_PARAMETER);
2789 }
2790 //
2791 // FileSystem != NULL
2792 //
2793 MapListItem = ShellCommandFindMapItem(FileSystem);
2794 if (MapListItem == NULL) {
2795 return (EFI_INVALID_PARAMETER);
2796 }
2797 // gShellCurDir = MapListItem;
2798 if (DirectoryName != NULL) {
2799 //
2800 // change current dir on that file system
2801 //
2802
2803 if (MapListItem->CurrentDirectoryPath != NULL) {
2804 FreePool(MapListItem->CurrentDirectoryPath);
2805 DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
2806 }
2807 // ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2808 // MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
2809 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2810 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
2811 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2812 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
2813 if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') {
2814 ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
2815 MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
2816 }
2817 }
2818 }
2819 //
2820 // if updated the current directory then update the environment variable
2821 //
2822 if (MapListItem == gShellCurDir) {
2823 Size = 0;
2824 ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
2825 StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
2826 ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
2827 StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
2828 Status = InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
2829 FreePool(TempString);
2830 return (Status);
2831 }
2832 return(EFI_SUCCESS);
2833 }
2834
2835 /**
2836 Return help information about a specific command.
2837
2838 This function returns the help information for the specified command. The help text
2839 can be internal to the shell or can be from a UEFI Shell manual page.
2840
2841 If Sections is specified, then each section name listed will be compared in a casesensitive
2842 manner, to the section names described in Appendix B. If the section exists,
2843 it will be appended to the returned help text. If the section does not exist, no
2844 information will be returned. If Sections is NULL, then all help text information
2845 available will be returned.
2846
2847 @param Command Points to the NULL-terminated UEFI Shell command name.
2848 @param Sections Points to the NULL-terminated comma-delimited
2849 section names to return. If NULL, then all
2850 sections will be returned.
2851 @param HelpText On return, points to a callee-allocated buffer
2852 containing all specified help text.
2853
2854 @retval EFI_SUCCESS The help text was returned.
2855 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
2856 returned help text.
2857 @retval EFI_INVALID_PARAMETER HelpText is NULL
2858 @retval EFI_NOT_FOUND There is no help text available for Command.
2859 **/
2860 EFI_STATUS
2861 EFIAPI
2862 EfiShellGetHelpText(
2863 IN CONST CHAR16 *Command,
2864 IN CONST CHAR16 *Sections OPTIONAL,
2865 OUT CHAR16 **HelpText
2866 )
2867 {
2868 CONST CHAR16 *ManFileName;
2869 CHAR16 *FixCommand;
2870 EFI_STATUS Status;
2871
2872 ASSERT(HelpText != NULL);
2873 FixCommand = NULL;
2874
2875 ManFileName = ShellCommandGetManFileNameHandler(Command);
2876
2877 if (ManFileName != NULL) {
2878 return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
2879 } else {
2880 if ((StrLen(Command)> 4)
2881 && (Command[StrLen(Command)-1] == L'i' || Command[StrLen(Command)-1] == L'I')
2882 && (Command[StrLen(Command)-2] == L'f' || Command[StrLen(Command)-2] == L'F')
2883 && (Command[StrLen(Command)-3] == L'e' || Command[StrLen(Command)-3] == L'E')
2884 && (Command[StrLen(Command)-4] == L'.')
2885 ) {
2886 FixCommand = AllocateZeroPool(StrSize(Command) - 4 * sizeof (CHAR16));
2887 ASSERT(FixCommand != NULL);
2888
2889 StrnCpy(FixCommand, Command, StrLen(Command)-4);
2890 Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText);
2891 FreePool(FixCommand);
2892 return Status;
2893 } else {
2894 return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
2895 }
2896 }
2897 }
2898
2899 /**
2900 Gets the enable status of the page break output mode.
2901
2902 User can use this function to determine current page break mode.
2903
2904 @retval TRUE The page break output mode is enabled.
2905 @retval FALSE The page break output mode is disabled.
2906 **/
2907 BOOLEAN
2908 EFIAPI
2909 EfiShellGetPageBreak(
2910 VOID
2911 )
2912 {
2913 return(ShellInfoObject.PageBreakEnabled);
2914 }
2915
2916 /**
2917 Judges whether the active shell is the root shell.
2918
2919 This function makes the user to know that whether the active Shell is the root shell.
2920
2921 @retval TRUE The active Shell is the root Shell.
2922 @retval FALSE The active Shell is NOT the root Shell.
2923 **/
2924 BOOLEAN
2925 EFIAPI
2926 EfiShellIsRootShell(
2927 VOID
2928 )
2929 {
2930 return(ShellInfoObject.RootShellInstance);
2931 }
2932
2933 /**
2934 function to return a semi-colon delimeted list of all alias' in the current shell
2935
2936 up to caller to free the memory.
2937
2938 @retval NULL No alias' were found
2939 @retval NULL An error ocurred getting alias'
2940 @return !NULL a list of all alias'
2941 **/
2942 CHAR16 *
2943 EFIAPI
2944 InternalEfiShellGetListAlias(
2945 )
2946 {
2947 UINT64 MaxStorSize;
2948 UINT64 RemStorSize;
2949 UINT64 MaxVarSize;
2950 EFI_STATUS Status;
2951 EFI_GUID Guid;
2952 CHAR16 *VariableName;
2953 UINTN NameSize;
2954 CHAR16 *RetVal;
2955 UINTN RetSize;
2956
2957 Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize);
2958 ASSERT_EFI_ERROR(Status);
2959
2960 VariableName = AllocateZeroPool((UINTN)MaxVarSize);
2961 RetSize = 0;
2962 RetVal = NULL;
2963
2964 if (VariableName == NULL) {
2965 return (NULL);
2966 }
2967
2968 VariableName[0] = CHAR_NULL;
2969
2970 while (TRUE) {
2971 NameSize = (UINTN)MaxVarSize;
2972 Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
2973 if (Status == EFI_NOT_FOUND){
2974 break;
2975 }
2976 ASSERT_EFI_ERROR(Status);
2977 if (EFI_ERROR(Status)) {
2978 break;
2979 }
2980 if (CompareGuid(&Guid, &gShellAliasGuid)){
2981 ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
2982 RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
2983 RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
2984 } // compare guid
2985 } // while
2986 FreePool(VariableName);
2987
2988 return (RetVal);
2989 }
2990
2991 /**
2992 This function returns the command associated with a alias or a list of all
2993 alias'.
2994
2995 @param[in] Alias Points to the NULL-terminated shell alias.
2996 If this parameter is NULL, then all
2997 aliases will be returned in ReturnedData.
2998 @param[out] Volatile upon return of a single command if TRUE indicates
2999 this is stored in a volatile fashion. FALSE otherwise.
3000
3001 @return If Alias is not NULL, it will return a pointer to
3002 the NULL-terminated command for that alias.
3003 If Alias is NULL, ReturnedData points to a ';'
3004 delimited list of alias (e.g.
3005 ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
3006 @retval NULL an error ocurred
3007 @retval NULL Alias was not a valid Alias
3008 **/
3009 CONST CHAR16 *
3010 EFIAPI
3011 EfiShellGetAlias(
3012 IN CONST CHAR16 *Alias,
3013 OUT BOOLEAN *Volatile OPTIONAL
3014 )
3015 {
3016 CHAR16 *RetVal;
3017 UINTN RetSize;
3018 UINT32 Attribs;
3019 EFI_STATUS Status;
3020
3021 if (Alias != NULL) {
3022 if (Volatile == NULL) {
3023 return (AddBufferToFreeList(GetVariable((CHAR16*)Alias, &gShellAliasGuid)));
3024 }
3025 RetSize = 0;
3026 RetVal = NULL;
3027 Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
3028 if (Status == EFI_BUFFER_TOO_SMALL) {
3029 RetVal = AllocateZeroPool(RetSize);
3030 Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
3031 }
3032 if (EFI_ERROR(Status)) {
3033 if (RetVal != NULL) {
3034 FreePool(RetVal);
3035 }
3036 return (NULL);
3037 }
3038 if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
3039 *Volatile = FALSE;
3040 } else {
3041 *Volatile = TRUE;
3042 }
3043
3044 return (AddBufferToFreeList(RetVal));
3045 }
3046 return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
3047 }
3048
3049 /**
3050 Changes a shell command alias.
3051
3052 This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
3053
3054 this function does not check for built in alias'.
3055
3056 @param[in] Command Points to the NULL-terminated shell command or existing alias.
3057 @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
3058 Command refers to an alias, that alias will be deleted.
3059 @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
3060 Alias being set will be stored in a non-volatile fashion.
3061
3062 @retval EFI_SUCCESS Alias created or deleted successfully.
3063 @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
3064 **/
3065 EFI_STATUS
3066 EFIAPI
3067 InternalSetAlias(
3068 IN CONST CHAR16 *Command,
3069 IN CONST CHAR16 *Alias,
3070 IN BOOLEAN Volatile
3071 )
3072 {
3073 //
3074 // We must be trying to remove one if Alias is NULL
3075 //
3076 if (Alias == NULL) {
3077 //
3078 // remove an alias (but passed in COMMAND parameter)
3079 //
3080 return (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));
3081 } else {
3082 //
3083 // Add and replace are the same
3084 //
3085
3086 // We dont check the error return on purpose since the variable may not exist.
3087 gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);
3088
3089 return (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));
3090 }
3091 }
3092
3093 /**
3094 Changes a shell command alias.
3095
3096 This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
3097
3098
3099 @param[in] Command Points to the NULL-terminated shell command or existing alias.
3100 @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
3101 Command refers to an alias, that alias will be deleted.
3102 @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If
3103 FALSE and the alias already exists, then the existing alias is unchanged and
3104 EFI_ACCESS_DENIED is returned.
3105 @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
3106 Alias being set will be stored in a non-volatile fashion.
3107
3108 @retval EFI_SUCCESS Alias created or deleted successfully.
3109 @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
3110 @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to
3111 FALSE.
3112 **/
3113 EFI_STATUS
3114 EFIAPI
3115 EfiShellSetAlias(
3116 IN CONST CHAR16 *Command,
3117 IN CONST CHAR16 *Alias,
3118 IN BOOLEAN Replace,
3119 IN BOOLEAN Volatile
3120 )
3121 {
3122 //
3123 // cant set over a built in alias
3124 //
3125 if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
3126 return (EFI_ACCESS_DENIED);
3127 }
3128 if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
3129 return (EFI_INVALID_PARAMETER);
3130 }
3131
3132 if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
3133 return (EFI_ACCESS_DENIED);
3134 }
3135
3136 return (InternalSetAlias(Command, Alias, Volatile));
3137 }
3138
3139 // Pure FILE_HANDLE operations are passed to FileHandleLib
3140 // these functions are indicated by the *
3141 EFI_SHELL_PROTOCOL mShellProtocol = {
3142 EfiShellExecute,
3143 EfiShellGetEnv,
3144 EfiShellSetEnv,
3145 EfiShellGetAlias,
3146 EfiShellSetAlias,
3147 EfiShellGetHelpText,
3148 EfiShellGetDevicePathFromMap,
3149 EfiShellGetMapFromDevicePath,
3150 EfiShellGetDevicePathFromFilePath,
3151 EfiShellGetFilePathFromDevicePath,
3152 EfiShellSetMap,
3153 EfiShellGetCurDir,
3154 EfiShellSetCurDir,
3155 EfiShellOpenFileList,
3156 EfiShellFreeFileList,
3157 EfiShellRemoveDupInFileList,
3158 EfiShellBatchIsActive,
3159 EfiShellIsRootShell,
3160 EfiShellEnablePageBreak,
3161 EfiShellDisablePageBreak,
3162 EfiShellGetPageBreak,
3163 EfiShellGetDeviceName,
3164 (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo, //*
3165 (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo, //*
3166 EfiShellOpenFileByName,
3167 EfiShellClose,
3168 EfiShellCreateFile,
3169 (EFI_SHELL_READ_FILE)FileHandleRead, //*
3170 (EFI_SHELL_WRITE_FILE)FileHandleWrite, //*
3171 (EFI_SHELL_DELETE_FILE)FileHandleDelete, //*
3172 EfiShellDeleteFileByName,
3173 (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, //*
3174 (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, //*
3175 (EFI_SHELL_FLUSH_FILE)FileHandleFlush, //*
3176 EfiShellFindFiles,
3177 EfiShellFindFilesInDir,
3178 (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize, //*
3179 EfiShellOpenRoot,
3180 EfiShellOpenRootByHandle,
3181 NULL,
3182 SHELL_MAJOR_VERSION,
3183 SHELL_MINOR_VERSION
3184 };
3185
3186 /**
3187 Function to create and install on the current handle.
3188
3189 Will overwrite any existing ShellProtocols in the system to be sure that
3190 the current shell is in control.
3191
3192 This must be removed via calling CleanUpShellProtocol().
3193
3194 @param[in, out] NewShell The pointer to the pointer to the structure
3195 to install.
3196
3197 @retval EFI_SUCCESS The operation was successful.
3198 @return An error from LocateHandle, CreateEvent, or other core function.
3199 **/
3200 EFI_STATUS
3201 EFIAPI
3202 CreatePopulateInstallShellProtocol (
3203 IN OUT EFI_SHELL_PROTOCOL **NewShell
3204 )
3205 {
3206 EFI_STATUS Status;
3207 UINTN BufferSize;
3208 EFI_HANDLE *Buffer;
3209 UINTN HandleCounter;
3210 SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode;
3211
3212 if (NewShell == NULL) {
3213 return (EFI_INVALID_PARAMETER);
3214 }
3215
3216 BufferSize = 0;
3217 Buffer = NULL;
3218 OldProtocolNode = NULL;
3219 InitializeListHead(&ShellInfoObject.OldShellList.Link);
3220
3221 //
3222 // Initialize EfiShellProtocol object...
3223 //
3224 Status = gBS->CreateEvent(0,
3225 0,
3226 NULL,
3227 NULL,
3228 &mShellProtocol.ExecutionBreak);
3229 if (EFI_ERROR(Status)) {
3230 return (Status);
3231 }
3232
3233 //
3234 // Get the size of the buffer we need.
3235 //
3236 Status = gBS->LocateHandle(ByProtocol,
3237 &gEfiShellProtocolGuid,
3238 NULL,
3239 &BufferSize,
3240 Buffer);
3241 if (Status == EFI_BUFFER_TOO_SMALL) {
3242 //
3243 // Allocate and recall with buffer of correct size
3244 //
3245 Buffer = AllocateZeroPool(BufferSize);
3246 if (Buffer == NULL) {
3247 return (EFI_OUT_OF_RESOURCES);
3248 }
3249 Status = gBS->LocateHandle(ByProtocol,
3250 &gEfiShellProtocolGuid,
3251 NULL,
3252 &BufferSize,
3253 Buffer);
3254 if (EFI_ERROR(Status)) {
3255 FreePool(Buffer);
3256 return (Status);
3257 }
3258 //
3259 // now overwrite each of them, but save the info to restore when we end.
3260 //
3261 for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
3262 OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
3263 ASSERT(OldProtocolNode != NULL);
3264 Status = gBS->OpenProtocol(Buffer[HandleCounter],
3265 &gEfiShellProtocolGuid,
3266 (VOID **) &(OldProtocolNode->Interface),
3267 gImageHandle,
3268 NULL,
3269 EFI_OPEN_PROTOCOL_GET_PROTOCOL
3270 );
3271 if (!EFI_ERROR(Status)) {
3272 //
3273 // reinstall over the old one...
3274 //
3275 OldProtocolNode->Handle = Buffer[HandleCounter];
3276 Status = gBS->ReinstallProtocolInterface(
3277 OldProtocolNode->Handle,
3278 &gEfiShellProtocolGuid,
3279 OldProtocolNode->Interface,
3280 (VOID*)(&mShellProtocol));
3281 if (!EFI_ERROR(Status)) {
3282 //
3283 // we reinstalled sucessfully. log this so we can reverse it later.
3284 //
3285
3286 //
3287 // add to the list for subsequent...
3288 //
3289 InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
3290 }
3291 }
3292 }
3293 FreePool(Buffer);
3294 } else if (Status == EFI_NOT_FOUND) {
3295 ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
3296 //
3297 // no one else published yet. just publish it ourselves.
3298 //
3299 Status = gBS->InstallProtocolInterface (
3300 &gImageHandle,
3301 &gEfiShellProtocolGuid,
3302 EFI_NATIVE_INTERFACE,
3303 (VOID*)(&mShellProtocol));
3304 }
3305
3306 if (PcdGetBool(PcdShellSupportOldProtocols)){
3307 ///@todo support ShellEnvironment2
3308 ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
3309 }
3310
3311 if (!EFI_ERROR(Status)) {
3312 *NewShell = &mShellProtocol;
3313 }
3314 return (Status);
3315 }
3316
3317 /**
3318 Opposite of CreatePopulateInstallShellProtocol.
3319
3320 Free all memory and restore the system to the state it was in before calling
3321 CreatePopulateInstallShellProtocol.
3322
3323 @param[in, out] NewShell The pointer to the new shell protocol structure.
3324
3325 @retval EFI_SUCCESS The operation was successful.
3326 **/
3327 EFI_STATUS
3328 EFIAPI
3329 CleanUpShellProtocol (
3330 IN OUT EFI_SHELL_PROTOCOL *NewShell
3331 )
3332 {
3333 EFI_STATUS Status;
3334 SHELL_PROTOCOL_HANDLE_LIST *Node2;
3335 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
3336
3337 //
3338 // if we need to restore old protocols...
3339 //
3340 if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
3341 for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
3342 ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
3343 ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
3344 ){
3345 RemoveEntryList(&Node2->Link);
3346 Status = gBS->ReinstallProtocolInterface(Node2->Handle,
3347 &gEfiShellProtocolGuid,
3348 NewShell,
3349 Node2->Interface);
3350 FreePool(Node2);
3351 }
3352 } else {
3353 //
3354 // no need to restore
3355 //
3356 Status = gBS->UninstallProtocolInterface(gImageHandle,
3357 &gEfiShellProtocolGuid,
3358 NewShell);
3359 }
3360 Status = gBS->CloseEvent(NewShell->ExecutionBreak);
3361 NewShell->ExecutionBreak = NULL;
3362
3363 Status = gBS->OpenProtocol(
3364 gST->ConsoleInHandle,
3365 &gEfiSimpleTextInputExProtocolGuid,
3366 (VOID**)&SimpleEx,
3367 gImageHandle,
3368 NULL,
3369 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
3370
3371 if (!EFI_ERROR (Status)) {
3372 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
3373 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
3374 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
3375 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
3376 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
3377 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
3378 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
3379 Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
3380 }
3381 return (Status);
3382 }
3383
3384 /**
3385 Notification function for keystrokes.
3386
3387 @param[in] KeyData The key that was pressed.
3388
3389 @retval EFI_SUCCESS The operation was successful.
3390 **/
3391 EFI_STATUS
3392 EFIAPI
3393 NotificationFunction(
3394 IN EFI_KEY_DATA *KeyData
3395 )
3396 {
3397 if ( ((KeyData->Key.UnicodeChar == L'c') &&
3398 (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) ||
3399 (KeyData->Key.UnicodeChar == 3)
3400 ){
3401 if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
3402 return (EFI_UNSUPPORTED);
3403 }
3404 return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
3405 } else if ((KeyData->Key.UnicodeChar == L's') &&
3406 (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))
3407 ){
3408 ShellInfoObject.HaltOutput = TRUE;
3409 }
3410 return (EFI_SUCCESS);
3411 }
3412
3413 /**
3414 Function to start monitoring for CTRL-C using SimpleTextInputEx. This
3415 feature's enabled state was not known when the shell initially launched.
3416
3417 @retval EFI_SUCCESS The feature is enabled.
3418 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
3419 **/
3420 EFI_STATUS
3421 EFIAPI
3422 InernalEfiShellStartMonitor(
3423 VOID
3424 )
3425 {
3426 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
3427 EFI_KEY_DATA KeyData;
3428 EFI_STATUS Status;
3429
3430 Status = gBS->OpenProtocol(
3431 gST->ConsoleInHandle,
3432 &gEfiSimpleTextInputExProtocolGuid,
3433 (VOID**)&SimpleEx,
3434 gImageHandle,
3435 NULL,
3436 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
3437 if (EFI_ERROR(Status)) {
3438 ShellPrintHiiEx(
3439 -1,
3440 -1,
3441 NULL,
3442 STRING_TOKEN (STR_SHELL_NO_IN_EX),
3443 ShellInfoObject.HiiHandle);
3444 return (EFI_SUCCESS);
3445 }
3446
3447 if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
3448 return (EFI_UNSUPPORTED);
3449 }
3450
3451 KeyData.KeyState.KeyToggleState = 0;
3452 KeyData.Key.ScanCode = 0;
3453 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
3454 KeyData.Key.UnicodeChar = L'c';
3455
3456 Status = SimpleEx->RegisterKeyNotify(
3457 SimpleEx,
3458 &KeyData,
3459 NotificationFunction,
3460 &ShellInfoObject.CtrlCNotifyHandle1);
3461
3462 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
3463 if (!EFI_ERROR(Status)) {
3464 Status = SimpleEx->RegisterKeyNotify(
3465 SimpleEx,
3466 &KeyData,
3467 NotificationFunction,
3468 &ShellInfoObject.CtrlCNotifyHandle2);
3469 }
3470 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
3471 KeyData.Key.UnicodeChar = 3;
3472 if (!EFI_ERROR(Status)) {
3473 Status = SimpleEx->RegisterKeyNotify(
3474 SimpleEx,
3475 &KeyData,
3476 NotificationFunction,
3477 &ShellInfoObject.CtrlCNotifyHandle3);
3478 }
3479 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
3480 if (!EFI_ERROR(Status)) {
3481 Status = SimpleEx->RegisterKeyNotify(
3482 SimpleEx,
3483 &KeyData,
3484 NotificationFunction,
3485 &ShellInfoObject.CtrlCNotifyHandle4);
3486 }
3487 return (Status);
3488 }