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