1 /** @file
2   Main file for attrib shell level 2 function.
3 
4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2015, 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 "UefiShellLevel2CommandsLib.h"
17 
18 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
19   {L"-q", TypeFlag},
20   {NULL, TypeMax}
21   };
22 
23 /**
24   Determine if a directory has no files in it.
25 
26   @param[in] FileHandle   The EFI_HANDLE to the directory.
27 
28   @retval TRUE  The directory has no files (or directories).
29   @retval FALSE The directory has at least 1 file or directory in it.
30 **/
31 BOOLEAN
32 EFIAPI
IsDirectoryEmpty(IN EFI_HANDLE FileHandle)33 IsDirectoryEmpty (
34   IN EFI_HANDLE   FileHandle
35   )
36 {
37   EFI_FILE_INFO   *FileInfo;
38   BOOLEAN         NoFile;
39   BOOLEAN         RetVal;
40 
41   RetVal = TRUE;
42   NoFile = FALSE;
43   FileInfo = NULL;
44 
45   for (FileHandleFindFirstFile(FileHandle, &FileInfo)
46     ;  !NoFile
47     ;  FileHandleFindNextFile(FileHandle, FileInfo, &NoFile)
48    ){
49     if (StrStr(FileInfo->FileName, L".") != FileInfo->FileName
50       &&StrStr(FileInfo->FileName, L"..") != FileInfo->FileName) {
51       RetVal = FALSE;
52     }
53   }
54   return (RetVal);
55 }
56 
57 /**
58   Delete a node and all nodes under it (including sub directories).
59 
60   @param[in] Node   The node to start deleting with.
61   @param[in] Quiet  TRUE to print no messages.
62 
63   @retval SHELL_SUCCESS       The operation was successful.
64   @retval SHELL_ACCESS_DENIED A file was read only.
65   @retval SHELL_ABORTED       The abort message was received.
66   @retval SHELL_DEVICE_ERROR  A device error occured reading this Node.
67 **/
68 SHELL_STATUS
69 EFIAPI
CascadeDelete(IN EFI_SHELL_FILE_INFO * Node,IN CONST BOOLEAN Quiet)70 CascadeDelete(
71   IN EFI_SHELL_FILE_INFO  *Node,
72   IN CONST BOOLEAN        Quiet
73   )
74 {
75   SHELL_STATUS          ShellStatus;
76   EFI_SHELL_FILE_INFO   *List;
77   EFI_SHELL_FILE_INFO   *Node2;
78   EFI_STATUS            Status;
79   SHELL_PROMPT_RESPONSE *Resp;
80   CHAR16                *TempName;
81   UINTN                 NewSize;
82 
83   Resp                  = NULL;
84   ShellStatus           = SHELL_SUCCESS;
85   List                  = NULL;
86   Status                = EFI_SUCCESS;
87 
88   if ((Node->Info->Attribute & EFI_FILE_READ_ONLY) == EFI_FILE_READ_ONLY) {
89     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DETELE_RO), gShellLevel2HiiHandle, L"rm", Node->FullName);
90     return (SHELL_ACCESS_DENIED);
91   }
92 
93   if ((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY) {
94     if (!IsDirectoryEmpty(Node->Handle)) {
95       if (!Quiet) {
96         Status = ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_RM_LOG_DELETE_CONF), gShellLevel2HiiHandle, Node->FullName);
97         Status = ShellPromptForResponse(ShellPromptResponseTypeYesNo, NULL, (VOID**)&Resp);
98         ASSERT(Resp != NULL);
99         if (EFI_ERROR(Status) || *Resp != ShellPromptResponseYes) {
100           SHELL_FREE_NON_NULL(Resp);
101           return (SHELL_ABORTED);
102         }
103         SHELL_FREE_NON_NULL(Resp);
104       }
105       //
106       // empty out the directory
107       //
108       Status = gEfiShellProtocol->FindFilesInDir(Node->Handle, &List);
109       if (EFI_ERROR(Status)) {
110         if (List!=NULL) {
111           gEfiShellProtocol->FreeFileList(&List);
112         }
113         return (SHELL_DEVICE_ERROR);
114       }
115       for (Node2 = (EFI_SHELL_FILE_INFO   *)GetFirstNode(&List->Link)
116         ;  !IsNull(&List->Link, &Node2->Link)
117         ;  Node2 = (EFI_SHELL_FILE_INFO   *)GetNextNode(&List->Link, &Node2->Link)
118        ){
119         //
120         // skip the directory traversing stuff...
121         //
122         if (StrCmp(Node2->FileName, L".") == 0 || StrCmp(Node2->FileName, L"..") == 0) {
123           continue;
124         }
125         Node2->Status = gEfiShellProtocol->OpenFileByName (Node2->FullName, &Node2->Handle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE);
126         if (EFI_ERROR(Node2->Status) && StrStr(Node2->FileName, L":") == NULL) {
127           //
128           // Update the node filename to have full path with file system identifier
129           //
130           NewSize = StrSize(Node->FullName) + StrSize(Node2->FullName);
131           TempName = AllocateZeroPool(NewSize);
132           if (TempName == NULL) {
133             ShellStatus = SHELL_OUT_OF_RESOURCES;
134           } else {
135             StrCpyS(TempName, NewSize/sizeof(CHAR16), Node->FullName);
136             TempName[StrStr(TempName, L":")+1-TempName] = CHAR_NULL;
137             StrCatS(TempName, NewSize/sizeof(CHAR16), Node2->FullName);
138             FreePool((VOID*)Node2->FullName);
139             Node2->FullName = TempName;
140 
141             //
142             // Now try again to open the file
143             //
144             Node2->Status = gEfiShellProtocol->OpenFileByName (Node2->FullName, &Node2->Handle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE);
145           }
146         }
147         if (!EFI_ERROR(Node2->Status)) {
148           ShellStatus = CascadeDelete(Node2, Quiet);
149         } else if (ShellStatus == SHELL_SUCCESS) {
150           ShellStatus = (SHELL_STATUS)(Node2->Status&(~0x80000000));
151         }
152         if (ShellStatus != SHELL_SUCCESS) {
153           if (List!=NULL) {
154             gEfiShellProtocol->FreeFileList(&List);
155           }
156           return (ShellStatus);
157         }
158       }
159       if (List!=NULL) {
160         gEfiShellProtocol->FreeFileList(&List);
161       }
162     }
163   }
164 
165   if (!(StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0)) {
166     //
167     // now delete the current node...
168     //
169     if (!Quiet) {
170       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DELETE), gShellLevel2HiiHandle, Node->FullName);
171     }
172     Status = gEfiShellProtocol->DeleteFile(Node->Handle);
173     Node->Handle = NULL;
174   }
175 
176   //
177   // We cant allow for the warning here! (Dont use EFI_ERROR Macro).
178   //
179   if (Status != EFI_SUCCESS){
180     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DELETE_ERR), gShellLevel2HiiHandle, Status);
181     return (SHELL_ACCESS_DENIED);
182   } else {
183     if (!Quiet) {
184       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DELETE_COMP), gShellLevel2HiiHandle);
185     }
186     return (SHELL_SUCCESS);
187   }
188 }
189 
190 /**
191   Determines if a Node is a valid delete target.  Will prevent deleting the root directory.
192 
193   @param[in] List       RESERVED.  Not used.
194   @param[in] Node       The node to analyze.
195   @param[in] Package    RESERVED.  Not used.
196 **/
197 BOOLEAN
198 EFIAPI
IsValidDeleteTarget(IN CONST EFI_SHELL_FILE_INFO * List,IN CONST EFI_SHELL_FILE_INFO * Node,IN CONST LIST_ENTRY * Package)199 IsValidDeleteTarget(
200   IN CONST EFI_SHELL_FILE_INFO  *List,
201   IN CONST EFI_SHELL_FILE_INFO  *Node,
202   IN CONST LIST_ENTRY           *Package
203   )
204 {
205   CONST CHAR16        *TempLocation;
206   BOOLEAN             RetVal;
207   CHAR16              *SearchString;
208   CHAR16              *Pattern;
209   UINTN               Size;
210 
211   if (Node == NULL || Node->FullName == NULL) {
212     return (FALSE);
213   }
214 
215   TempLocation = StrStr(Node->FullName, L":");
216   if (StrLen(TempLocation) <= 2) {
217     //
218     // Deleting the root directory is invalid.
219     //
220     return (FALSE);
221   }
222 
223   TempLocation = ShellGetCurrentDir(NULL);
224   if (TempLocation == NULL) {
225     //
226     // No working directory is specified so whatever is left is ok.
227     //
228     return (TRUE);
229   }
230 
231   Pattern       = NULL;
232   SearchString  = NULL;
233   Size          = 0;
234   Pattern       = StrnCatGrow(&Pattern, &Size, TempLocation  , 0);
235   Pattern       = StrnCatGrow(&Pattern, &Size, L"\\"  , 0);
236   Size = 0;
237   SearchString  = StrnCatGrow(&SearchString, &Size, Node->FullName, 0);
238   if (!EFI_ERROR(ShellIsDirectory(SearchString))) {
239     SearchString  = StrnCatGrow(&SearchString, &Size, L"\\", 0);
240     SearchString  = StrnCatGrow(&SearchString, &Size, L"*", 0);
241   }
242 
243   if (Pattern == NULL || SearchString == NULL) {
244     RetVal = FALSE;
245   } else {
246     RetVal = TRUE;
247     if (gUnicodeCollation->MetaiMatch(gUnicodeCollation, Pattern, SearchString)) {
248       RetVal = FALSE;
249     }
250   }
251 
252   SHELL_FREE_NON_NULL(Pattern     );
253   SHELL_FREE_NON_NULL(SearchString);
254 
255   return (RetVal);
256 }
257 
258 /**
259   Function for 'rm' command.
260 
261   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
262   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
263 **/
264 SHELL_STATUS
265 EFIAPI
ShellCommandRunRm(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)266 ShellCommandRunRm (
267   IN EFI_HANDLE        ImageHandle,
268   IN EFI_SYSTEM_TABLE  *SystemTable
269   )
270 {
271   EFI_STATUS            Status;
272   LIST_ENTRY            *Package;
273   CHAR16                *ProblemParam;
274   CONST CHAR16          *Param;
275   SHELL_STATUS          ShellStatus;
276   UINTN                 ParamCount;
277   EFI_SHELL_FILE_INFO   *FileList;
278   EFI_SHELL_FILE_INFO   *Node;
279 
280   ProblemParam        = NULL;
281   ShellStatus         = SHELL_SUCCESS;
282   ParamCount          = 0;
283   FileList            = NULL;
284 
285   //
286   // initialize the shell lib (we must be in non-auto-init...)
287   //
288   Status = ShellInitialize();
289   ASSERT_EFI_ERROR(Status);
290 
291   //
292   // parse the command line
293   //
294   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
295   if (EFI_ERROR(Status)) {
296     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
297       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"rm", ProblemParam);
298       FreePool(ProblemParam);
299       ShellStatus = SHELL_INVALID_PARAMETER;
300     } else {
301       ASSERT(FALSE);
302     }
303   } else {
304     //
305     // check for "-?"
306     //
307     if (ShellCommandLineGetFlag(Package, L"-?")) {
308       ASSERT(FALSE);
309     }
310     if (ShellCommandLineGetRawValue(Package, 1) == NULL) {
311       //
312       // we insufficient parameters
313       //
314       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"rm");
315       ShellStatus = SHELL_INVALID_PARAMETER;
316     } else {
317       //
318       // get a list with each file specified by parameters
319       // if parameter is a directory then add all the files below it to the list
320       //
321       for ( ParamCount = 1, Param = ShellCommandLineGetRawValue(Package, ParamCount)
322           ; Param != NULL
323           ; ParamCount++, Param = ShellCommandLineGetRawValue(Package, ParamCount)
324          ){
325         Status = ShellOpenFileMetaArg((CHAR16*)Param, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
326         if (EFI_ERROR(Status) || FileList == NULL || IsListEmpty(&FileList->Link)) {
327           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"rm", (CHAR16*)Param);
328           ShellStatus = SHELL_NOT_FOUND;
329           break;
330         }
331       }
332 
333       if (ShellStatus == SHELL_SUCCESS){
334         //
335         // loop through the list and make sure we are not aborting...
336         //
337         for ( Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&FileList->Link)
338             ; !IsNull(&FileList->Link, &Node->Link) && !ShellGetExecutionBreakFlag()
339             ; Node = (EFI_SHELL_FILE_INFO*)GetNextNode(&FileList->Link, &Node->Link)
340            ){
341           //
342           // skip the directory traversing stuff...
343           //
344           if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
345             continue;
346           }
347 
348           //
349           // do the deleting of nodes
350           //
351           if (EFI_ERROR(Node->Status)){
352             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DELETE_ERR2), gShellLevel2HiiHandle, Node->Status);
353             ShellStatus = SHELL_ACCESS_DENIED;
354             break;
355           }
356           if (!IsValidDeleteTarget(FileList, Node, Package)) {
357             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_RM_LOG_DELETE_ERR3), gShellLevel2HiiHandle, Node->FullName);
358             ShellStatus = SHELL_INVALID_PARAMETER;
359             break;
360           }
361 
362           ShellStatus = CascadeDelete(Node, ShellCommandLineGetFlag(Package, L"-q"));
363         }
364       }
365       //
366       // Free the fileList
367       //
368       if (FileList != NULL) {
369         Status = ShellCloseFileMetaArg(&FileList);
370       }
371       FileList = NULL;
372     }
373 
374     //
375     // free the command line package
376     //
377     ShellCommandLineFreeVarList (Package);
378   }
379 
380   return (ShellStatus);
381 }
382 
383