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