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