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