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