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