1 /** @file
2   Provides interface to shell internal functions for shell commands.
3 
4   (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "UefiShellCommandLib.h"
17 
18 // STATIC local variables
19 STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY  mCommandList;
20 STATIC SCRIPT_FILE_LIST                   mScriptList;
21 STATIC ALIAS_LIST                         mAliasList;
22 STATIC BOOLEAN                            mEchoState;
23 STATIC BOOLEAN                            mExitRequested;
24 STATIC UINT64                             mExitCode;
25 STATIC BOOLEAN                            mExitScript;
26 STATIC CHAR16                             *mProfileList;
27 STATIC UINTN                              mProfileListSize;
28 STATIC UINTN                              mFsMaxCount = 0;
29 STATIC UINTN                              mBlkMaxCount = 0;
30 STATIC BUFFER_LIST                        mFileHandleList;
31 
32 // global variables required by library class.
33 EFI_UNICODE_COLLATION_PROTOCOL    *gUnicodeCollation            = NULL;
34 SHELL_MAP_LIST                    gShellMapList;
35 SHELL_MAP_LIST                    *gShellCurDir                 = NULL;
36 
37 CONST CHAR16* SupportLevel[] = {
38   L"Minimal",
39   L"Scripting",
40   L"Basic",
41   L"Interactive"
42 };
43 
44 /**
45   Function to make sure that the global protocol pointers are valid.
46   must be called after constructor before accessing the pointers.
47 **/
48 EFI_STATUS
49 EFIAPI
CommandInit(VOID)50 CommandInit(
51   VOID
52   )
53 {
54   EFI_STATUS Status;
55   if (gUnicodeCollation == NULL) {
56     Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
57     if (EFI_ERROR(Status)) {
58       return (EFI_DEVICE_ERROR);
59     }
60   }
61   return (EFI_SUCCESS);
62 }
63 
64 /**
65   Constructor for the Shell Command library.
66 
67   Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
68 
69   @param ImageHandle    the image handle of the process
70   @param SystemTable    the EFI System Table pointer
71 
72   @retval EFI_SUCCESS   the initialization was complete sucessfully
73 **/
74 RETURN_STATUS
75 EFIAPI
ShellCommandLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)76 ShellCommandLibConstructor (
77   IN EFI_HANDLE        ImageHandle,
78   IN EFI_SYSTEM_TABLE  *SystemTable
79   )
80 {
81   EFI_STATUS        Status;
82   InitializeListHead(&gShellMapList.Link);
83   InitializeListHead(&mCommandList.Link);
84   InitializeListHead(&mAliasList.Link);
85   InitializeListHead(&mScriptList.Link);
86   InitializeListHead(&mFileHandleList.Link);
87   mEchoState = TRUE;
88 
89   mExitRequested    = FALSE;
90   mExitScript       = FALSE;
91   mProfileListSize  = 0;
92   mProfileList      = NULL;
93 
94   if (gUnicodeCollation == NULL) {
95     Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
96     if (EFI_ERROR(Status)) {
97       return (EFI_DEVICE_ERROR);
98     }
99   }
100 
101   return (RETURN_SUCCESS);
102 }
103 
104 /**
105   Frees list of file handles.
106 
107   @param[in] List     The list to free.
108 **/
109 VOID
110 EFIAPI
FreeFileHandleList(IN BUFFER_LIST * List)111 FreeFileHandleList (
112   IN BUFFER_LIST *List
113   )
114 {
115   BUFFER_LIST               *BufferListEntry;
116 
117   if (List == NULL){
118     return;
119   }
120   //
121   // enumerate through the buffer list and free all memory
122   //
123   for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
124       ; !IsListEmpty (&List->Link)
125       ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
126      ){
127     RemoveEntryList(&BufferListEntry->Link);
128     ASSERT(BufferListEntry->Buffer != NULL);
129     SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE*)(BufferListEntry->Buffer))->Path);
130     SHELL_FREE_NON_NULL(BufferListEntry->Buffer);
131     SHELL_FREE_NON_NULL(BufferListEntry);
132   }
133 }
134 
135 /**
136   Destructor for the library.  free any resources.
137 
138   @param ImageHandle    the image handle of the process
139   @param SystemTable    the EFI System Table pointer
140 
141   @retval RETURN_SUCCESS this function always returns success
142 **/
143 RETURN_STATUS
144 EFIAPI
ShellCommandLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)145 ShellCommandLibDestructor (
146   IN EFI_HANDLE        ImageHandle,
147   IN EFI_SYSTEM_TABLE  *SystemTable
148   )
149 {
150   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
151   ALIAS_LIST                        *Node2;
152   SCRIPT_FILE_LIST                  *Node3;
153   SHELL_MAP_LIST                    *MapNode;
154   //
155   // enumerate throught the list and free all the memory
156   //
157   while (!IsListEmpty (&mCommandList.Link)) {
158     Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
159     RemoveEntryList(&Node->Link);
160     SHELL_FREE_NON_NULL(Node->CommandString);
161     FreePool(Node);
162     DEBUG_CODE(Node = NULL;);
163   }
164 
165   //
166   // enumerate through the alias list and free all memory
167   //
168   while (!IsListEmpty (&mAliasList.Link)) {
169     Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link);
170     RemoveEntryList(&Node2->Link);
171     SHELL_FREE_NON_NULL(Node2->CommandString);
172     SHELL_FREE_NON_NULL(Node2->Alias);
173     SHELL_FREE_NON_NULL(Node2);
174     DEBUG_CODE(Node2 = NULL;);
175   }
176 
177   //
178   // enumerate throught the list and free all the memory
179   //
180   while (!IsListEmpty (&mScriptList.Link)) {
181     Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
182     RemoveEntryList(&Node3->Link);
183     DeleteScriptFileStruct(Node3->Data);
184     FreePool(Node3);
185   }
186 
187   //
188   // enumerate throught the mappings list and free all the memory
189   //
190   if (!IsListEmpty(&gShellMapList.Link)) {
191     for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
192          ; !IsListEmpty (&gShellMapList.Link)
193          ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
194         ){
195       ASSERT(MapNode != NULL);
196       RemoveEntryList(&MapNode->Link);
197       SHELL_FREE_NON_NULL(MapNode->DevicePath);
198       SHELL_FREE_NON_NULL(MapNode->MapName);
199       SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
200       FreePool(MapNode);
201     }
202   }
203   if (!IsListEmpty(&mFileHandleList.Link)){
204     FreeFileHandleList(&mFileHandleList);
205   }
206 
207   if (mProfileList != NULL) {
208     FreePool(mProfileList);
209   }
210 
211   gUnicodeCollation            = NULL;
212   gShellCurDir                 = NULL;
213 
214   return (RETURN_SUCCESS);
215 }
216 
217 /**
218   Find a dynamic command protocol instance given a command name string.
219 
220   @param CommandString  the command name string
221 
222   @return instance      the command protocol instance, if dynamic command instance found
223   @retval NULL          no dynamic command protocol instance found for name
224 **/
225 CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
226 EFIAPI
ShellCommandFindDynamicCommand(IN CONST CHAR16 * CommandString)227 ShellCommandFindDynamicCommand (
228   IN CONST CHAR16 *CommandString
229   )
230 {
231   EFI_STATUS                          Status;
232   EFI_HANDLE                          *CommandHandleList;
233   EFI_HANDLE                          *NextCommand;
234   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
235 
236   CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
237   if (CommandHandleList == NULL) {
238     //
239     // not found or out of resources
240     //
241     return NULL;
242   }
243 
244   for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
245     Status = gBS->HandleProtocol(
246       *NextCommand,
247       &gEfiShellDynamicCommandProtocolGuid,
248       (VOID **)&DynamicCommand
249       );
250 
251     if (EFI_ERROR(Status)) {
252       continue;
253     }
254 
255     if (gUnicodeCollation->StriColl(
256           gUnicodeCollation,
257           (CHAR16*)CommandString,
258           (CHAR16*)DynamicCommand->CommandName) == 0
259           ){
260         FreePool(CommandHandleList);
261         return (DynamicCommand);
262     }
263   }
264 
265   FreePool(CommandHandleList);
266   return (NULL);
267 }
268 
269 /**
270   Checks if a command exists as a dynamic command protocol instance
271 
272   @param[in] CommandString        The command string to check for on the list.
273 **/
274 BOOLEAN
275 EFIAPI
ShellCommandDynamicCommandExists(IN CONST CHAR16 * CommandString)276 ShellCommandDynamicCommandExists (
277   IN CONST CHAR16 *CommandString
278   )
279 {
280   return (BOOLEAN) ((ShellCommandFindDynamicCommand(CommandString) != NULL));
281 }
282 
283 /**
284   Checks if a command is already on the internal command list.
285 
286   @param[in] CommandString        The command string to check for on the list.
287 **/
288 BOOLEAN
289 EFIAPI
ShellCommandIsCommandOnInternalList(IN CONST CHAR16 * CommandString)290 ShellCommandIsCommandOnInternalList(
291   IN CONST  CHAR16 *CommandString
292   )
293 {
294   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
295 
296   //
297   // assert for NULL parameter
298   //
299   ASSERT(CommandString != NULL);
300 
301   //
302   // check for the command
303   //
304   for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
305       ; !IsNull(&mCommandList.Link, &Node->Link)
306       ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
307      ){
308     ASSERT(Node->CommandString != NULL);
309     if (gUnicodeCollation->StriColl(
310           gUnicodeCollation,
311           (CHAR16*)CommandString,
312           Node->CommandString) == 0
313        ){
314       return (TRUE);
315     }
316   }
317   return (FALSE);
318 }
319 
320 /**
321   Checks if a command exists, either internally or through the dynamic command protocol.
322 
323   @param[in] CommandString        The command string to check for on the list.
324 **/
325 BOOLEAN
326 EFIAPI
ShellCommandIsCommandOnList(IN CONST CHAR16 * CommandString)327 ShellCommandIsCommandOnList(
328   IN CONST  CHAR16                      *CommandString
329   )
330 {
331   if (ShellCommandIsCommandOnInternalList(CommandString)) {
332     return TRUE;
333   }
334 
335   return ShellCommandDynamicCommandExists(CommandString);
336 }
337 
338 /**
339  Get the help text for a dynamic command.
340 
341   @param[in] CommandString        The command name.
342 
343   @retval NULL  No help text was found.
344   @return       String of help text. Caller required to free.
345 **/
346 CHAR16*
347 EFIAPI
ShellCommandGetDynamicCommandHelp(IN CONST CHAR16 * CommandString)348 ShellCommandGetDynamicCommandHelp(
349   IN CONST  CHAR16                      *CommandString
350   )
351 {
352   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
353 
354   DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);
355   if (DynamicCommand == NULL) {
356     return (NULL);
357   }
358 
359   //
360   // TODO: how to get proper language?
361   //
362   return DynamicCommand->GetHelp(DynamicCommand, "en");
363 }
364 
365 /**
366   Get the help text for an internal command.
367 
368   @param[in] CommandString        The command name.
369 
370   @retval NULL  No help text was found.
371   @return       String of help text. Caller reuiqred to free.
372 **/
373 CHAR16*
374 EFIAPI
ShellCommandGetInternalCommandHelp(IN CONST CHAR16 * CommandString)375 ShellCommandGetInternalCommandHelp(
376   IN CONST  CHAR16                      *CommandString
377   )
378 {
379   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
380 
381   //
382   // assert for NULL parameter
383   //
384   ASSERT(CommandString != NULL);
385 
386   //
387   // check for the command
388   //
389   for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
390       ; !IsNull(&mCommandList.Link, &Node->Link)
391       ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
392      ){
393     ASSERT(Node->CommandString != NULL);
394     if (gUnicodeCollation->StriColl(
395           gUnicodeCollation,
396           (CHAR16*)CommandString,
397           Node->CommandString) == 0
398        ){
399       return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
400     }
401   }
402   return (NULL);
403 }
404 
405 /**
406   Get the help text for a command.
407 
408   @param[in] CommandString        The command name.
409 
410   @retval NULL  No help text was found.
411   @return       String of help text.Caller reuiqred to free.
412 **/
413 CHAR16*
414 EFIAPI
ShellCommandGetCommandHelp(IN CONST CHAR16 * CommandString)415 ShellCommandGetCommandHelp (
416   IN CONST  CHAR16                      *CommandString
417   )
418 {
419   CHAR16      *HelpStr;
420   HelpStr = ShellCommandGetInternalCommandHelp(CommandString);
421 
422   if (HelpStr == NULL) {
423     HelpStr = ShellCommandGetDynamicCommandHelp(CommandString);
424   }
425 
426   return HelpStr;
427 }
428 
429 
430 /**
431   Registers handlers of type SHELL_RUN_COMMAND and
432   SHELL_GET_MAN_FILENAME for each shell command.
433 
434   If the ShellSupportLevel is greater than the value of the
435   PcdShellSupportLevel then return RETURN_UNSUPPORTED.
436 
437   Registers the handlers specified by GetHelpInfoHandler and CommandHandler
438   with the command specified by CommandString. If the command named by
439   CommandString has already been registered, then return
440   RETURN_ALREADY_STARTED.
441 
442   If there are not enough resources available to register the handlers then
443   RETURN_OUT_OF_RESOURCES is returned.
444 
445   If CommandString is NULL, then ASSERT().
446   If GetHelpInfoHandler is NULL, then ASSERT().
447   If CommandHandler is NULL, then ASSERT().
448   If ProfileName is NULL, then ASSERT().
449 
450   @param[in]  CommandString         Pointer to the command name.  This is the
451                                     name to look for on the command line in
452                                     the shell.
453   @param[in]  CommandHandler        Pointer to a function that runs the
454                                     specified command.
455   @param[in]  GetManFileName        Pointer to a function that provides man
456                                     filename.
457   @param[in]  ShellMinSupportLevel  minimum Shell Support Level which has this
458                                     function.
459   @param[in]  ProfileName           profile name to require for support of this
460                                     function.
461   @param[in]  CanAffectLE           indicates whether this command's return value
462                                     can change the LASTERROR environment variable.
463   @param[in]  HiiHandle             Handle of this command's HII entry.
464   @param[in]  ManFormatHelp         HII locator for the help text.
465 
466   @retval  RETURN_SUCCESS           The handlers were registered.
467   @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources available to
468                                     register the shell command.
469   @retval RETURN_UNSUPPORTED        the ShellMinSupportLevel was higher than the
470                                     currently allowed support level.
471   @retval RETURN_ALREADY_STARTED    The CommandString represents a command that
472                                     is already registered.  Only 1 handler set for
473                                     a given command is allowed.
474   @sa SHELL_GET_MAN_FILENAME
475   @sa SHELL_RUN_COMMAND
476 **/
477 RETURN_STATUS
478 EFIAPI
ShellCommandRegisterCommandName(IN CONST CHAR16 * CommandString,IN SHELL_RUN_COMMAND CommandHandler,IN SHELL_GET_MAN_FILENAME GetManFileName,IN UINT32 ShellMinSupportLevel,IN CONST CHAR16 * ProfileName,IN CONST BOOLEAN CanAffectLE,IN CONST EFI_HANDLE HiiHandle,IN CONST EFI_STRING_ID ManFormatHelp)479 ShellCommandRegisterCommandName (
480   IN CONST  CHAR16                      *CommandString,
481   IN        SHELL_RUN_COMMAND           CommandHandler,
482   IN        SHELL_GET_MAN_FILENAME      GetManFileName,
483   IN        UINT32                      ShellMinSupportLevel,
484   IN CONST  CHAR16                      *ProfileName,
485   IN CONST  BOOLEAN                     CanAffectLE,
486   IN CONST  EFI_HANDLE                  HiiHandle,
487   IN CONST  EFI_STRING_ID               ManFormatHelp
488   )
489 {
490   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
491   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
492   SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
493   INTN LexicalMatchValue;
494 
495   //
496   // Initialize local variables.
497   //
498   Command = NULL;
499   PrevCommand = NULL;
500   LexicalMatchValue = 0;
501 
502   //
503   // ASSERTs for NULL parameters
504   //
505   ASSERT(CommandString  != NULL);
506   ASSERT(GetManFileName != NULL);
507   ASSERT(CommandHandler != NULL);
508   ASSERT(ProfileName    != NULL);
509 
510   //
511   // check for shell support level
512   //
513   if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
514     return (RETURN_UNSUPPORTED);
515   }
516 
517   //
518   // check for already on the list
519   //
520   if (ShellCommandIsCommandOnList(CommandString)) {
521     return (RETURN_ALREADY_STARTED);
522   }
523 
524   //
525   // allocate memory for new struct
526   //
527   Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
528   ASSERT(Node != NULL);
529   Node->CommandString = AllocateCopyPool(StrSize(CommandString), CommandString);
530   ASSERT(Node->CommandString != NULL);
531 
532   Node->GetManFileName  = GetManFileName;
533   Node->CommandHandler  = CommandHandler;
534   Node->LastError       = CanAffectLE;
535   Node->HiiHandle       = HiiHandle;
536   Node->ManFormatHelp   = ManFormatHelp;
537 
538   if ( StrLen(ProfileName)>0
539     && ((mProfileList != NULL
540     && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
541    ){
542     ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
543     if (mProfileList == NULL) {
544       //
545       // If this is the first make a leading ';'
546       //
547       StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
548     }
549     StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
550     StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
551   }
552 
553   //
554   // Insert a new entry on top of the list
555   //
556   InsertHeadList (&mCommandList.Link, &Node->Link);
557 
558   //
559   // Move a new registered command to its sorted ordered location in the list
560   //
561   for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
562         PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
563         ; !IsNull (&mCommandList.Link, &Command->Link)
564         ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) {
565 
566     //
567     // Get Lexical Comparison Value between PrevCommand and Command list entry
568     //
569     LexicalMatchValue = gUnicodeCollation->StriColl (
570                                              gUnicodeCollation,
571                                              PrevCommand->CommandString,
572                                              Command->CommandString
573                                              );
574 
575     //
576     // Swap PrevCommand and Command list entry if PrevCommand list entry
577     // is alphabetically greater than Command list entry
578     //
579     if (LexicalMatchValue > 0){
580       Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link);
581     } else if (LexicalMatchValue < 0) {
582       //
583       // PrevCommand entry is lexically lower than Command entry
584       //
585       break;
586     }
587   }
588 
589   return (RETURN_SUCCESS);
590 }
591 
592 /**
593   Function to get the current Profile string.
594 
595   @retval NULL  There are no installed profiles.
596   @return       A semi-colon delimited list of profiles.
597 **/
598 CONST CHAR16 *
599 EFIAPI
ShellCommandGetProfileList(VOID)600 ShellCommandGetProfileList (
601   VOID
602   )
603 {
604   return (mProfileList);
605 }
606 
607 /**
608   Checks if a command string has been registered for CommandString and if so it runs
609   the previously registered handler for that command with the command line.
610 
611   If CommandString is NULL, then ASSERT().
612 
613   If Sections is specified, then each section name listed will be compared in a casesensitive
614   manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists,
615   it will be appended to the returned help text. If the section does not exist, no
616   information will be returned. If Sections is NULL, then all help text information
617   available will be returned.
618 
619   @param[in]  CommandString          Pointer to the command name.  This is the name
620                                      found on the command line in the shell.
621   @param[in, out] RetVal             Pointer to the return vaule from the command handler.
622 
623   @param[in, out]  CanAffectLE       indicates whether this command's return value
624                                      needs to be placed into LASTERROR environment variable.
625 
626   @retval RETURN_SUCCESS            The handler was run.
627   @retval RETURN_NOT_FOUND          The CommandString did not match a registered
628                                     command name.
629   @sa SHELL_RUN_COMMAND
630 **/
631 RETURN_STATUS
632 EFIAPI
ShellCommandRunCommandHandler(IN CONST CHAR16 * CommandString,IN OUT SHELL_STATUS * RetVal,IN OUT BOOLEAN * CanAffectLE OPTIONAL)633 ShellCommandRunCommandHandler (
634   IN CONST CHAR16               *CommandString,
635   IN OUT SHELL_STATUS           *RetVal,
636   IN OUT BOOLEAN                *CanAffectLE OPTIONAL
637   )
638 {
639   SHELL_COMMAND_INTERNAL_LIST_ENTRY   *Node;
640   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
641 
642   //
643   // assert for NULL parameters
644   //
645   ASSERT(CommandString != NULL);
646 
647   //
648   // check for the command
649   //
650   for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
651       ; !IsNull(&mCommandList.Link, &Node->Link)
652       ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
653      ){
654     ASSERT(Node->CommandString != NULL);
655     if (gUnicodeCollation->StriColl(
656           gUnicodeCollation,
657           (CHAR16*)CommandString,
658           Node->CommandString) == 0
659       ){
660       if (CanAffectLE != NULL) {
661         *CanAffectLE = Node->LastError;
662       }
663       if (RetVal != NULL) {
664         *RetVal = Node->CommandHandler(NULL, gST);
665       } else {
666         Node->CommandHandler(NULL, gST);
667       }
668       return (RETURN_SUCCESS);
669     }
670   }
671 
672   //
673   // An internal command was not found, try to find a dynamic command
674   //
675   DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);
676   if (DynamicCommand != NULL) {
677     if (RetVal != NULL) {
678       *RetVal = DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
679     } else {
680       DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
681     }
682     return (RETURN_SUCCESS);
683   }
684 
685   return (RETURN_NOT_FOUND);
686 }
687 
688 /**
689   Checks if a command string has been registered for CommandString and if so it
690   returns the MAN filename specified for that command.
691 
692   If CommandString is NULL, then ASSERT().
693 
694   @param[in]  CommandString         Pointer to the command name.  This is the name
695                                     found on the command line in the shell.\
696 
697   @retval NULL                      the commandString was not a registered command.
698   @return other                     the name of the MAN file.
699   @sa SHELL_GET_MAN_FILENAME
700 **/
701 CONST CHAR16*
702 EFIAPI
ShellCommandGetManFileNameHandler(IN CONST CHAR16 * CommandString)703 ShellCommandGetManFileNameHandler (
704   IN CONST CHAR16               *CommandString
705   )
706 {
707   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
708 
709   //
710   // assert for NULL parameters
711   //
712   ASSERT(CommandString != NULL);
713 
714   //
715   // check for the command
716   //
717   for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
718       ; !IsNull(&mCommandList.Link, &Node->Link)
719       ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
720      ){
721     ASSERT(Node->CommandString != NULL);
722     if (gUnicodeCollation->StriColl(
723           gUnicodeCollation,
724           (CHAR16*)CommandString,
725           Node->CommandString) == 0
726        ){
727       return (Node->GetManFileName());
728     }
729   }
730   return (NULL);
731 }
732 
733 /**
734   Get the list of all available shell internal commands.  This is a linked list
735   (via LIST_ENTRY structure).  enumerate through it using the BaseLib linked
736   list functions.  do not modify the values.
737 
738   @param[in] Sort       TRUE to alphabetically sort the values first.  FALSE otherwise.
739 
740   @return a Linked list of all available shell commands.
741 **/
742 CONST COMMAND_LIST*
743 EFIAPI
ShellCommandGetCommandList(IN CONST BOOLEAN Sort)744 ShellCommandGetCommandList (
745   IN CONST BOOLEAN Sort
746   )
747 {
748 //  if (!Sort) {
749 //    return ((COMMAND_LIST*)(&mCommandList));
750 //  }
751   return ((COMMAND_LIST*)(&mCommandList));
752 }
753 
754 /**
755   Registers aliases to be set as part of the initialization of the shell application.
756 
757   If Command is NULL, then ASSERT().
758   If Alias is NULL, then ASSERT().
759 
760   @param[in]  Command               Pointer to the Command
761   @param[in]  Alias                 Pointer to Alias
762 
763   @retval  RETURN_SUCCESS           The handlers were registered.
764   @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources available to
765                                     register the shell command.
766 **/
767 RETURN_STATUS
768 EFIAPI
ShellCommandRegisterAlias(IN CONST CHAR16 * Command,IN CONST CHAR16 * Alias)769 ShellCommandRegisterAlias (
770   IN CONST CHAR16                       *Command,
771   IN CONST CHAR16                       *Alias
772   )
773 {
774   ALIAS_LIST *Node;
775   ALIAS_LIST *CommandAlias;
776   ALIAS_LIST *PrevCommandAlias;
777   INTN       LexicalMatchValue;
778 
779   //
780   // Asserts for NULL
781   //
782   ASSERT(Command != NULL);
783   ASSERT(Alias   != NULL);
784 
785   //
786   // allocate memory for new struct
787   //
788   Node = AllocateZeroPool(sizeof(ALIAS_LIST));
789   ASSERT(Node != NULL);
790   Node->CommandString = AllocateCopyPool(StrSize(Command), Command);
791   Node->Alias = AllocateCopyPool(StrSize(Alias), Alias);
792   ASSERT(Node->CommandString != NULL);
793   ASSERT(Node->Alias != NULL);
794 
795   InsertHeadList (&mAliasList.Link, &Node->Link);
796 
797   //
798   // Move a new pre-defined registered alias to its sorted ordered location in the list
799   //
800   for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
801          PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
802        ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
803        ; CommandAlias = (ALIAS_LIST *) GetNextNode (&mAliasList.Link, &CommandAlias->Link) ) {
804     //
805     // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
806     //
807     LexicalMatchValue = gUnicodeCollation->StriColl (
808                                              gUnicodeCollation,
809                                              PrevCommandAlias->Alias,
810                                              CommandAlias->Alias
811                                              );
812 
813     //
814     // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
815     // is alphabetically greater than CommandAlias list entry
816     //
817     if (LexicalMatchValue > 0) {
818       CommandAlias = (ALIAS_LIST *) SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
819     } else if (LexicalMatchValue < 0) {
820       //
821       // PrevCommandAlias entry is lexically lower than CommandAlias entry
822       //
823       break;
824     }
825   }
826 
827   return (RETURN_SUCCESS);
828 }
829 
830 /**
831   Get the list of all shell alias commands.  This is a linked list
832   (via LIST_ENTRY structure).  enumerate through it using the BaseLib linked
833   list functions.  do not modify the values.
834 
835   @return a Linked list of all requested shell alias'.
836 **/
837 CONST ALIAS_LIST*
838 EFIAPI
ShellCommandGetInitAliasList(VOID)839 ShellCommandGetInitAliasList (
840   VOID
841   )
842 {
843     return (&mAliasList);
844 }
845 
846 /**
847   Determine if a given alias is on the list of built in alias'.
848 
849   @param[in] Alias              The alias to test for
850 
851   @retval TRUE                  The alias is a built in alias
852   @retval FALSE                 The alias is not a built in alias
853 **/
854 BOOLEAN
855 EFIAPI
ShellCommandIsOnAliasList(IN CONST CHAR16 * Alias)856 ShellCommandIsOnAliasList(
857   IN CONST CHAR16 *Alias
858   )
859 {
860   ALIAS_LIST *Node;
861 
862   //
863   // assert for NULL parameter
864   //
865   ASSERT(Alias != NULL);
866 
867   //
868   // check for the Alias
869   //
870   for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
871       ; !IsNull(&mAliasList.Link, &Node->Link)
872       ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
873      ){
874     ASSERT(Node->CommandString != NULL);
875     ASSERT(Node->Alias != NULL);
876     if (gUnicodeCollation->StriColl(
877           gUnicodeCollation,
878           (CHAR16*)Alias,
879           Node->CommandString) == 0
880        ){
881       return (TRUE);
882     }
883     if (gUnicodeCollation->StriColl(
884           gUnicodeCollation,
885           (CHAR16*)Alias,
886           Node->Alias) == 0
887        ){
888       return (TRUE);
889     }
890   }
891   return (FALSE);
892 }
893 
894 /**
895   Function to determine current state of ECHO.  Echo determines if lines from scripts
896   and ECHO commands are enabled.
897 
898   @retval TRUE    Echo is currently enabled
899   @retval FALSE   Echo is currently disabled
900 **/
901 BOOLEAN
902 EFIAPI
ShellCommandGetEchoState(VOID)903 ShellCommandGetEchoState(
904   VOID
905   )
906 {
907   return (mEchoState);
908 }
909 
910 /**
911   Function to set current state of ECHO.  Echo determines if lines from scripts
912   and ECHO commands are enabled.
913 
914   If State is TRUE, Echo will be enabled.
915   If State is FALSE, Echo will be disabled.
916 
917   @param[in] State      How to set echo.
918 **/
919 VOID
920 EFIAPI
ShellCommandSetEchoState(IN BOOLEAN State)921 ShellCommandSetEchoState(
922   IN BOOLEAN State
923   )
924 {
925   mEchoState = State;
926 }
927 
928 /**
929   Indicate that the current shell or script should exit.
930 
931   @param[in] ScriptOnly   TRUE if exiting a script; FALSE otherwise.
932   @param[in] ErrorCode    The 64 bit error code to return.
933 **/
934 VOID
935 EFIAPI
ShellCommandRegisterExit(IN BOOLEAN ScriptOnly,IN CONST UINT64 ErrorCode)936 ShellCommandRegisterExit (
937   IN BOOLEAN      ScriptOnly,
938   IN CONST UINT64 ErrorCode
939   )
940 {
941   mExitRequested = (BOOLEAN)(!mExitRequested);
942   if (mExitRequested) {
943     mExitScript    = ScriptOnly;
944   } else {
945     mExitScript    = FALSE;
946   }
947   mExitCode = ErrorCode;
948 }
949 
950 /**
951   Retrieve the Exit indicator.
952 
953   @retval TRUE      Exit was indicated.
954   @retval FALSE     Exis was not indicated.
955 **/
956 BOOLEAN
957 EFIAPI
ShellCommandGetExit(VOID)958 ShellCommandGetExit (
959   VOID
960   )
961 {
962   return (mExitRequested);
963 }
964 
965 /**
966   Retrieve the Exit code.
967 
968   If ShellCommandGetExit returns FALSE than the return from this is undefined.
969 
970   @return the value passed into RegisterExit.
971 **/
972 UINT64
973 EFIAPI
ShellCommandGetExitCode(VOID)974 ShellCommandGetExitCode (
975   VOID
976   )
977 {
978   return (mExitCode);
979 }
980 /**
981   Retrieve the Exit script indicator.
982 
983   If ShellCommandGetExit returns FALSE than the return from this is undefined.
984 
985   @retval TRUE      ScriptOnly was indicated.
986   @retval FALSE     ScriptOnly was not indicated.
987 **/
988 BOOLEAN
989 EFIAPI
ShellCommandGetScriptExit(VOID)990 ShellCommandGetScriptExit (
991   VOID
992   )
993 {
994   return (mExitScript);
995 }
996 
997 /**
998   Function to cleanup all memory from a SCRIPT_FILE structure.
999 
1000   @param[in] Script     The pointer to the structure to cleanup.
1001 **/
1002 VOID
1003 EFIAPI
DeleteScriptFileStruct(IN SCRIPT_FILE * Script)1004 DeleteScriptFileStruct (
1005   IN SCRIPT_FILE *Script
1006   )
1007 {
1008   UINT8       LoopVar;
1009 
1010   if (Script == NULL) {
1011     return;
1012   }
1013 
1014   for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
1015     SHELL_FREE_NON_NULL(Script->Argv[LoopVar]);
1016   }
1017   if (Script->Argv != NULL) {
1018     SHELL_FREE_NON_NULL(Script->Argv);
1019   }
1020   Script->CurrentCommand = NULL;
1021   while (!IsListEmpty (&Script->CommandList)) {
1022     Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
1023     if (Script->CurrentCommand != NULL) {
1024       RemoveEntryList(&Script->CurrentCommand->Link);
1025       if (Script->CurrentCommand->Cl != NULL) {
1026         SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl);
1027       }
1028       if (Script->CurrentCommand->Data != NULL) {
1029         SHELL_FREE_NON_NULL(Script->CurrentCommand->Data);
1030       }
1031       SHELL_FREE_NON_NULL(Script->CurrentCommand);
1032     }
1033   }
1034   SHELL_FREE_NON_NULL(Script->ScriptName);
1035   SHELL_FREE_NON_NULL(Script);
1036 }
1037 
1038 /**
1039   Function to return a pointer to the currently running script file object.
1040 
1041   @retval NULL        A script file is not currently running.
1042   @return             A pointer to the current script file object.
1043 **/
1044 SCRIPT_FILE*
1045 EFIAPI
ShellCommandGetCurrentScriptFile(VOID)1046 ShellCommandGetCurrentScriptFile (
1047   VOID
1048   )
1049 {
1050   SCRIPT_FILE_LIST *List;
1051   if (IsListEmpty (&mScriptList.Link)) {
1052     return (NULL);
1053   }
1054   List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
1055   return (List->Data);
1056 }
1057 
1058 /**
1059   Function to set a new script as the currently running one.
1060 
1061   This function will correctly stack and unstack nested scripts.
1062 
1063   @param[in] Script   Pointer to new script information structure.  if NULL
1064                       will remove and de-allocate the top-most Script structure.
1065 
1066   @return             A pointer to the current running script file after this
1067                       change.  NULL if removing the final script.
1068 **/
1069 SCRIPT_FILE*
1070 EFIAPI
ShellCommandSetNewScript(IN SCRIPT_FILE * Script OPTIONAL)1071 ShellCommandSetNewScript (
1072   IN SCRIPT_FILE *Script OPTIONAL
1073   )
1074 {
1075   SCRIPT_FILE_LIST *Node;
1076   if (Script == NULL) {
1077     if (IsListEmpty (&mScriptList.Link)) {
1078       return (NULL);
1079     }
1080     Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
1081     RemoveEntryList(&Node->Link);
1082     DeleteScriptFileStruct(Node->Data);
1083     FreePool(Node);
1084   } else {
1085     Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
1086     if (Node == NULL) {
1087       return (NULL);
1088     }
1089     Node->Data = Script;
1090     InsertHeadList(&mScriptList.Link, &Node->Link);
1091   }
1092   return (ShellCommandGetCurrentScriptFile());
1093 }
1094 
1095 /**
1096   Function to generate the next default mapping name.
1097 
1098   If the return value is not NULL then it must be callee freed.
1099 
1100   @param Type                   What kind of mapping name to make.
1101 
1102   @retval NULL                  a memory allocation failed.
1103   @return a new map name string
1104 **/
1105 CHAR16*
1106 EFIAPI
ShellCommandCreateNewMappingName(IN CONST SHELL_MAPPING_TYPE Type)1107 ShellCommandCreateNewMappingName(
1108   IN CONST SHELL_MAPPING_TYPE Type
1109   )
1110 {
1111   CHAR16  *String;
1112   ASSERT(Type < MappingTypeMax);
1113 
1114   String = NULL;
1115 
1116   String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
1117   UnicodeSPrint(
1118     String,
1119     PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
1120     Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
1121     Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
1122 
1123   return (String);
1124 }
1125 
1126 /**
1127   Function to add a map node to the list of map items and update the "path" environment variable (optionally).
1128 
1129   If Path is TRUE (during initialization only), the path environment variable will also be updated to include
1130   default paths on the new map name...
1131 
1132   Path should be FALSE when this function is called from the protocol SetMap function.
1133 
1134   @param[in] Name               The human readable mapped name.
1135   @param[in] DevicePath         The Device Path for this map.
1136   @param[in] Flags              The Flags attribute for this map item.
1137   @param[in] Path               TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
1138 
1139   @retval EFI_SUCCESS           The addition was sucessful.
1140   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
1141   @retval EFI_INVALID_PARAMETER A parameter was invalid.
1142 **/
1143 EFI_STATUS
1144 EFIAPI
ShellCommandAddMapItemAndUpdatePath(IN CONST CHAR16 * Name,IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN CONST UINT64 Flags,IN CONST BOOLEAN Path)1145 ShellCommandAddMapItemAndUpdatePath(
1146   IN CONST CHAR16                   *Name,
1147   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1148   IN CONST UINT64                   Flags,
1149   IN CONST BOOLEAN                  Path
1150   )
1151 {
1152   EFI_STATUS      Status;
1153   SHELL_MAP_LIST  *MapListNode;
1154   CONST CHAR16    *OriginalPath;
1155   CHAR16          *NewPath;
1156   UINTN           NewPathSize;
1157 
1158   NewPathSize = 0;
1159   NewPath = NULL;
1160   OriginalPath = NULL;
1161   Status = EFI_SUCCESS;
1162 
1163   MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
1164   if (MapListNode == NULL) {
1165     Status = EFI_OUT_OF_RESOURCES;
1166   } else {
1167     MapListNode->Flags = Flags;
1168     MapListNode->MapName = AllocateCopyPool(StrSize(Name), Name);
1169     MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
1170     if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
1171       Status = EFI_OUT_OF_RESOURCES;
1172     } else {
1173       InsertTailList(&gShellMapList.Link, &MapListNode->Link);
1174     }
1175   }
1176   if (EFI_ERROR(Status)) {
1177     if (MapListNode != NULL) {
1178       if (MapListNode->DevicePath != NULL) {
1179         FreePool(MapListNode->DevicePath);
1180       }
1181       if (MapListNode->MapName != NULL) {
1182         FreePool(MapListNode->MapName);
1183       }
1184       FreePool(MapListNode);
1185     }
1186   } else if (Path) {
1187     //
1188     // Since there was no error and Path was TRUE
1189     // Now add the correct path for that mapping
1190     //
1191     OriginalPath = gEfiShellProtocol->GetEnv(L"path");
1192     ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
1193     if (OriginalPath != NULL) {
1194       StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
1195     } else {
1196       StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0);
1197     }
1198     StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
1199     StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1200     StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
1201     StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1202     StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
1203     StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1204     StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
1205 
1206     Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
1207     ASSERT_EFI_ERROR(Status);
1208     FreePool(NewPath);
1209   }
1210   return (Status);
1211 }
1212 
1213 /**
1214   Creates the default map names for each device path in the system with
1215   a protocol depending on the Type.
1216 
1217   Creates the consistent map names for each device path in the system with
1218   a protocol depending on the Type.
1219 
1220   Note: This will reset all mappings in the system("map -r").
1221 
1222   Also sets up the default path environment variable if Type is FileSystem.
1223 
1224   @retval EFI_SUCCESS           All map names were created sucessfully.
1225   @retval EFI_NOT_FOUND         No protocols were found in the system.
1226   @return                       Error returned from gBS->LocateHandle().
1227 
1228   @sa LocateHandle
1229 **/
1230 EFI_STATUS
1231 EFIAPI
ShellCommandCreateInitialMappingsAndPaths(VOID)1232 ShellCommandCreateInitialMappingsAndPaths(
1233   VOID
1234   )
1235 {
1236   EFI_STATUS                Status;
1237   EFI_HANDLE                *HandleList;
1238   UINTN                     Count;
1239   EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;
1240   CHAR16                    *NewDefaultName;
1241   CHAR16                    *NewConsistName;
1242   EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;
1243   SHELL_MAP_LIST            *MapListNode;
1244 
1245   HandleList  = NULL;
1246 
1247   //
1248   // Reset the static members back to zero
1249   //
1250   mFsMaxCount = 0;
1251   mBlkMaxCount = 0;
1252 
1253   gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
1254 
1255   //
1256   // First empty out the existing list.
1257   //
1258   if (!IsListEmpty(&gShellMapList.Link)) {
1259     for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1260         ; !IsListEmpty(&gShellMapList.Link)
1261         ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1262        ){
1263           RemoveEntryList(&MapListNode->Link);
1264           SHELL_FREE_NON_NULL(MapListNode->DevicePath);
1265           SHELL_FREE_NON_NULL(MapListNode->MapName);
1266           SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
1267           FreePool(MapListNode);
1268     } // for loop
1269   }
1270 
1271   //
1272   // Find each handle with Simple File System
1273   //
1274   HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1275   if (HandleList != NULL) {
1276     //
1277     // Do a count of the handles
1278     //
1279     for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1280 
1281     //
1282     // Get all Device Paths
1283     //
1284     DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1285     ASSERT(DevicePathList != NULL);
1286 
1287     for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1288       DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1289     }
1290 
1291     //
1292     // Sort all DevicePaths
1293     //
1294     PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1295 
1296     ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1297     //
1298     // Assign new Mappings to all...
1299     //
1300     for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1301       //
1302       // Get default name first
1303       //
1304       NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1305       ASSERT(NewDefaultName != NULL);
1306       Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
1307       ASSERT_EFI_ERROR(Status);
1308       FreePool(NewDefaultName);
1309 
1310       //
1311       // Now do consistent name
1312       //
1313       NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1314       if (NewConsistName != NULL) {
1315         Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
1316         ASSERT_EFI_ERROR(Status);
1317         FreePool(NewConsistName);
1318       }
1319     }
1320 
1321     ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1322 
1323     SHELL_FREE_NON_NULL(HandleList);
1324     SHELL_FREE_NON_NULL(DevicePathList);
1325 
1326     HandleList = NULL;
1327   } else {
1328     Count = (UINTN)-1;
1329   }
1330 
1331   //
1332   // Find each handle with Block Io
1333   //
1334   HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid);
1335   if (HandleList != NULL) {
1336     for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1337 
1338     //
1339     // Get all Device Paths
1340     //
1341     DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1342     ASSERT(DevicePathList != NULL);
1343 
1344     for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1345       DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1346     }
1347 
1348     //
1349     // Sort all DevicePaths
1350     //
1351     PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1352 
1353     //
1354     // Assign new Mappings to all...
1355     //
1356     for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1357       //
1358       // Get default name first
1359       //
1360       NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
1361       ASSERT(NewDefaultName != NULL);
1362       Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
1363       ASSERT_EFI_ERROR(Status);
1364       FreePool(NewDefaultName);
1365     }
1366 
1367     SHELL_FREE_NON_NULL(HandleList);
1368     SHELL_FREE_NON_NULL(DevicePathList);
1369   } else if (Count == (UINTN)-1) {
1370     return (EFI_NOT_FOUND);
1371   }
1372 
1373   return (EFI_SUCCESS);
1374 }
1375 
1376 /**
1377   Add mappings for any devices without one.  Do not change any existing maps.
1378 
1379   @retval EFI_SUCCESS   The operation was successful.
1380 **/
1381 EFI_STATUS
1382 EFIAPI
ShellCommandUpdateMapping(VOID)1383 ShellCommandUpdateMapping (
1384   VOID
1385   )
1386 {
1387   EFI_STATUS                Status;
1388   EFI_HANDLE                *HandleList;
1389   UINTN                     Count;
1390   EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;
1391   CHAR16                    *NewDefaultName;
1392   CHAR16                    *NewConsistName;
1393   EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;
1394 
1395   HandleList  = NULL;
1396   Status      = EFI_SUCCESS;
1397 
1398   //
1399   // remove mappings that represent removed devices.
1400   //
1401 
1402   //
1403   // Find each handle with Simple File System
1404   //
1405   HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1406   if (HandleList != NULL) {
1407     //
1408     // Do a count of the handles
1409     //
1410     for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1411 
1412     //
1413     // Get all Device Paths
1414     //
1415     DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1416     if (DevicePathList == NULL) {
1417       return (EFI_OUT_OF_RESOURCES);
1418     }
1419 
1420     for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1421       DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1422     }
1423 
1424     //
1425     // Sort all DevicePaths
1426     //
1427     PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1428 
1429     ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1430 
1431     //
1432     // Assign new Mappings to remainders
1433     //
1434     for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {
1435       //
1436       // Skip ones that already have
1437       //
1438       if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {
1439         continue;
1440       }
1441       //
1442       // Get default name
1443       //
1444       NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1445       if (NewDefaultName == NULL) {
1446         Status = EFI_OUT_OF_RESOURCES;
1447         break;
1448       }
1449 
1450       //
1451       // Call shell protocol SetMap function now...
1452       //
1453       Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);
1454 
1455       if (!EFI_ERROR(Status)) {
1456         //
1457         // Now do consistent name
1458         //
1459         NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1460         if (NewConsistName != NULL) {
1461           Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);
1462           FreePool(NewConsistName);
1463         }
1464       }
1465 
1466       FreePool(NewDefaultName);
1467     }
1468     ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1469     SHELL_FREE_NON_NULL(HandleList);
1470     SHELL_FREE_NON_NULL(DevicePathList);
1471 
1472     HandleList = NULL;
1473   } else {
1474     Count = (UINTN)-1;
1475   }
1476   //
1477   // Do it all over again for gEfiBlockIoProtocolGuid
1478   //
1479 
1480   return (Status);
1481 }
1482 
1483 /**
1484   Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
1485 
1486   @param[in] Handle     The SHELL_FILE_HANDLE to convert.
1487 
1488   @return a EFI_FILE_PROTOCOL* representing the same file.
1489 **/
1490 EFI_FILE_PROTOCOL*
1491 EFIAPI
ConvertShellHandleToEfiFileProtocol(IN CONST SHELL_FILE_HANDLE Handle)1492 ConvertShellHandleToEfiFileProtocol(
1493   IN CONST SHELL_FILE_HANDLE Handle
1494   )
1495 {
1496   return ((EFI_FILE_PROTOCOL*)(Handle));
1497 }
1498 
1499 /**
1500   Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
1501 
1502   @param[in] Handle     The pointer to EFI_FILE_PROTOCOL to convert.
1503   @param[in] Path       The path to the file for verification.
1504 
1505   @return               A SHELL_FILE_HANDLE representing the same file.
1506   @retval NULL          There was not enough memory.
1507 **/
1508 SHELL_FILE_HANDLE
1509 EFIAPI
ConvertEfiFileProtocolToShellHandle(IN CONST EFI_FILE_PROTOCOL * Handle,IN CONST CHAR16 * Path)1510 ConvertEfiFileProtocolToShellHandle(
1511   IN CONST EFI_FILE_PROTOCOL *Handle,
1512   IN CONST CHAR16            *Path
1513   )
1514 {
1515   SHELL_COMMAND_FILE_HANDLE *Buffer;
1516   BUFFER_LIST               *NewNode;
1517 
1518   if (Path != NULL) {
1519     Buffer              = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
1520     if (Buffer == NULL) {
1521       return (NULL);
1522     }
1523     NewNode             = AllocateZeroPool(sizeof(BUFFER_LIST));
1524     if (NewNode == NULL) {
1525       SHELL_FREE_NON_NULL(Buffer);
1526       return (NULL);
1527     }
1528     Buffer->FileHandle  = (EFI_FILE_PROTOCOL*)Handle;
1529     Buffer->Path        = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
1530     if (Buffer->Path == NULL) {
1531       SHELL_FREE_NON_NULL(NewNode);
1532       SHELL_FREE_NON_NULL(Buffer);
1533       return (NULL);
1534     }
1535     NewNode->Buffer     = Buffer;
1536 
1537     InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
1538   }
1539   return ((SHELL_FILE_HANDLE)(Handle));
1540 }
1541 
1542 /**
1543   Find the path that was logged with the specified SHELL_FILE_HANDLE.
1544 
1545   @param[in] Handle     The SHELL_FILE_HANDLE to query on.
1546 
1547   @return A pointer to the path for the file.
1548 **/
1549 CONST CHAR16*
1550 EFIAPI
ShellFileHandleGetPath(IN CONST SHELL_FILE_HANDLE Handle)1551 ShellFileHandleGetPath(
1552   IN CONST SHELL_FILE_HANDLE Handle
1553   )
1554 {
1555   BUFFER_LIST               *Node;
1556 
1557   for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1558     ;  !IsNull(&mFileHandleList.Link, &Node->Link)
1559     ;  Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1560    ){
1561     if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1562       return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1563     }
1564   }
1565   return (NULL);
1566 }
1567 
1568 /**
1569   Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
1570 
1571   @param[in] Handle     The SHELL_FILE_HANDLE to remove.
1572 
1573   @retval TRUE          The item was removed.
1574   @retval FALSE         The item was not found.
1575 **/
1576 BOOLEAN
1577 EFIAPI
ShellFileHandleRemove(IN CONST SHELL_FILE_HANDLE Handle)1578 ShellFileHandleRemove(
1579   IN CONST SHELL_FILE_HANDLE Handle
1580   )
1581 {
1582   BUFFER_LIST               *Node;
1583 
1584   for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1585     ;  !IsNull(&mFileHandleList.Link, &Node->Link)
1586     ;  Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1587    ){
1588     if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1589       RemoveEntryList(&Node->Link);
1590       SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1591       SHELL_FREE_NON_NULL(Node->Buffer);
1592       SHELL_FREE_NON_NULL(Node);
1593       return (TRUE);
1594     }
1595   }
1596   return (FALSE);
1597 }
1598 
1599 /**
1600   Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
1601 
1602   This will NOT work on directories.
1603 
1604   If Handle is NULL, then ASSERT.
1605 
1606   @param[in] Handle     the file handle
1607 
1608   @retval TRUE          the position is at the end of the file
1609   @retval FALSE         the position is not at the end of the file
1610 **/
1611 BOOLEAN
1612 EFIAPI
ShellFileHandleEof(IN SHELL_FILE_HANDLE Handle)1613 ShellFileHandleEof(
1614   IN SHELL_FILE_HANDLE Handle
1615   )
1616 {
1617   EFI_FILE_INFO *Info;
1618   UINT64        Pos;
1619   BOOLEAN       RetVal;
1620 
1621   //
1622   // ASSERT if Handle is NULL
1623   //
1624   ASSERT(Handle != NULL);
1625 
1626   gEfiShellProtocol->GetFilePosition(Handle, &Pos);
1627   Info = gEfiShellProtocol->GetFileInfo (Handle);
1628   gEfiShellProtocol->SetFilePosition(Handle, Pos);
1629 
1630   if (Info == NULL) {
1631     return (FALSE);
1632   }
1633 
1634   if (Pos == Info->FileSize) {
1635     RetVal = TRUE;
1636   } else {
1637     RetVal = FALSE;
1638   }
1639 
1640   FreePool (Info);
1641 
1642   return (RetVal);
1643 }
1644 
1645 /**
1646   Frees any BUFFER_LIST defined type.
1647 
1648   @param[in] List     The BUFFER_LIST object to free.
1649 **/
1650 VOID
1651 EFIAPI
FreeBufferList(IN BUFFER_LIST * List)1652 FreeBufferList (
1653   IN BUFFER_LIST *List
1654   )
1655 {
1656   BUFFER_LIST               *BufferListEntry;
1657 
1658   if (List == NULL){
1659     return;
1660   }
1661   //
1662   // enumerate through the buffer list and free all memory
1663   //
1664   for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
1665       ; !IsListEmpty (&List->Link)
1666       ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
1667      ){
1668     RemoveEntryList(&BufferListEntry->Link);
1669     if (BufferListEntry->Buffer != NULL) {
1670       FreePool(BufferListEntry->Buffer);
1671     }
1672     FreePool(BufferListEntry);
1673   }
1674 }
1675 
1676