1 /** @file
2   function declarations for shell environment functions.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "Shell.h"
16 
17 #define INIT_NAME_BUFFER_SIZE  128
18 #define INIT_DATA_BUFFER_SIZE  1024
19 
20 //
21 // The list is used to cache the environment variables.
22 //
23 ENV_VAR_LIST                   gShellEnvVarList;
24 
25 /**
26   Reports whether an environment variable is Volatile or Non-Volatile.
27 
28   @param EnvVarName             The name of the environment variable in question
29   @param Volatile               Return TRUE if the environment variable is volatile
30 
31   @retval EFI_SUCCESS           The volatile attribute is returned successfully
32   @retval others                Some errors happened.
33 **/
34 EFI_STATUS
IsVolatileEnv(IN CONST CHAR16 * EnvVarName,OUT BOOLEAN * Volatile)35 IsVolatileEnv (
36   IN CONST CHAR16 *EnvVarName,
37   OUT BOOLEAN     *Volatile
38   )
39 {
40   EFI_STATUS  Status;
41   UINTN       Size;
42   VOID        *Buffer;
43   UINT32      Attribs;
44 
45   ASSERT (Volatile != NULL);
46 
47   Size = 0;
48   Buffer = NULL;
49 
50   //
51   // get the variable
52   //
53   Status = gRT->GetVariable((CHAR16*)EnvVarName,
54                             &gShellVariableGuid,
55                             &Attribs,
56                             &Size,
57                             Buffer);
58   if (Status == EFI_BUFFER_TOO_SMALL) {
59     Buffer = AllocateZeroPool(Size);
60     if (Buffer == NULL) {
61       return EFI_OUT_OF_RESOURCES;
62     }
63     Status = gRT->GetVariable((CHAR16*)EnvVarName,
64                               &gShellVariableGuid,
65                               &Attribs,
66                               &Size,
67                               Buffer);
68     FreePool(Buffer);
69   }
70   //
71   // not found means volatile
72   //
73   if (Status == EFI_NOT_FOUND) {
74     *Volatile = TRUE;
75     return EFI_SUCCESS;
76   }
77   if (EFI_ERROR (Status)) {
78     return Status;
79   }
80 
81   //
82   // check for the Non Volatile bit
83   //
84   *Volatile = !(BOOLEAN) ((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE);
85   return EFI_SUCCESS;
86 }
87 
88 /**
89   free function for ENV_VAR_LIST objects.
90 
91   @param[in] List               The pointer to pointer to list.
92 **/
93 VOID
FreeEnvironmentVariableList(IN LIST_ENTRY * List)94 FreeEnvironmentVariableList(
95   IN LIST_ENTRY *List
96   )
97 {
98   ENV_VAR_LIST *Node;
99 
100   ASSERT (List != NULL);
101   if (List == NULL) {
102     return;
103   }
104 
105   for ( Node = (ENV_VAR_LIST*)GetFirstNode(List)
106       ; !IsListEmpty(List)
107       ; Node = (ENV_VAR_LIST*)GetFirstNode(List)
108      ){
109     ASSERT(Node != NULL);
110     RemoveEntryList(&Node->Link);
111     if (Node->Key != NULL) {
112       FreePool(Node->Key);
113     }
114     if (Node->Val != NULL) {
115       FreePool(Node->Val);
116     }
117     FreePool(Node);
118   }
119 }
120 
121 /**
122   Creates a list of all Shell-Guid-based environment variables.
123 
124   @param[in, out] ListHead       The pointer to pointer to LIST ENTRY object for
125                                  storing this list.
126 
127   @retval EFI_SUCCESS           the list was created sucessfully.
128 **/
129 EFI_STATUS
GetEnvironmentVariableList(IN OUT LIST_ENTRY * ListHead)130 GetEnvironmentVariableList(
131   IN OUT LIST_ENTRY *ListHead
132   )
133 {
134   CHAR16            *VariableName;
135   UINTN             NameSize;
136   UINTN             NameBufferSize;
137   EFI_STATUS        Status;
138   EFI_GUID          Guid;
139   UINTN             ValSize;
140   UINTN             ValBufferSize;
141   ENV_VAR_LIST      *VarList;
142 
143   if (ListHead == NULL) {
144     return (EFI_INVALID_PARAMETER);
145   }
146 
147   Status = EFI_SUCCESS;
148 
149   ValBufferSize = INIT_DATA_BUFFER_SIZE;
150   NameBufferSize = INIT_NAME_BUFFER_SIZE;
151   VariableName = AllocateZeroPool(NameBufferSize);
152   if (VariableName == NULL) {
153     return (EFI_OUT_OF_RESOURCES);
154   }
155   *VariableName = CHAR_NULL;
156 
157   while (!EFI_ERROR(Status)) {
158     NameSize = NameBufferSize;
159     Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
160     if (Status == EFI_NOT_FOUND){
161       Status = EFI_SUCCESS;
162       break;
163     } else if (Status == EFI_BUFFER_TOO_SMALL) {
164       NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
165       SHELL_FREE_NON_NULL(VariableName);
166       VariableName = AllocateZeroPool(NameBufferSize);
167       if (VariableName == NULL) {
168         Status = EFI_OUT_OF_RESOURCES;
169         break;
170       }
171       NameSize = NameBufferSize;
172       Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
173     }
174 
175     if (!EFI_ERROR(Status) && CompareGuid(&Guid, &gShellVariableGuid)){
176       VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST));
177       if (VarList == NULL) {
178         Status = EFI_OUT_OF_RESOURCES;
179       } else {
180         ValSize = ValBufferSize;
181         //
182         // We need another CHAR16 to save '\0' in VarList->Val.
183         //
184         VarList->Val = AllocateZeroPool (ValSize + sizeof (CHAR16));
185         if (VarList->Val == NULL) {
186             SHELL_FREE_NON_NULL(VarList);
187             Status = EFI_OUT_OF_RESOURCES;
188             break;
189         }
190         Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val);
191         if (Status == EFI_BUFFER_TOO_SMALL){
192           ValBufferSize = ValSize > ValBufferSize * 2 ? ValSize : ValBufferSize * 2;
193           SHELL_FREE_NON_NULL (VarList->Val);
194           //
195           // We need another CHAR16 to save '\0' in VarList->Val.
196           //
197           VarList->Val = AllocateZeroPool (ValBufferSize + sizeof (CHAR16));
198           if (VarList->Val == NULL) {
199             SHELL_FREE_NON_NULL(VarList);
200             Status = EFI_OUT_OF_RESOURCES;
201             break;
202           }
203 
204           ValSize = ValBufferSize;
205           Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val);
206         }
207         if (!EFI_ERROR(Status)) {
208           VarList->Key = AllocateCopyPool(StrSize(VariableName), VariableName);
209           if (VarList->Key == NULL) {
210             SHELL_FREE_NON_NULL(VarList->Val);
211             SHELL_FREE_NON_NULL(VarList);
212             Status = EFI_OUT_OF_RESOURCES;
213           } else {
214             InsertTailList(ListHead, &VarList->Link);
215           }
216         } else {
217           SHELL_FREE_NON_NULL(VarList->Val);
218           SHELL_FREE_NON_NULL(VarList);
219         }
220       } // if (VarList == NULL) ... else ...
221     } // compare guid
222   } // while
223   SHELL_FREE_NON_NULL (VariableName);
224 
225   if (EFI_ERROR(Status)) {
226     FreeEnvironmentVariableList(ListHead);
227   }
228 
229   return (Status);
230 }
231 
232 /**
233   Sets a list of all Shell-Guid-based environment variables.  this will
234   also eliminate all existing shell environment variables (even if they
235   are not on the list).
236 
237   This function will also deallocate the memory from List.
238 
239   @param[in] ListHead           The pointer to LIST_ENTRY from
240                                 GetShellEnvVarList().
241 
242   @retval EFI_SUCCESS           the list was Set sucessfully.
243 **/
244 EFI_STATUS
SetEnvironmentVariableList(IN LIST_ENTRY * ListHead)245 SetEnvironmentVariableList(
246   IN LIST_ENTRY *ListHead
247   )
248 {
249   ENV_VAR_LIST      VarList;
250   ENV_VAR_LIST      *Node;
251   EFI_STATUS        Status;
252   UINTN             Size;
253 
254   InitializeListHead(&VarList.Link);
255 
256   //
257   // Delete all the current environment variables
258   //
259   Status = GetEnvironmentVariableList(&VarList.Link);
260   ASSERT_EFI_ERROR(Status);
261 
262   for ( Node = (ENV_VAR_LIST*)GetFirstNode(&VarList.Link)
263       ; !IsNull(&VarList.Link, &Node->Link)
264       ; Node = (ENV_VAR_LIST*)GetNextNode(&VarList.Link, &Node->Link)
265      ){
266     if (Node->Key != NULL) {
267       Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Node->Key);
268     }
269     ASSERT_EFI_ERROR(Status);
270   }
271 
272   FreeEnvironmentVariableList(&VarList.Link);
273 
274   //
275   // set all the variables fron the list
276   //
277   for ( Node = (ENV_VAR_LIST*)GetFirstNode(ListHead)
278       ; !IsNull(ListHead, &Node->Link)
279       ; Node = (ENV_VAR_LIST*)GetNextNode(ListHead, &Node->Link)
280      ){
281     Size = StrSize (Node->Val) - sizeof (CHAR16);
282     if (Node->Atts & EFI_VARIABLE_NON_VOLATILE) {
283       Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV(Node->Key, Size, Node->Val);
284     } else {
285       Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (Node->Key, Size, Node->Val);
286     }
287     ASSERT_EFI_ERROR(Status);
288   }
289   FreeEnvironmentVariableList(ListHead);
290 
291   return (Status);
292 }
293 
294 /**
295   sets a list of all Shell-Guid-based environment variables.
296 
297   @param Environment        Points to a NULL-terminated array of environment
298                             variables with the format 'x=y', where x is the
299                             environment variable name and y is the value.
300 
301   @retval EFI_SUCCESS       The command executed successfully.
302   @retval EFI_INVALID_PARAMETER The parameter is invalid.
303   @retval EFI_OUT_OF_RESOURCES Out of resources.
304 
305   @sa SetEnvironmentVariableList
306 **/
307 EFI_STATUS
SetEnvironmentVariables(IN CONST CHAR16 ** Environment)308 SetEnvironmentVariables(
309   IN CONST CHAR16 **Environment
310   )
311 {
312   CONST CHAR16  *CurrentString;
313   UINTN         CurrentCount;
314   ENV_VAR_LIST  *VarList;
315   ENV_VAR_LIST  *Node;
316 
317   VarList = NULL;
318 
319   if (Environment == NULL) {
320     return (EFI_INVALID_PARAMETER);
321   }
322 
323   //
324   // Build a list identical to the ones used for get/set list functions above
325   //
326   for ( CurrentCount = 0
327       ;
328       ; CurrentCount++
329      ){
330     CurrentString = Environment[CurrentCount];
331     if (CurrentString == NULL) {
332       break;
333     }
334     ASSERT(StrStr(CurrentString, L"=") != NULL);
335     Node = AllocateZeroPool(sizeof(ENV_VAR_LIST));
336     if (Node == NULL) {
337       SetEnvironmentVariableList(&VarList->Link);
338       return (EFI_OUT_OF_RESOURCES);
339     }
340 
341     Node->Key = AllocateZeroPool((StrStr(CurrentString, L"=") - CurrentString + 1) * sizeof(CHAR16));
342     if (Node->Key == NULL) {
343       SHELL_FREE_NON_NULL(Node);
344       SetEnvironmentVariableList(&VarList->Link);
345       return (EFI_OUT_OF_RESOURCES);
346     }
347 
348     //
349     // Copy the string into the Key, leaving the last character allocated as NULL to terminate
350     //
351     StrnCpyS( Node->Key,
352               StrStr(CurrentString, L"=") - CurrentString + 1,
353               CurrentString,
354               StrStr(CurrentString, L"=") - CurrentString
355               );
356 
357     //
358     // ValueSize = TotalSize - already removed size - size for '=' + size for terminator (the last 2 items cancel each other)
359     //
360     Node->Val = AllocateCopyPool(StrSize(CurrentString) - StrSize(Node->Key), CurrentString + StrLen(Node->Key) + 1);
361     if (Node->Val == NULL) {
362       SHELL_FREE_NON_NULL(Node->Key);
363       SHELL_FREE_NON_NULL(Node);
364       SetEnvironmentVariableList(&VarList->Link);
365       return (EFI_OUT_OF_RESOURCES);
366     }
367 
368     Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS;
369 
370     if (VarList == NULL) {
371       VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST));
372       if (VarList == NULL) {
373         SHELL_FREE_NON_NULL(Node->Key);
374         SHELL_FREE_NON_NULL(Node->Val);
375         SHELL_FREE_NON_NULL(Node);
376         return (EFI_OUT_OF_RESOURCES);
377       }
378       InitializeListHead(&VarList->Link);
379     }
380     InsertTailList(&VarList->Link, &Node->Link);
381 
382   } // for loop
383 
384   //
385   // set this new list as the set of all environment variables.
386   // this function also frees the memory and deletes all pre-existing
387   // shell-guid based environment variables.
388   //
389   return (SetEnvironmentVariableList(&VarList->Link));
390 }
391 
392 /**
393   Find an environment variable in the gShellEnvVarList.
394 
395   @param Key        The name of the environment variable.
396   @param Value      The value of the environment variable, the buffer
397                     shoule be freed by the caller.
398   @param ValueSize  The size in bytes of the environment variable
399                     including the tailing CHAR_NELL.
400   @param Atts       The attributes of the variable.
401 
402   @retval EFI_SUCCESS       The command executed successfully.
403   @retval EFI_NOT_FOUND     The environment variable is not found in
404                             gShellEnvVarList.
405 
406 **/
407 EFI_STATUS
ShellFindEnvVarInList(IN CONST CHAR16 * Key,OUT CHAR16 ** Value,OUT UINTN * ValueSize,OUT UINT32 * Atts OPTIONAL)408 ShellFindEnvVarInList (
409   IN  CONST CHAR16    *Key,
410   OUT CHAR16          **Value,
411   OUT UINTN           *ValueSize,
412   OUT UINT32          *Atts OPTIONAL
413   )
414 {
415   ENV_VAR_LIST      *Node;
416 
417   if (Key == NULL || Value == NULL || ValueSize == NULL) {
418     return SHELL_INVALID_PARAMETER;
419   }
420 
421   for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
422       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
423       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
424      ){
425     if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
426       *Value      = AllocateCopyPool(StrSize(Node->Val), Node->Val);
427       *ValueSize  = StrSize(Node->Val);
428       if (Atts != NULL) {
429         *Atts = Node->Atts;
430       }
431       return EFI_SUCCESS;
432     }
433   }
434 
435   return EFI_NOT_FOUND;
436 }
437 
438 /**
439   Add an environment variable into gShellEnvVarList.
440 
441   @param Key        The name of the environment variable.
442   @param Value      The value of environment variable.
443   @param ValueSize  The size in bytes of the environment variable
444                     including the tailing CHAR_NULL
445   @param Atts       The attributes of the variable.
446 
447   @retval EFI_SUCCESS  The environment variable was added to list successfully.
448   @retval others       Some errors happened.
449 
450 **/
451 EFI_STATUS
ShellAddEnvVarToList(IN CONST CHAR16 * Key,IN CONST CHAR16 * Value,IN UINTN ValueSize,IN UINT32 Atts)452 ShellAddEnvVarToList (
453   IN CONST CHAR16     *Key,
454   IN CONST CHAR16     *Value,
455   IN UINTN            ValueSize,
456   IN UINT32           Atts
457   )
458 {
459   ENV_VAR_LIST      *Node;
460   CHAR16            *LocalKey;
461   CHAR16            *LocalValue;
462 
463   if (Key == NULL || Value == NULL || ValueSize == 0) {
464     return EFI_INVALID_PARAMETER;
465   }
466 
467   LocalValue = AllocateCopyPool (ValueSize, Value);
468   if (LocalValue == NULL) {
469     return EFI_OUT_OF_RESOURCES;
470   }
471 
472   //
473   // Update the variable value if it exists in gShellEnvVarList.
474   //
475   for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
476       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
477       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
478      ){
479     if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
480       Node->Atts = Atts;
481       SHELL_FREE_NON_NULL(Node->Val);
482       Node->Val  = LocalValue;
483       return EFI_SUCCESS;
484     }
485   }
486 
487   //
488   // If the environment varialbe key doesn't exist in list just insert
489   // a new node.
490   //
491   LocalKey = AllocateCopyPool (StrSize(Key), Key);
492   if (LocalKey == NULL) {
493     FreePool (LocalValue);
494     return EFI_OUT_OF_RESOURCES;
495   }
496   Node = (ENV_VAR_LIST*)AllocateZeroPool (sizeof(ENV_VAR_LIST));
497   if (Node == NULL) {
498     FreePool (LocalKey);
499     FreePool (LocalValue);
500     return EFI_OUT_OF_RESOURCES;
501   }
502   Node->Key = LocalKey;
503   Node->Val = LocalValue;
504   Node->Atts = Atts;
505   InsertTailList(&gShellEnvVarList.Link, &Node->Link);
506 
507   return EFI_SUCCESS;
508 }
509 
510 /**
511   Remove a specified environment variable in gShellEnvVarList.
512 
513   @param Key        The name of the environment variable.
514 
515   @retval EFI_SUCCESS       The command executed successfully.
516   @retval EFI_NOT_FOUND     The environment variable is not found in
517                             gShellEnvVarList.
518 **/
519 EFI_STATUS
ShellRemvoeEnvVarFromList(IN CONST CHAR16 * Key)520 ShellRemvoeEnvVarFromList (
521   IN CONST CHAR16           *Key
522   )
523 {
524   ENV_VAR_LIST      *Node;
525 
526   if (Key == NULL) {
527     return EFI_INVALID_PARAMETER;
528   }
529 
530   for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
531       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
532       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
533      ){
534     if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
535       SHELL_FREE_NON_NULL(Node->Key);
536       SHELL_FREE_NON_NULL(Node->Val);
537       RemoveEntryList(&Node->Link);
538       SHELL_FREE_NON_NULL(Node);
539       return EFI_SUCCESS;
540     }
541   }
542 
543   return EFI_NOT_FOUND;
544 }
545 
546 /**
547   Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
548   variables.
549 
550 **/
551 EFI_STATUS
ShellInitEnvVarList(VOID)552 ShellInitEnvVarList (
553   VOID
554   )
555 {
556   EFI_STATUS    Status;
557 
558   InitializeListHead(&gShellEnvVarList.Link);
559   Status = GetEnvironmentVariableList (&gShellEnvVarList.Link);
560 
561   return Status;
562 }
563 
564 /**
565   Destructe the gShellEnvVarList.
566 
567 **/
568 VOID
ShellFreeEnvVarList(VOID)569 ShellFreeEnvVarList (
570   VOID
571   )
572 {
573   FreeEnvironmentVariableList (&gShellEnvVarList.Link);
574   InitializeListHead(&gShellEnvVarList.Link);
575 
576   return;
577 }
578 
579