VirtualBox

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

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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