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