1 /** @file
2   Main file for mv shell level 2 function.
3 
4   (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2016, 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 determine if a move is between file systems.
20 
21   @param FullName [in]    The name of the file to move.
22   @param Cwd      [in]    The current working directory
23   @param DestPath [in]    The target location to move to
24 
25   @retval TRUE            The move is across file system.
26   @retval FALSE           The move is within a file system.
27 **/
28 BOOLEAN
29 EFIAPI
IsBetweenFileSystem(IN CONST CHAR16 * FullName,IN CONST CHAR16 * Cwd,IN CONST CHAR16 * DestPath)30 IsBetweenFileSystem(
31   IN CONST CHAR16     *FullName,
32   IN CONST CHAR16     *Cwd,
33   IN CONST CHAR16     *DestPath
34   )
35 {
36   CHAR16  *Test;
37   CHAR16  *Test1;
38   UINTN   Result;
39 
40   Test = StrStr(FullName, L":");
41   if (Test == NULL && Cwd != NULL) {
42     Test = StrStr(Cwd, L":");
43   }
44   Test1 = StrStr(DestPath, L":");
45   if (Test1 == NULL && Cwd != NULL) {
46     Test1 = StrStr(Cwd, L":");
47   }
48   if (Test1 != NULL && Test != NULL) {
49     *Test = CHAR_NULL;
50     *Test1 = CHAR_NULL;
51     Result = StringNoCaseCompare(&FullName, &DestPath);
52     *Test = L':';
53     *Test1 = L':';
54     if (Result != 0) {
55       return (TRUE);
56     }
57   }
58   return (FALSE);
59 }
60 
61 /**
62   Function to validate that moving a specific file (FileName) to a specific
63   location (DestPath) is valid.
64 
65   This function will verify that the destination is not a subdirectory of
66   FullName, that the Current working Directory is not being moved, and that
67   the directory is not read only.
68 
69   if the move is invalid this function will report the error to StdOut.
70 
71   @param SourcePath [in]    The name of the file to move.
72   @param Cwd        [in]    The current working directory
73   @param DestPath   [in]    The target location to move to
74   @param Attribute  [in]    The Attribute of the file
75   @param DestAttr   [in]    The Attribute of the destination
76   @param FileStatus [in]    The Status of the file when opened
77 
78   @retval TRUE        The move is valid
79   @retval FALSE       The move is not
80 **/
81 BOOLEAN
82 EFIAPI
IsValidMove(IN CONST CHAR16 * SourcePath,IN CONST CHAR16 * Cwd,IN CONST CHAR16 * DestPath,IN CONST UINT64 Attribute,IN CONST UINT64 DestAttr,IN CONST EFI_STATUS FileStatus)83 IsValidMove(
84   IN CONST CHAR16     *SourcePath,
85   IN CONST CHAR16     *Cwd,
86   IN CONST CHAR16     *DestPath,
87   IN CONST UINT64     Attribute,
88   IN CONST UINT64     DestAttr,
89   IN CONST EFI_STATUS FileStatus
90   )
91 {
92   CHAR16  *DestPathCopy;
93   CHAR16  *DestPathWalker;
94 
95   if (Cwd != NULL && StrCmp(SourcePath, Cwd) == 0) {
96     //
97     // Invalid move
98     //
99     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_CWD), gShellLevel2HiiHandle);
100     return (FALSE);
101   }
102 
103   //
104   // invalid to move read only or move to a read only destination
105   //
106   if (((Attribute & EFI_FILE_READ_ONLY) != 0)
107     || (FileStatus == EFI_WRITE_PROTECTED)
108     || ((DestAttr & EFI_FILE_READ_ONLY) != 0)
109     ) {
110     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_RO), gShellLevel2HiiHandle, SourcePath);
111     return (FALSE);
112   }
113 
114   DestPathCopy = AllocateCopyPool(StrSize(DestPath), DestPath);
115   if (DestPathCopy == NULL) {
116     return (FALSE);
117   }
118 
119   for (DestPathWalker = DestPathCopy; *DestPathWalker == L'\\'; DestPathWalker++) ;
120 
121   while(DestPathWalker != NULL && DestPathWalker[StrLen(DestPathWalker)-1] == L'\\') {
122     DestPathWalker[StrLen(DestPathWalker)-1] = CHAR_NULL;
123   }
124 
125   ASSERT(DestPathWalker != NULL);
126   ASSERT(SourcePath   != NULL);
127 
128   //
129   // If they're the same, or if source is "above" dest on file path tree
130   //
131   if ( StringNoCaseCompare (&DestPathWalker, &SourcePath) == 0 ||
132        ((StrStr(DestPathWalker, SourcePath) == DestPathWalker) &&
133         (DestPathWalker[StrLen(SourcePath)] == '\\')
134        )
135      ) {
136     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);
137     FreePool(DestPathCopy);
138     return (FALSE);
139   }
140   FreePool(DestPathCopy);
141 
142   return (TRUE);
143 }
144 
145 /**
146   Function to take a destination path that might contain wildcards and verify
147   that there is only a single possible target (IE we cant have wildcards that
148   have 2 possible destination).
149 
150   if the result is sucessful the caller must free *DestPathPointer.
151 
152   @param[in] DestParameter               The original path to the destination.
153   @param[in, out] DestPathPointer  A pointer to the callee allocated final path.
154   @param[in] Cwd                   A pointer to the current working directory.
155   @param[in] SingleSource          TRUE to have only one source file.
156   @param[in, out] DestAttr         A pointer to the destination information attribute.
157 
158   @retval SHELL_INVALID_PARAMETER  The DestParameter could not be resolved to a location.
159   @retval SHELL_INVALID_PARAMETER  The DestParameter could be resolved to more than 1 location.
160   @retval SHELL_INVALID_PARAMETER  Cwd is required and is NULL.
161   @retval SHELL_SUCCESS            The operation was sucessful.
162 **/
163 SHELL_STATUS
164 EFIAPI
GetDestinationLocation(IN CONST CHAR16 * DestParameter,IN OUT CHAR16 ** DestPathPointer,IN CONST CHAR16 * Cwd,IN CONST BOOLEAN SingleSource,IN OUT UINT64 * DestAttr)165 GetDestinationLocation(
166   IN CONST CHAR16               *DestParameter,
167   IN OUT CHAR16                 **DestPathPointer,
168   IN CONST CHAR16               *Cwd,
169   IN CONST BOOLEAN              SingleSource,
170   IN OUT UINT64                 *DestAttr
171   )
172 {
173   EFI_SHELL_FILE_INFO       *DestList;
174   EFI_SHELL_FILE_INFO       *Node;
175   CHAR16                    *DestPath;
176   UINTN                     NewSize;
177   UINTN                     CurrentSize;
178 
179   DestList = NULL;
180   DestPath = NULL;
181 
182   ASSERT(DestAttr != NULL);
183 
184   if (StrStr(DestParameter, L"\\") == DestParameter) {
185     if (Cwd == NULL) {
186       return SHELL_INVALID_PARAMETER;
187     }
188     DestPath = AllocateZeroPool(StrSize(Cwd));
189     if (DestPath == NULL) {
190       return (SHELL_OUT_OF_RESOURCES);
191     }
192     StrCpyS(DestPath, StrSize(Cwd) / sizeof(CHAR16), Cwd);
193     while (PathRemoveLastItem(DestPath)) ;
194 
195     //
196     // Append DestParameter beyond '\' which may be present
197     //
198     CurrentSize = StrSize(DestPath);
199     StrnCatGrow(&DestPath, &CurrentSize, &DestParameter[1], 0);
200 
201     *DestPathPointer =  DestPath;
202     return (SHELL_SUCCESS);
203   }
204   //
205   // get the destination path
206   //
207   ShellOpenFileMetaArg((CHAR16*)DestParameter, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, &DestList);
208   if (DestList == NULL || IsListEmpty(&DestList->Link)) {
209     //
210     // Not existing... must be renaming
211     //
212     if (StrStr(DestParameter, L":") == NULL) {
213       if (Cwd == NULL) {
214         ShellCloseFileMetaArg(&DestList);
215         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
216         return (SHELL_INVALID_PARAMETER);
217       }
218       NewSize = StrSize(Cwd);
219       NewSize += StrSize(DestParameter);
220       DestPath = AllocateZeroPool(NewSize);
221       if (DestPath == NULL) {
222         ShellCloseFileMetaArg(&DestList);
223         return (SHELL_OUT_OF_RESOURCES);
224       }
225       StrCpyS(DestPath, NewSize / sizeof(CHAR16), Cwd);
226       if (DestPath[StrLen(DestPath)-1] != L'\\' && DestParameter[0] != L'\\') {
227         StrCatS(DestPath, NewSize / sizeof(CHAR16), L"\\");
228       } else if (DestPath[StrLen(DestPath)-1] == L'\\' && DestParameter[0] == L'\\') {
229         ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
230       }
231       StrCatS(DestPath, NewSize / sizeof(CHAR16), DestParameter);
232     } else {
233       ASSERT(DestPath == NULL);
234       DestPath = StrnCatGrow(&DestPath, NULL, DestParameter, 0);
235       if (DestPath == NULL) {
236         ShellCloseFileMetaArg(&DestList);
237         return (SHELL_OUT_OF_RESOURCES);
238       }
239     }
240   } else {
241     Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&DestList->Link);
242     *DestAttr = Node->Info->Attribute;
243     //
244     // Make sure there is only 1 node in the list.
245     //
246     if (!IsNodeAtEnd(&DestList->Link, &Node->Link)) {
247       ShellCloseFileMetaArg(&DestList);
248       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, L"mv", DestParameter);
249       return (SHELL_INVALID_PARAMETER);
250     }
251 
252     //
253     // If we are a directory or a single file, then one node is fine.
254     //
255     if (ShellIsDirectory(Node->FullName)==EFI_SUCCESS || SingleSource) {
256       DestPath = AllocateZeroPool(StrSize(Node->FullName)+sizeof(CHAR16));
257       if (DestPath == NULL) {
258         ShellCloseFileMetaArg(&DestList);
259         return (SHELL_OUT_OF_RESOURCES);
260       }
261       StrCpyS(DestPath, (StrSize(Node->FullName)+sizeof(CHAR16)) / sizeof(CHAR16), Node->FullName);
262       StrCatS(DestPath, (StrSize(Node->FullName)+sizeof(CHAR16)) / sizeof(CHAR16), L"\\");
263     } else {
264       //
265       // cant move multiple files onto a single file.
266       //
267       ShellCloseFileMetaArg(&DestList);
268       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_ERROR), gShellLevel2HiiHandle, L"mv", DestParameter);
269       return (SHELL_INVALID_PARAMETER);
270     }
271   }
272 
273   *DestPathPointer =  DestPath;
274   ShellCloseFileMetaArg(&DestList);
275 
276   return (SHELL_SUCCESS);
277 }
278 
279 /**
280   Function to do a move across file systems.
281 
282   @param[in] Node               A pointer to the file to be removed.
283   @param[in] DestPath           A pointer to the destination file path.
284   @param[out] Resp              A pointer to response from question.  Pass back on looped calling
285 
286   @retval SHELL_SUCCESS     The source file was moved to the destination.
287 **/
288 EFI_STATUS
289 EFIAPI
MoveBetweenFileSystems(IN EFI_SHELL_FILE_INFO * Node,IN CONST CHAR16 * DestPath,OUT VOID ** Resp)290 MoveBetweenFileSystems(
291   IN EFI_SHELL_FILE_INFO  *Node,
292   IN CONST CHAR16         *DestPath,
293   OUT VOID                **Resp
294   )
295 {
296   SHELL_STATUS    ShellStatus;
297 
298   //
299   // First we copy the file
300   //
301   ShellStatus = CopySingleFile (Node->FullName, DestPath, Resp, TRUE, L"mv");
302 
303   //
304   // Check our result
305   //
306   if (ShellStatus == SHELL_SUCCESS) {
307     //
308     // The copy was successful.  delete the source file.
309     //
310     CascadeDelete(Node, TRUE);
311     Node->Handle = NULL;
312   } else if (ShellStatus == SHELL_ABORTED) {
313     return EFI_ABORTED;
314   } else if (ShellStatus == SHELL_ACCESS_DENIED) {
315     return EFI_ACCESS_DENIED;
316   } else if (ShellStatus == SHELL_VOLUME_FULL) {
317     return EFI_VOLUME_FULL;
318   } else {
319     return EFI_UNSUPPORTED;
320   }
321 
322   return (EFI_SUCCESS);
323 }
324 
325 /**
326   Function to take the destination path and target file name to generate the full destination path.
327 
328   @param[in] DestPath           A pointer to the destination file path string.
329   @param[out] FullDestPath      A pointer to the full destination path string.
330   @param[in] FileName           Name string of  the targe file.
331 
332   @retval SHELL_SUCCESS             the files were all moved.
333   @retval SHELL_INVALID_PARAMETER   a parameter was invalid
334   @retval SHELL_OUT_OF_RESOURCES    a memory allocation failed
335 **/
336 EFI_STATUS
337 EFIAPI
CreateFullDestPath(IN CONST CHAR16 ** DestPath,OUT CHAR16 ** FullDestPath,IN CONST CHAR16 * FileName)338 CreateFullDestPath(
339   IN CONST CHAR16 **DestPath,
340   OUT CHAR16      **FullDestPath,
341   IN CONST CHAR16 *FileName
342   )
343 {
344   UINTN Size;
345   if (FullDestPath == NULL || FileName == NULL || DestPath == NULL || *DestPath == NULL){
346     return (EFI_INVALID_PARAMETER);
347   }
348 
349   Size = StrSize(*DestPath) + StrSize(FileName);
350 
351   *FullDestPath = AllocateZeroPool(Size);
352   if (*FullDestPath == NULL){
353     return (EFI_OUT_OF_RESOURCES);
354   }
355 
356   StrCpyS(*FullDestPath, Size / sizeof(CHAR16), *DestPath);
357   if ((*FullDestPath)[StrLen(*FullDestPath)-1] != L'\\' && FileName[0] != L'\\') {
358     StrCatS(*FullDestPath, Size / sizeof(CHAR16), L"\\");
359   }
360   StrCatS(*FullDestPath, Size / sizeof(CHAR16), FileName);
361 
362   return (EFI_SUCCESS);
363 }
364 
365 /**
366   Function to do a move within a file system.
367 
368   @param[in] Node               A pointer to the file to be removed.
369   @param[in] DestPath           A pointer to the destination file path.
370   @param[out] Resp              A pointer to response from question.  Pass back on looped calling.
371 
372   @retval SHELL_SUCCESS           The source file was moved to the destination.
373   @retval SHELL_OUT_OF_RESOURCES  A memory allocation failed.
374 **/
375 EFI_STATUS
376 EFIAPI
MoveWithinFileSystems(IN EFI_SHELL_FILE_INFO * Node,IN CHAR16 * DestPath,OUT VOID ** Resp)377 MoveWithinFileSystems(
378   IN EFI_SHELL_FILE_INFO  *Node,
379   IN CHAR16               *DestPath,
380   OUT VOID                **Resp
381   )
382 {
383   EFI_FILE_INFO             *NewFileInfo;
384   CHAR16                    *TempLocation;
385   UINTN                     NewSize;
386   UINTN                     Length;
387   EFI_STATUS                Status;
388 
389   //
390   // Chop off map info from DestPath
391   //
392   if ((TempLocation = StrStr(DestPath, L":")) != NULL) {
393     CopyMem(DestPath, TempLocation+1, StrSize(TempLocation+1));
394   }
395 
396   //
397   // construct the new file info block
398   //
399   NewSize = StrSize(DestPath);
400   NewSize += StrSize(Node->FileName) + SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
401   NewFileInfo = AllocateZeroPool(NewSize);
402   if (NewFileInfo == NULL) {
403     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle);
404     Status = EFI_OUT_OF_RESOURCES;
405   } else {
406     CopyMem(NewFileInfo, Node->Info, SIZE_OF_EFI_FILE_INFO);
407     if (DestPath[0] != L'\\') {
408       StrCpyS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), L"\\");
409       StrCatS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), DestPath);
410     } else {
411       StrCpyS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), DestPath);
412     }
413     Length = StrLen(NewFileInfo->FileName);
414     if (Length > 0) {
415       Length--;
416     }
417     if (NewFileInfo->FileName[Length] == L'\\') {
418       if (Node->FileName[0] == L'\\') {
419         //
420         // Don't allow for double slashes. Eliminate one of them.
421         //
422         NewFileInfo->FileName[Length] = CHAR_NULL;
423       }
424       StrCatS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), Node->FileName);
425     }
426     NewFileInfo->Size = SIZE_OF_EFI_FILE_INFO + StrSize(NewFileInfo->FileName);
427 
428     //
429     // Perform the move operation
430     //
431     Status = ShellSetFileInfo(Node->Handle, NewFileInfo);
432 
433     //
434     // Free the info object we used...
435     //
436     FreePool(NewFileInfo);
437   }
438 
439   return (Status);
440 }
441 /**
442   function to take a list of files to move and a destination location and do
443   the verification and moving of those files to that location.  This function
444   will report any errors to the user and continue to move the rest of the files.
445 
446   @param[in] FileList           A LIST_ENTRY* based list of files to move
447   @param[out] Resp              pointer to response from question.  Pass back on looped calling
448   @param[in] DestParameter      the originally specified destination location
449 
450   @retval SHELL_SUCCESS             the files were all moved.
451   @retval SHELL_INVALID_PARAMETER   a parameter was invalid
452   @retval SHELL_SECURITY_VIOLATION  a security violation ocurred
453   @retval SHELL_WRITE_PROTECTED     the destination was write protected
454   @retval SHELL_OUT_OF_RESOURCES    a memory allocation failed
455 **/
456 SHELL_STATUS
457 EFIAPI
ValidateAndMoveFiles(IN EFI_SHELL_FILE_INFO * FileList,OUT VOID ** Resp,IN CONST CHAR16 * DestParameter)458 ValidateAndMoveFiles(
459   IN EFI_SHELL_FILE_INFO        *FileList,
460   OUT VOID                      **Resp,
461   IN CONST CHAR16               *DestParameter
462   )
463 {
464   EFI_STATUS                Status;
465   CHAR16                    *HiiOutput;
466   CHAR16                    *HiiResultOk;
467   CHAR16                    *DestPath;
468   CHAR16                    *FullDestPath;
469   CONST CHAR16              *Cwd;
470   CHAR16                    *FullCwd;
471   SHELL_STATUS              ShellStatus;
472   EFI_SHELL_FILE_INFO       *Node;
473   VOID                      *Response;
474   UINT64                    Attr;
475   CHAR16                    *CleanFilePathStr;
476 
477   ASSERT(FileList != NULL);
478   ASSERT(DestParameter  != NULL);
479 
480   DestPath          = NULL;
481   FullDestPath      = NULL;
482   Cwd               = ShellGetCurrentDir(NULL);
483   Response          = *Resp;
484   Attr              = 0;
485   CleanFilePathStr  = NULL;
486 
487   FullCwd = AllocateZeroPool(StrSize(Cwd) + sizeof(CHAR16));
488   if (FullCwd == NULL) {
489     return SHELL_OUT_OF_RESOURCES;
490   } else {
491     StrCpyS(FullCwd, StrSize(Cwd)/sizeof(CHAR16)+1, Cwd);
492     StrCatS(FullCwd, StrSize(Cwd)/sizeof(CHAR16)+1, L"\\");
493   }
494 
495   Status = ShellLevel2StripQuotes (DestParameter, &CleanFilePathStr);
496   if (EFI_ERROR (Status)) {
497     FreePool (FullCwd);
498     if (Status == EFI_OUT_OF_RESOURCES) {
499       return SHELL_OUT_OF_RESOURCES;
500     } else {
501       return SHELL_INVALID_PARAMETER;
502     }
503   }
504 
505   ASSERT (CleanFilePathStr != NULL);
506 
507   //
508   // Get and validate the destination location
509   //
510   ShellStatus = GetDestinationLocation(CleanFilePathStr, &DestPath, FullCwd, (BOOLEAN)(FileList->Link.ForwardLink == FileList->Link.BackLink), &Attr);
511   FreePool (CleanFilePathStr);
512 
513   if (ShellStatus != SHELL_SUCCESS) {
514     FreePool (FullCwd);
515     return (ShellStatus);
516   }
517   DestPath = PathCleanUpDirectories(DestPath);
518   if (DestPath == NULL) {
519     FreePool (FullCwd);
520     return (SHELL_OUT_OF_RESOURCES);
521   }
522 
523   HiiOutput   = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_MV_OUTPUT), NULL);
524   HiiResultOk = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_GEN_RES_OK), NULL);
525   if (HiiOutput == NULL || HiiResultOk == NULL) {
526     SHELL_FREE_NON_NULL(DestPath);
527     SHELL_FREE_NON_NULL(HiiOutput);
528     SHELL_FREE_NON_NULL(HiiResultOk);
529     FreePool (FullCwd);
530     return (SHELL_OUT_OF_RESOURCES);
531   }
532 
533   //
534   // Go through the list of files and directories to move...
535   //
536   for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
537     ;  !IsNull(&FileList->Link, &Node->Link)
538     ;  Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
539    ){
540     if (ShellGetExecutionBreakFlag()) {
541       break;
542     }
543 
544     //
545     // These should never be NULL
546     //
547     ASSERT(Node->FileName != NULL);
548     ASSERT(Node->FullName != NULL);
549     ASSERT(Node->Info     != NULL);
550 
551     //
552     // skip the directory traversing stuff...
553     //
554     if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
555       continue;
556     }
557 
558     SHELL_FREE_NON_NULL(FullDestPath);
559     FullDestPath = NULL;
560     if (ShellIsDirectory(DestPath)==EFI_SUCCESS) {
561       CreateFullDestPath((CONST CHAR16 **)&DestPath, &FullDestPath, Node->FileName);
562     }
563 
564     //
565     // Validate that the move is valid
566     //
567     if (!IsValidMove(Node->FullName, FullCwd, FullDestPath!=NULL? FullDestPath:DestPath, Node->Info->Attribute, Attr, Node->Status)) {
568       ShellStatus = SHELL_INVALID_PARAMETER;
569       continue;
570     }
571 
572     ShellPrintEx(-1, -1, HiiOutput, Node->FullName, FullDestPath!=NULL? FullDestPath:DestPath);
573 
574     //
575     // See if destination exists
576     //
577     if (!EFI_ERROR(ShellFileExists(FullDestPath!=NULL? FullDestPath:DestPath))) {
578       if (Response == NULL) {
579         ShellPromptForResponseHii(ShellPromptResponseTypeYesNoAllCancel, STRING_TOKEN (STR_GEN_DEST_EXIST_OVR), gShellLevel2HiiHandle, &Response);
580       }
581       switch (*(SHELL_PROMPT_RESPONSE*)Response) {
582         case ShellPromptResponseNo:
583           FreePool(Response);
584           Response = NULL;
585           continue;
586         case ShellPromptResponseCancel:
587           *Resp = Response;
588           //
589           // indicate to stop everything
590           //
591           FreePool(FullCwd);
592           return (SHELL_ABORTED);
593         case ShellPromptResponseAll:
594           *Resp = Response;
595           break;
596         case ShellPromptResponseYes:
597           FreePool(Response);
598           Response = NULL;
599           break;
600         default:
601           FreePool(Response);
602           FreePool(FullCwd);
603           return SHELL_ABORTED;
604       }
605       Status = ShellDeleteFileByName(FullDestPath!=NULL? FullDestPath:DestPath);
606     }
607 
608     if (IsBetweenFileSystem(Node->FullName, FullCwd, DestPath)) {
609       while (FullDestPath == NULL && DestPath != NULL && DestPath[0] != CHAR_NULL && DestPath[StrLen(DestPath) - 1] == L'\\') {
610         DestPath[StrLen(DestPath) - 1] = CHAR_NULL;
611       }
612       Status = MoveBetweenFileSystems(Node, FullDestPath!=NULL? FullDestPath:DestPath, &Response);
613     } else {
614       Status = MoveWithinFileSystems(Node, DestPath, &Response);
615       //
616       // Display error status
617       //
618       if (EFI_ERROR(Status)) {
619         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_UK), gShellLevel2HiiHandle, L"mv", Status);
620       }
621     }
622 
623     //
624     // Check our result
625     //
626     if (EFI_ERROR(Status)) {
627       ShellStatus = SHELL_INVALID_PARAMETER;
628       if (Status == EFI_SECURITY_VIOLATION) {
629         ShellStatus = SHELL_SECURITY_VIOLATION;
630       } else if (Status == EFI_WRITE_PROTECTED) {
631         ShellStatus = SHELL_WRITE_PROTECTED;
632       } else if (Status == EFI_OUT_OF_RESOURCES) {
633         ShellStatus = SHELL_OUT_OF_RESOURCES;
634       } else if (Status == EFI_DEVICE_ERROR) {
635         ShellStatus = SHELL_DEVICE_ERROR;
636       } else if (Status == EFI_ACCESS_DENIED) {
637         ShellStatus = SHELL_ACCESS_DENIED;
638       }
639     } else {
640       ShellPrintEx(-1, -1, L"%s", HiiResultOk);
641     }
642 
643   } // main for loop
644 
645   SHELL_FREE_NON_NULL(FullDestPath);
646   SHELL_FREE_NON_NULL(DestPath);
647   SHELL_FREE_NON_NULL(HiiOutput);
648   SHELL_FREE_NON_NULL(HiiResultOk);
649   FreePool (FullCwd);
650   return (ShellStatus);
651 }
652 
653 /**
654   Function for 'mv' command.
655 
656   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
657   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
658 **/
659 SHELL_STATUS
660 EFIAPI
ShellCommandRunMv(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)661 ShellCommandRunMv (
662   IN EFI_HANDLE        ImageHandle,
663   IN EFI_SYSTEM_TABLE  *SystemTable
664   )
665 {
666   EFI_STATUS          Status;
667   LIST_ENTRY          *Package;
668   CHAR16              *ProblemParam;
669   CHAR16              *Cwd;
670   UINTN               CwdSize;
671   SHELL_STATUS        ShellStatus;
672   UINTN               ParamCount;
673   UINTN               LoopCounter;
674   EFI_SHELL_FILE_INFO *FileList;
675   VOID                *Response;
676 
677   ProblemParam        = NULL;
678   ShellStatus         = SHELL_SUCCESS;
679   ParamCount          = 0;
680   FileList            = NULL;
681   Response            = NULL;
682 
683   //
684   // initialize the shell lib (we must be in non-auto-init...)
685   //
686   Status = ShellInitialize();
687   ASSERT_EFI_ERROR(Status);
688 
689   //
690   // parse the command line
691   //
692   Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
693   if (EFI_ERROR(Status)) {
694     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
695       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"mv", ProblemParam);
696       FreePool(ProblemParam);
697       ShellStatus = SHELL_INVALID_PARAMETER;
698     } else {
699       ASSERT(FALSE);
700     }
701   } else {
702     //
703     // check for "-?"
704     //
705     if (ShellCommandLineGetFlag(Package, L"-?")) {
706       ASSERT(FALSE);
707     }
708 
709     switch (ParamCount = ShellCommandLineGetCount(Package)) {
710       case 0:
711       case 1:
712         //
713         // we have insufficient parameters
714         //
715         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"mv");
716         ShellStatus = SHELL_INVALID_PARAMETER;
717         break;
718       case 2:
719         //
720         // must have valid CWD for single parameter...
721         //
722         if (ShellGetCurrentDir(NULL) == NULL){
723           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"mv");
724           ShellStatus = SHELL_INVALID_PARAMETER;
725         } else {
726           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
727           if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
728             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, 1));
729             ShellStatus = SHELL_NOT_FOUND;
730           } else  {
731             //
732             // ValidateAndMoveFiles will report errors to the screen itself
733             //
734             CwdSize = StrSize(ShellGetCurrentDir(NULL)) + sizeof(CHAR16);
735             Cwd = AllocateZeroPool(CwdSize);
736             ASSERT (Cwd != NULL);
737             StrCpyS(Cwd, CwdSize/sizeof(CHAR16), ShellGetCurrentDir(NULL));
738             StrCatS(Cwd, CwdSize/sizeof(CHAR16), L"\\");
739             ShellStatus = ValidateAndMoveFiles(FileList, &Response, Cwd);
740             FreePool(Cwd);
741           }
742         }
743 
744         break;
745       default:
746         ///@todo make sure this works with error half way through and continues...
747         for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount ; LoopCounter++) {
748           if (ShellGetExecutionBreakFlag()) {
749             break;
750           }
751           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
752           if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
753             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, LoopCounter));
754             ShellStatus = SHELL_NOT_FOUND;
755           } else  {
756             //
757             // ValidateAndMoveFiles will report errors to the screen itself
758             // Only change ShellStatus if it's sucessful
759             //
760             if (ShellStatus == SHELL_SUCCESS) {
761               ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
762             } else {
763               ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
764             }
765           }
766           if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
767             Status = ShellCloseFileMetaArg(&FileList);
768             if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
769               ShellStatus = SHELL_ACCESS_DENIED;
770               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, 1), ShellStatus|MAX_BIT);
771             }
772           }
773         }
774         break;
775     } // switch on parameter count
776 
777     if (FileList != NULL) {
778       ShellCloseFileMetaArg(&FileList);
779     }
780 
781     //
782     // free the command line package
783     //
784     ShellCommandLineFreeVarList (Package);
785   }
786 
787   SHELL_FREE_NON_NULL(Response);
788 
789   if (ShellGetExecutionBreakFlag()) {
790     return (SHELL_ABORTED);
791   }
792 
793   return (ShellStatus);
794 }
795