]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/Library/UefiShellLevel2CommandsLib/Mv.c
Verify memory allocations were successful.
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Mv.c
CommitLineData
a405b86d 1/** @file\r
2 Main file for mv shell level 2 function.\r
3\r
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "UefiShellLevel2CommandsLib.h"\r
16\r
17/**\r
18 Function to validate that moving a specific file (FileName) to a specific\r
19 location (DestPath) is valid.\r
20\r
21 This function will verify that the destination is not a subdirectory of\r
22 FullName, that the Current working Directory is not being moved, and that\r
23 the directory is not read only.\r
24\r
25 if the move is invalid this function will report the error to StdOut.\r
26\r
27 @param FullName [in] The name of the file to move.\r
28 @param Cwd [in] The current working directory\r
29 @param DestPath [in] The target location to move to\r
30 @param Attribute[in] The Attribute of the file\r
31\r
32 @retval TRUE The move is valid\r
33 @retval FALSE The move is not\r
34**/\r
35BOOLEAN\r
36EFIAPI\r
37IsValidMove(\r
38 IN CONST CHAR16 *FullName,\r
39 IN CONST CHAR16 *Cwd,\r
40 IN CONST CHAR16 *DestPath,\r
41 IN CONST UINT64 Attribute\r
42 )\r
43{\r
44 CHAR16 *Test;\r
45 CHAR16 *Test1;\r
46 CHAR16 *TestWalker;\r
47 UINTN Result;\r
48 UINTN TempLen;\r
49 if (Cwd != NULL && StrCmp(FullName, Cwd) == 0) {\r
50 //\r
51 // Invalid move\r
52 //\r
53 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_CWD), gShellLevel2HiiHandle);\r
54 return (FALSE);\r
55 }\r
56 Test = NULL;\r
57 Test = StrnCatGrow(&Test, NULL, DestPath, 0);\r
58 TestWalker = Test;\r
59 ASSERT(TestWalker != NULL);\r
60 while(*TestWalker == L'\\') {\r
61 TestWalker++;\r
62 }\r
63 while(TestWalker != NULL && TestWalker[StrLen(TestWalker)-1] == L'\\') {\r
64 TestWalker[StrLen(TestWalker)-1] = CHAR_NULL;\r
65 }\r
66 ASSERT(TestWalker != NULL);\r
67 ASSERT(FullName != NULL);\r
68 if (StrStr(FullName, TestWalker) != 0) {\r
69 TempLen = StrLen(FullName);\r
70 if (StrStr(FullName, TestWalker) != FullName // not the first items... (could below it)\r
71 && TempLen <= (StrLen(TestWalker) + 1)\r
72 && StrStr(FullName+StrLen(TestWalker) + 1, L"\\") == NULL) {\r
73 //\r
74 // Invalid move\r
75 //\r
76 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);\r
77 FreePool(Test);\r
78 return (FALSE);\r
79 }\r
80 }\r
81 FreePool(Test);\r
82 if (StrStr(DestPath, FullName) != 0 && StrStr(DestPath, FullName) != DestPath) {\r
83 //\r
84 // Invalid move\r
85 //\r
86 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);\r
87 return (FALSE);\r
88 }\r
89 if ((Attribute & EFI_FILE_READ_ONLY) != 0) {\r
90 //\r
91 // invalid to move read only\r
92 //\r
93 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_RO), gShellLevel2HiiHandle);\r
94 return (FALSE);\r
95 }\r
96 Test = StrStr(FullName, L":");\r
97 Test1 = StrStr(DestPath, L":");\r
98 if (Test1 != NULL && Test != NULL) {\r
99 *Test = CHAR_NULL;\r
100 *Test1 = CHAR_NULL;\r
101 Result = StringNoCaseCompare(&FullName, &DestPath);\r
102 *Test = L':';\r
103 *Test1 = L':';\r
104 if (Result != 0) {\r
105 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_FS), gShellLevel2HiiHandle);\r
106 return (FALSE);\r
107 }\r
108 }\r
109 return (TRUE);\r
110}\r
111\r
112/**\r
113 Function to take a destination path that might contain wildcards and verify\r
114 that there is only a single possible target (IE we cant have wildcards that\r
115 have 2 possible destination).\r
116\r
117 if the result is sucessful the caller must free *DestPathPointer.\r
118\r
119 @param[in] DestDir The original path to the destination\r
120 @param[in,out] DestPathPointer a pointer to the callee allocated final path.\r
121\r
122 @retval EFI_INVALID_PARAMETR the DestDir could not be resolved to a location\r
123 @retval EFI_INVALID_PARAMETR the DestDir could be resolved to more than 1 location\r
124 @retval EFI_SUCCESS the operation was sucessful\r
125**/\r
126SHELL_STATUS\r
127EFIAPI\r
128GetDestinationLocation(\r
129 IN CONST CHAR16 *DestDir,\r
130 IN OUT CHAR16 **DestPathPointer,\r
131 IN CONST CHAR16 *Cwd\r
132 )\r
133{\r
134 EFI_SHELL_FILE_INFO *DestList;\r
135 EFI_SHELL_FILE_INFO *Node;\r
136 EFI_STATUS Status;\r
137 CHAR16 *DestPath;\r
138 CHAR16 *TempLocation;\r
139 UINTN NewSize;\r
140\r
141 DestList = NULL;\r
142 DestPath = NULL;\r
143 //\r
144 // get the destination path\r
145 //\r
146 Status = ShellOpenFileMetaArg((CHAR16*)DestDir, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, &DestList);\r
147 if (DestList == NULL || IsListEmpty(&DestList->Link)) {\r
148 //\r
149 // Not existing... must be renaming\r
150 //\r
151 if ((TempLocation = StrStr(DestDir, L":")) == NULL) {\r
152 NewSize = StrSize(Cwd);\r
153 NewSize += StrSize(DestDir);\r
154 DestPath = AllocateZeroPool(NewSize);\r
9ea69f8a 155 if (DestPath == NULL) {\r
156 ShellCloseFileMetaArg(&DestList);\r
157 return (SHELL_OUT_OF_RESOURCES);\r
158 }\r
a405b86d 159 StrCpy(DestPath, Cwd);\r
160 if (DestPath[StrLen(DestPath)-1] != L'\\' && DestDir[0] != L'\\') {\r
161 StrCat(DestPath, L"\\");\r
162 } else if (DestPath[StrLen(DestPath)-1] == L'\\' && DestDir[0] == L'\\') {\r
163 ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;\r
164 }\r
165 StrCat(DestPath, DestDir);\r
166 } else {\r
167 ASSERT(DestPath == NULL);\r
168 DestPath = StrnCatGrow(&DestPath, NULL, DestDir, 0);\r
9ea69f8a 169 if (DestPath == NULL) {\r
170 ShellCloseFileMetaArg(&DestList);\r
171 return (SHELL_OUT_OF_RESOURCES);\r
172 }\r
a405b86d 173 }\r
174 } else {\r
175 Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&DestList->Link);\r
176 //\r
177 // Make sure there is only 1 node in the list.\r
178 //\r
179 if (!IsNodeAtEnd(&DestList->Link, &Node->Link)) {\r
180 ShellCloseFileMetaArg(&DestList);\r
181 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, DestDir);\r
182 return (SHELL_INVALID_PARAMETER);\r
183 }\r
184 if (ShellIsDirectory(Node->FullName)==EFI_SUCCESS) {\r
185 DestPath = AllocateZeroPool(StrSize(Node->FullName)+sizeof(CHAR16));\r
9ea69f8a 186 if (DestPath == NULL) {\r
187 ShellCloseFileMetaArg(&DestList);\r
188 return (SHELL_OUT_OF_RESOURCES);\r
189 }\r
a405b86d 190 StrCpy(DestPath, Node->FullName);\r
191 StrCat(DestPath, L"\\");\r
192 } else {\r
193 //\r
194 // cant move onto another file.\r
195 //\r
196 ShellCloseFileMetaArg(&DestList);\r
197 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_ERROR), gShellLevel2HiiHandle, DestDir);\r
198 return (SHELL_INVALID_PARAMETER);\r
199 }\r
200 }\r
201\r
202 *DestPathPointer = DestPath;\r
203 ShellCloseFileMetaArg(&DestList);\r
204\r
205 return (SHELL_SUCCESS);\r
206}\r
207\r
208/**\r
209 function to take a list of files to move and a destination location and do\r
210 the verification and moving of those files to that location. This function\r
211 will report any errors to the user and continue to move the rest of the files.\r
212\r
213 @param[in] FileList A LIST_ENTRY* based list of files to move\r
214 @param[in] DestDir the destination location\r
215\r
216 @retval SHELL_SUCCESS the files were all moved.\r
217 @retval SHELL_INVALID_PARAMETER a parameter was invalid\r
218 @retval SHELL_SECURITY_VIOLATION a security violation ocurred\r
219 @retval SHELL_WRITE_PROTECTED the destination was write protected\r
220 @retval SHELL_OUT_OF_RESOURCES a memory allocation failed\r
221**/\r
222SHELL_STATUS\r
223EFIAPI\r
224ValidateAndMoveFiles(\r
225 IN CONST EFI_SHELL_FILE_INFO *FileList,\r
226 IN CONST CHAR16 *DestDir\r
227 )\r
228{\r
229 EFI_STATUS Status;\r
230 CHAR16 *HiiOutput;\r
231 CHAR16 *HiiResultOk;\r
232 CHAR16 *DestPath;\r
233 CONST CHAR16 *Cwd;\r
234 SHELL_STATUS ShellStatus;\r
235 CONST EFI_SHELL_FILE_INFO *Node;\r
236 EFI_FILE_INFO *NewFileInfo;\r
237 CHAR16 *TempLocation;\r
238 UINTN NewSize;\r
239\r
240 ASSERT(FileList != NULL);\r
241 ASSERT(DestDir != NULL);\r
242\r
243 DestPath = NULL;\r
244 Cwd = ShellGetCurrentDir(NULL);\r
245\r
246 //\r
247 // Get and validate the destination location\r
248 //\r
249 ShellStatus = GetDestinationLocation(DestDir, &DestPath, Cwd);\r
250 if (ShellStatus != SHELL_SUCCESS) {\r
251 return (ShellStatus);\r
252 }\r
253\r
254 HiiOutput = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_MV_OUTPUT), NULL);\r
255 HiiResultOk = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_GEN_RES_OK), NULL);\r
256 ASSERT (DestPath != NULL);\r
257 ASSERT (HiiResultOk != NULL);\r
258 ASSERT (HiiOutput != NULL);\r
259// ASSERT (Cwd != NULL);\r
260\r
261 //\r
262 // Go through the list of files and directories to move...\r
263 //\r
264 for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)\r
265 ; !IsNull(&FileList->Link, &Node->Link)\r
266 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)\r
267 ){\r
268 if (ShellGetExecutionBreakFlag()) {\r
269 break;\r
270 }\r
271 ASSERT(Node->FileName != NULL);\r
272 ASSERT(Node->FullName != NULL);\r
273\r
274 //\r
275 // skip the directory traversing stuff...\r
276 //\r
277 if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {\r
278 continue;\r
279 }\r
280\r
281 //\r
282 // Validate that the move is valid\r
283 //\r
284 if (!IsValidMove(Node->FullName, Cwd, DestPath, Node->Info->Attribute)) {\r
285 ShellStatus = SHELL_INVALID_PARAMETER;\r
286 continue;\r
287 }\r
288\r
289 //\r
290 // Chop off map info from "DestPath"\r
291 //\r
292 if ((TempLocation = StrStr(DestPath, L":")) != NULL) {\r
293 CopyMem(DestPath, TempLocation+1, StrSize(TempLocation+1));\r
294 }\r
295\r
296 //\r
297 // construct the new file info block\r
298 //\r
299 NewSize = StrSize(DestPath);\r
300 NewSize += StrSize(Node->FileName) + sizeof(EFI_FILE_INFO) + sizeof(CHAR16);\r
301 NewFileInfo = AllocateZeroPool(NewSize);\r
9ea69f8a 302 if (NewFileInfo == NULL) {\r
303 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle);\r
304 ShellStatus = SHELL_OUT_OF_RESOURCES;\r
a405b86d 305 } else {\r
9ea69f8a 306 CopyMem(NewFileInfo, Node->Info, sizeof(EFI_FILE_INFO));\r
307 if (DestPath[0] != L'\\') {\r
308 StrCpy(NewFileInfo->FileName, L"\\");\r
309 StrCat(NewFileInfo->FileName, DestPath);\r
310 } else {\r
311 StrCpy(NewFileInfo->FileName, DestPath);\r
a405b86d 312 }\r
9ea69f8a 313 if (NewFileInfo->FileName[StrLen(NewFileInfo->FileName)-1] == L'\\') {\r
314 if (Node->FileName[0] == L'\\') {\r
315 //\r
316 // Don't allow for double slashes. Eliminate one of them.\r
317 //\r
318 NewFileInfo->FileName[StrLen(NewFileInfo->FileName)-1] = CHAR_NULL;\r
319 }\r
320 StrCat(NewFileInfo->FileName, Node->FileName);\r
321 }\r
322 NewFileInfo->Size = sizeof(EFI_FILE_INFO) + StrSize(NewFileInfo->FileName);\r
a405b86d 323\r
9ea69f8a 324 ShellPrintEx(-1, -1, HiiOutput, Node->FullName, NewFileInfo->FileName);\r
a405b86d 325\r
9ea69f8a 326 //\r
327 // Perform the move operation\r
328 //\r
329 Status = ShellSetFileInfo(Node->Handle, NewFileInfo);\r
a405b86d 330\r
9ea69f8a 331 //\r
332 // Free the info object we used...\r
333 //\r
334 ASSERT (NewFileInfo != NULL);\r
335 FreePool(NewFileInfo);\r
a405b86d 336\r
a405b86d 337 //\r
9ea69f8a 338 // Check our result\r
a405b86d 339 //\r
9ea69f8a 340 if (EFI_ERROR(Status)) {\r
341 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_UK), gShellLevel2HiiHandle, Status);\r
342 //\r
343 // move failed\r
344 //\r
345 switch(Status){\r
346 default:\r
347 ShellStatus = SHELL_INVALID_PARAMETER;\r
348 case EFI_SECURITY_VIOLATION:\r
349 ShellStatus = SHELL_SECURITY_VIOLATION;\r
350 case EFI_WRITE_PROTECTED:\r
351 ShellStatus = SHELL_WRITE_PROTECTED;\r
352 case EFI_OUT_OF_RESOURCES:\r
353 ShellStatus = SHELL_OUT_OF_RESOURCES;\r
354 case EFI_DEVICE_ERROR:\r
355 ShellStatus = SHELL_DEVICE_ERROR;\r
356 case EFI_ACCESS_DENIED:\r
357 ShellStatus = SHELL_ACCESS_DENIED;\r
358 } // switch\r
359 } else {\r
360 ShellPrintEx(-1, -1, L"%s", HiiResultOk);\r
361 }\r
a405b86d 362 }\r
363 } // for loop\r
364\r
365 FreePool(DestPath);\r
366 FreePool(HiiOutput);\r
367 FreePool(HiiResultOk);\r
368 return (ShellStatus);\r
369}\r
370\r
371SHELL_STATUS\r
372EFIAPI\r
373ShellCommandRunMv (\r
374 IN EFI_HANDLE ImageHandle,\r
375 IN EFI_SYSTEM_TABLE *SystemTable\r
376 )\r
377{\r
378 EFI_STATUS Status;\r
379 LIST_ENTRY *Package;\r
380 CHAR16 *ProblemParam;\r
381 SHELL_STATUS ShellStatus;\r
382 UINTN ParamCount;\r
383 UINTN LoopCounter;\r
384 EFI_SHELL_FILE_INFO *FileList;\r
385\r
386 ProblemParam = NULL;\r
387 ShellStatus = SHELL_SUCCESS;\r
388 ParamCount = 0;\r
389 FileList = NULL;\r
390\r
391 //\r
392 // initialize the shell lib (we must be in non-auto-init...)\r
393 //\r
394 Status = ShellInitialize();\r
395 ASSERT_EFI_ERROR(Status);\r
396\r
397 //\r
398 // parse the command line\r
399 //\r
400 Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);\r
401 if (EFI_ERROR(Status)) {\r
402 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {\r
403 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, ProblemParam);\r
404 FreePool(ProblemParam);\r
405 ShellStatus = SHELL_INVALID_PARAMETER;\r
406 } else {\r
407 ASSERT(FALSE);\r
408 }\r
409 } else {\r
410 //\r
411 // check for "-?"\r
412 //\r
413 if (ShellCommandLineGetFlag(Package, L"-?")) {\r
414 ASSERT(FALSE);\r
415 }\r
416\r
417 switch (ParamCount = ShellCommandLineGetCount(Package)) {\r
418 case 0:\r
419 case 1:\r
420 //\r
421 // we have insufficient parameters\r
422 //\r
423 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle);\r
424 ShellStatus = SHELL_INVALID_PARAMETER;\r
425 break;\r
426 case 2:\r
427 //\r
428 // must have valid CWD for single parameter...\r
429 //\r
430 if (ShellGetCurrentDir(NULL) == NULL){\r
431 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);\r
432 ShellStatus = SHELL_INVALID_PARAMETER;\r
433 } else {\r
434 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);\r
435 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {\r
436 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, 1));\r
437 ShellStatus = SHELL_NOT_FOUND;\r
438 } else {\r
439 //\r
440 // ValidateAndMoveFiles will report errors to the screen itself\r
441 //\r
442 ShellStatus = ValidateAndMoveFiles(FileList, ShellGetCurrentDir(NULL));\r
443 }\r
444 }\r
445\r
446 break;\r
447 default:\r
448 ///@todo make sure this works with error half way through and continues...\r
449 for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount && ShellStatus == SHELL_SUCCESS ; LoopCounter++) {\r
450 if (ShellGetExecutionBreakFlag()) {\r
451 break;\r
452 }\r
453 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);\r
454 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {\r
455 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, 1));\r
456 ShellStatus = SHELL_NOT_FOUND;\r
457 } else {\r
458 //\r
459 // ValidateAndMoveFiles will report errors to the screen itself\r
460 // Only change ShellStatus if it's sucessful\r
461 //\r
462 if (ShellStatus == SHELL_SUCCESS) {\r
463 ShellStatus = ValidateAndMoveFiles(FileList, ShellCommandLineGetRawValue(Package, ParamCount));\r
464 } else {\r
465 ValidateAndMoveFiles(FileList, ShellCommandLineGetRawValue(Package, ParamCount));\r
466 }\r
467 }\r
468 if (FileList != NULL && !IsListEmpty(&FileList->Link)) {\r
469 Status = ShellCloseFileMetaArg(&FileList);\r
470 if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {\r
471 ShellStatus = SHELL_ACCESS_DENIED;\r
472 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, 1), ShellStatus|MAX_BIT);\r
473 }\r
474 }\r
475 }\r
476 break;\r
477 } // switch on parameter count\r
478\r
479 if (FileList != NULL) {\r
480 ShellCloseFileMetaArg(&FileList);\r
481 }\r
482\r
483 //\r
484 // free the command line package\r
485 //\r
486 ShellCommandLineFreeVarList (Package);\r
487 }\r
488\r
489 if (ShellGetExecutionBreakFlag()) {\r
490 return (SHELL_ABORTED);\r
491 }\r
492\r
493 return (ShellStatus);\r
494}\r