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