Home | History | Annotate | Download | only in UefiShellLevel2CommandsLib
      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 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
     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
    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
    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
    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
    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
    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
    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
    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