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