1 /** @file
2   Main file for attrib shell level 2 function.
3 
4   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
5   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
6   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
7   Copyright (c) 2018, Dell Technologies. All rights reserved.<BR>
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include "UefiShellLevel2CommandsLib.h"
13 
14 /**
15   Function will replace drive identifier with CWD.
16 
17   If FullPath begining with ':' is invalid path, then ASSERT.
18   If FullPath not include dirve identifier , then do nothing.
19   If FullPath likes "fs0:\xx" or "fs0:/xx" , then do nothing.
20   If FullPath likes "fs0:xxx" or "fs0:", the drive replaced by CWD.
21 
22   @param[in, out]   FullPath    The pointer to the string containing the path.
23   @param[in]        Cwd         Current directory.
24 
25   @retval   EFI_SUCCESS         Success.
26   @retval   EFI_OUT_OF_SOURCES  A memory allocation failed.
27 **/
28 EFI_STATUS
ReplaceDriveWithCwd(IN OUT CHAR16 ** FullPath,IN CONST CHAR16 * Cwd)29 ReplaceDriveWithCwd (
30   IN OUT    CHAR16  **FullPath,
31   IN CONST  CHAR16  *Cwd
32   )
33 {
34   CHAR16        *Splitter;
35   CHAR16        *TempBuffer;
36   UINTN         TotalSize;
37 
38   Splitter   = NULL;
39   TempBuffer = NULL;
40   TotalSize  = 0;
41 
42   if (FullPath == NULL || *FullPath == NULL) {
43     return EFI_SUCCESS;
44   }
45 
46   Splitter = StrStr (*FullPath, L":");
47   ASSERT(Splitter != *FullPath);
48 
49   if (Splitter != NULL && *(Splitter + 1) != L'\\' && *(Splitter + 1) != L'/') {
50     TotalSize = StrSize (Cwd) + StrSize (Splitter + 1);
51     TempBuffer = AllocateZeroPool (TotalSize);
52     if (TempBuffer == NULL) {
53       return EFI_OUT_OF_RESOURCES;
54     }
55 
56     StrCpyS (TempBuffer, TotalSize / sizeof(CHAR16), Cwd);
57     StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), L"\\");
58     StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), Splitter + 1);
59 
60     FreePool(*FullPath);
61     *FullPath = TempBuffer;
62   }
63 
64   return EFI_SUCCESS;
65 }
66 
67 /**
68   function to determine if FullPath is under current filesystem.
69 
70   @param[in]    FullPath    The target location to determine.
71   @param[in]    Cwd         Current directory.
72 
73   @retval       TRUE        The FullPath is in the current filesystem.
74   @retval       FALSE       The FullPaht isn't in the current filesystem.
75 **/
76 BOOLEAN
IsCurrentFileSystem(IN CONST CHAR16 * FullPath,IN CONST CHAR16 * Cwd)77 IsCurrentFileSystem (
78   IN CONST CHAR16   *FullPath,
79   IN CONST CHAR16   *Cwd
80   )
81 {
82   CHAR16 *Splitter1;
83   CHAR16 *Splitter2;
84 
85   Splitter1 = NULL;
86   Splitter2 = NULL;
87 
88   ASSERT(FullPath != NULL);
89 
90   Splitter1 = StrStr (FullPath, L":");
91   if (Splitter1 == NULL) {
92     return TRUE;
93   }
94 
95   Splitter2 = StrStr (Cwd, L":");
96 
97   if (((UINTN) Splitter1 - (UINTN) FullPath) != ((UINTN) Splitter2 - (UINTN) Cwd)) {
98     return FALSE;
99   } else {
100     if (StrniCmp (FullPath, Cwd, ((UINTN) Splitter1 - (UINTN) FullPath) / sizeof (CHAR16)) == 0) {
101       return TRUE;
102     } else {
103       return FALSE;
104     }
105   }
106 }
107 
108 /**
109   Extract drive string and path string from FullPath.
110 
111   The caller must be free Drive and Path.
112 
113   @param[in]    FullPath    A path to be extracted.
114   @param[out]   Drive       Buffer to save drive identifier.
115   @param[out]   Path        Buffer to save path.
116 
117   @retval       EFI_SUCCESS           Success.
118   @retval       EFI_OUT_OF_RESOUCES   A memory allocation failed.
119 **/
120 EFI_STATUS
ExtractDriveAndPath(IN CONST CHAR16 * FullPath,OUT CHAR16 ** Drive,OUT CHAR16 ** Path)121 ExtractDriveAndPath (
122   IN CONST CHAR16   *FullPath,
123   OUT CHAR16        **Drive,
124   OUT CHAR16        **Path
125   )
126 {
127   CHAR16 *Splitter;
128 
129   ASSERT (FullPath != NULL);
130 
131   Splitter = StrStr (FullPath, L":");
132 
133   if (Splitter == NULL) {
134     *Drive = NULL;
135     *Path = AllocateCopyPool (StrSize (FullPath), FullPath);
136     if (*Path == NULL) {
137       return EFI_OUT_OF_RESOURCES;
138     }
139   } else {
140     if (*(Splitter + 1) == CHAR_NULL) {
141       *Drive = AllocateCopyPool (StrSize (FullPath), FullPath);
142       *Path = NULL;
143       if (*Drive == NULL) {
144         return EFI_OUT_OF_RESOURCES;
145       }
146     } else {
147       *Drive = AllocateCopyPool ((Splitter - FullPath + 2) * sizeof(CHAR16), FullPath);
148       if (*Drive == NULL) {
149         return EFI_OUT_OF_RESOURCES;
150       }
151       (*Drive)[Splitter - FullPath + 1] = CHAR_NULL;
152 
153       *Path = AllocateCopyPool (StrSize (Splitter + 1), Splitter + 1);
154       if (*Path == NULL) {
155         FreePool (*Drive);
156         return EFI_OUT_OF_RESOURCES;
157       }
158     }
159   }
160 
161   return EFI_SUCCESS;
162 }
163 
164 /**
165   Function for 'cd' command.
166 
167   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
168   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
169 **/
170 SHELL_STATUS
171 EFIAPI
ShellCommandRunCd(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)172 ShellCommandRunCd (
173   IN EFI_HANDLE        ImageHandle,
174   IN EFI_SYSTEM_TABLE  *SystemTable
175   )
176 {
177   EFI_STATUS        Status;
178   LIST_ENTRY        *Package;
179   CONST CHAR16      *Cwd;
180   CHAR16            *Path;
181   CHAR16            *Drive;
182   CHAR16            *ProblemParam;
183   SHELL_STATUS      ShellStatus;
184   CONST CHAR16      *Param1;
185   CHAR16            *Param1Copy;
186   CHAR16            *Walker;
187   CHAR16            *Splitter;
188   CHAR16            *TempBuffer;
189   UINTN             TotalSize;
190 
191   ProblemParam  = NULL;
192   ShellStatus   = SHELL_SUCCESS;
193   Cwd           = NULL;
194   Path          = NULL;
195   Drive         = NULL;
196   Splitter      = NULL;
197   TempBuffer    = NULL;
198   TotalSize     = 0;
199 
200   Status = CommandInit();
201   ASSERT_EFI_ERROR(Status);
202 
203   //
204   // initialize the shell lib (we must be in non-auto-init...)
205   //
206   Status = ShellInitialize();
207   ASSERT_EFI_ERROR(Status);
208 
209   //
210   // parse the command line
211   //
212   Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
213   if (EFI_ERROR(Status)) {
214     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
215       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"cd", ProblemParam);
216       FreePool(ProblemParam);
217       ShellStatus = SHELL_INVALID_PARAMETER;
218     } else {
219       ASSERT(FALSE);
220     }
221   }
222 
223   //
224   // check for "-?"
225   //
226   if (ShellCommandLineGetFlag(Package, L"-?")) {
227     ASSERT(FALSE);
228   } else if (ShellCommandLineGetRawValue(Package, 2) != NULL) {
229     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"cd");
230     ShellStatus = SHELL_INVALID_PARAMETER;
231   } else {
232     //
233     // remember that param 0 is the command name
234     // If there are 0 value parameters, then print the current directory
235     // else If there are 2 value parameters, then print the error message
236     // else If there is  1 value paramerer , then change the directory
237     //
238     Cwd = ShellGetCurrentDir (NULL);
239     if (Cwd == NULL) {
240       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");
241       ShellStatus = SHELL_NOT_FOUND;
242     } else {
243       Param1 = ShellCommandLineGetRawValue (Package, 1);
244       if (Param1 == NULL) {
245         //
246         // display the current directory
247         //
248         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_CD_PRINT), gShellLevel2HiiHandle, Cwd);
249       } else {
250         Param1Copy = CatSPrint (NULL, L"%s", Param1, NULL);
251         for (Walker = Param1Copy; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
252           if (*Walker == L'\"') {
253             CopyMem (Walker, Walker + 1, StrSize(Walker) - sizeof(Walker[0]));
254           }
255         }
256 
257         if (Param1Copy != NULL && IsCurrentFileSystem (Param1Copy, Cwd)) {
258           Status = ReplaceDriveWithCwd (&Param1Copy,Cwd);
259         } else {
260           //
261           // Can't use cd command to change filesystem.
262           //
263           ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");
264           Status = EFI_NOT_FOUND;
265         }
266 
267         if (!EFI_ERROR(Status) && Param1Copy != NULL) {
268           Splitter = StrStr (Cwd, L":");
269           if (Param1Copy[0] == L'\\') {
270             //
271             // Absolute Path on current drive letter.
272             //
273             TotalSize = ((Splitter - Cwd + 1) * sizeof(CHAR16)) + StrSize(Param1Copy);
274             TempBuffer = AllocateZeroPool (TotalSize);
275             if (TempBuffer == NULL) {
276               Status = EFI_OUT_OF_RESOURCES;
277             } else {
278               StrnCpyS (TempBuffer, TotalSize / sizeof(CHAR16), Cwd, (Splitter - Cwd + 1));
279               StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), Param1Copy);
280 
281               FreePool (Param1Copy);
282               Param1Copy = TempBuffer;
283               TempBuffer = NULL;
284             }
285           } else {
286             if (StrStr (Param1Copy,L":") == NULL) {
287               TotalSize = StrSize (Cwd) + StrSize (Param1Copy);
288               TempBuffer = AllocateZeroPool (TotalSize);
289               if (TempBuffer == NULL) {
290                 Status = EFI_OUT_OF_RESOURCES;
291               } else {
292                 StrCpyS (TempBuffer, TotalSize / sizeof (CHAR16), Cwd);
293                 StrCatS (TempBuffer, TotalSize / sizeof (CHAR16), L"\\");
294                 StrCatS (TempBuffer, TotalSize / sizeof (CHAR16), Param1Copy);
295 
296                 FreePool (Param1Copy);
297                 Param1Copy = TempBuffer;
298                 TempBuffer = NULL;
299               }
300             }
301           }
302         }
303 
304         if (!EFI_ERROR(Status)) {
305           Param1Copy = PathCleanUpDirectories (Param1Copy);
306           Status = ExtractDriveAndPath (Param1Copy, &Drive, &Path);
307         }
308 
309         if (!EFI_ERROR (Status) && Drive != NULL && Path != NULL) {
310           if (EFI_ERROR(ShellIsDirectory (Param1Copy))) {
311             ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cd", Param1Copy);
312             ShellStatus = SHELL_NOT_FOUND;
313           } else {
314             Status = gEfiShellProtocol->SetCurDir (Drive, Path + 1);
315             if (EFI_ERROR (Status)) {
316               ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cd", Param1Copy);
317               ShellStatus = SHELL_NOT_FOUND;
318             }
319           }
320         }
321 
322         if (Drive != NULL) {
323           FreePool (Drive);
324         }
325 
326         if (Path != NULL) {
327           FreePool (Path);
328         }
329 
330         FreePool (Param1Copy);
331       }
332     }
333   }
334 
335   //
336   // free the command line package
337   //
338   ShellCommandLineFreeVarList (Package);
339 
340   //
341   // return the status
342   //
343   return (ShellStatus);
344 }
345 
346