VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/ShellPkg/Application/Shell/ShellProtocol.c@ 105681

Last change on this file since 105681 was 101291, checked in by vboxsync, 19 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette