Home | History | Annotate | Download | only in UefiShellLevel2CommandsLib
      1 /** @file
      2   Main file for cp shell level 2 function.
      3 
      4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
      5   Copyright (c) 2009 - 2015, 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 #include <Guid/FileSystemInfo.h>
     18 #include <Guid/FileSystemVolumeLabelInfo.h>
     19 
     20 /**
     21   Function to take a list of files to copy and a destination location and do
     22   the verification and copying of those files to that location.  This function
     23   will report any errors to the user and halt.
     24 
     25   @param[in] FileList           A LIST_ENTRY* based list of files to move.
     26   @param[in] DestDir            The destination location.
     27   @param[in] SilentMode         TRUE to eliminate screen output.
     28   @param[in] RecursiveMode      TRUE to copy directories.
     29   @param[in] Resp               The response to the overwrite query (if always).
     30 
     31   @retval SHELL_SUCCESS             the files were all moved.
     32   @retval SHELL_INVALID_PARAMETER   a parameter was invalid
     33   @retval SHELL_SECURITY_VIOLATION  a security violation ocurred
     34   @retval SHELL_WRITE_PROTECTED     the destination was write protected
     35   @retval SHELL_OUT_OF_RESOURCES    a memory allocation failed
     36 **/
     37 SHELL_STATUS
     38 EFIAPI
     39 ValidateAndCopyFiles(
     40   IN CONST EFI_SHELL_FILE_INFO  *FileList,
     41   IN CONST CHAR16               *DestDir,
     42   IN BOOLEAN                    SilentMode,
     43   IN BOOLEAN                    RecursiveMode,
     44   IN VOID                       **Resp
     45   );
     46 
     47 /**
     48   Function to Copy one file to another location
     49 
     50   If the destination exists the user will be prompted and the result put into *resp
     51 
     52   @param[in] Source     pointer to source file name
     53   @param[in] Dest       pointer to destination file name
     54   @param[out] Resp      pointer to response from question.  Pass back on looped calling
     55   @param[in] SilentMode whether to run in quiet mode or not
     56   @param[in] CmdName    Source command name requesting single file copy
     57 
     58   @retval SHELL_SUCCESS   The source file was copied to the destination
     59 **/
     60 SHELL_STATUS
     61 EFIAPI
     62 CopySingleFile(
     63   IN CONST CHAR16 *Source,
     64   IN CONST CHAR16 *Dest,
     65   OUT VOID        **Resp,
     66   IN BOOLEAN      SilentMode,
     67   IN CONST CHAR16 *CmdName
     68   )
     69 {
     70   VOID                  *Response;
     71   UINTN                 ReadSize;
     72   SHELL_FILE_HANDLE     SourceHandle;
     73   SHELL_FILE_HANDLE     DestHandle;
     74   EFI_STATUS            Status;
     75   VOID                  *Buffer;
     76   CHAR16                *TempName;
     77   UINTN                 Size;
     78   EFI_SHELL_FILE_INFO   *List;
     79   SHELL_STATUS          ShellStatus;
     80   UINT64                SourceFileSize;
     81   UINT64                DestFileSize;
     82   EFI_FILE_PROTOCOL     *DestVolumeFP;
     83   EFI_FILE_SYSTEM_INFO  *DestVolumeInfo;
     84   UINTN                 DestVolumeInfoSize;
     85 
     86   ASSERT(Resp != NULL);
     87 
     88   SourceHandle    = NULL;
     89   DestHandle      = NULL;
     90   Response        = *Resp;
     91   List            = NULL;
     92   DestVolumeInfo  = NULL;
     93   ShellStatus     = SHELL_SUCCESS;
     94 
     95   ReadSize = PcdGet32(PcdShellFileOperationSize);
     96   // Why bother copying a file to itself
     97   if (StrCmp(Source, Dest) == 0) {
     98     return (SHELL_SUCCESS);
     99   }
    100 
    101   //
    102   // if the destination file existed check response and possibly prompt user
    103   //
    104   if (ShellFileExists(Dest) == EFI_SUCCESS) {
    105     if (Response == NULL && !SilentMode) {
    106       Status = ShellPromptForResponseHii(ShellPromptResponseTypeYesNoAllCancel, STRING_TOKEN (STR_GEN_DEST_EXIST_OVR), gShellLevel2HiiHandle, &Response);
    107     }
    108     //
    109     // possibly return based on response
    110     //
    111     if (!SilentMode) {
    112       switch (*(SHELL_PROMPT_RESPONSE*)Response) {
    113         case ShellPromptResponseNo:
    114           //
    115           // return success here so we dont stop the process
    116           //
    117           return (SHELL_SUCCESS);
    118         case ShellPromptResponseCancel:
    119           *Resp = Response;
    120           //
    121           // indicate to stop everything
    122           //
    123           return (SHELL_ABORTED);
    124         case ShellPromptResponseAll:
    125           *Resp = Response;
    126         case ShellPromptResponseYes:
    127           break;
    128         default:
    129           return SHELL_ABORTED;
    130       }
    131     }
    132   }
    133 
    134   if (ShellIsDirectory(Source) == EFI_SUCCESS) {
    135     Status = ShellCreateDirectory(Dest, &DestHandle);
    136     if (EFI_ERROR(Status)) {
    137       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DEST_DIR_FAIL), gShellLevel2HiiHandle, CmdName, Dest);
    138       return (SHELL_ACCESS_DENIED);
    139     }
    140 
    141     //
    142     // Now copy all the files under the directory...
    143     //
    144     TempName    = NULL;
    145     Size        = 0;
    146     StrnCatGrow(&TempName, &Size, Source, 0);
    147     StrnCatGrow(&TempName, &Size, L"\\*", 0);
    148     if (TempName != NULL) {
    149       ShellOpenFileMetaArg((CHAR16*)TempName, EFI_FILE_MODE_READ, &List);
    150       *TempName = CHAR_NULL;
    151       StrnCatGrow(&TempName, &Size, Dest, 0);
    152       StrnCatGrow(&TempName, &Size, L"\\", 0);
    153       ShellStatus = ValidateAndCopyFiles(List, TempName, SilentMode, TRUE, Resp);
    154       ShellCloseFileMetaArg(&List);
    155       SHELL_FREE_NON_NULL(TempName);
    156       Size = 0;
    157     }
    158   } else {
    159     Status = ShellDeleteFileByName(Dest);
    160 
    161     //
    162     // open file with create enabled
    163     //
    164     Status = ShellOpenFileByName(Dest, &DestHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, 0);
    165     if (EFI_ERROR(Status)) {
    166       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DEST_OPEN_FAIL), gShellLevel2HiiHandle, CmdName, Dest);
    167       return (SHELL_ACCESS_DENIED);
    168     }
    169 
    170     //
    171     // open source file
    172     //
    173     Status = ShellOpenFileByName (Source, &SourceHandle, EFI_FILE_MODE_READ, 0);
    174     if (EFI_ERROR (Status)) {
    175       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_CP_SRC_OPEN_FAIL), gShellLevel2HiiHandle, CmdName, Source);
    176       return (SHELL_ACCESS_DENIED);
    177     }
    178 
    179     //
    180     //get file size of source file and freespace available on destination volume
    181     //
    182     ShellGetFileSize(SourceHandle, &SourceFileSize);
    183     ShellGetFileSize(DestHandle, &DestFileSize);
    184 
    185     //
    186     //if the destination file already exists then it will be replaced, meaning the sourcefile effectively needs less storage space
    187     //
    188     if(DestFileSize < SourceFileSize){
    189       SourceFileSize -= DestFileSize;
    190     } else {
    191       SourceFileSize = 0;
    192     }
    193 
    194     //
    195     //get the system volume info to check the free space
    196     //
    197     DestVolumeFP = ConvertShellHandleToEfiFileProtocol(DestHandle);
    198     DestVolumeInfo = NULL;
    199     DestVolumeInfoSize = 0;
    200     Status = DestVolumeFP->GetInfo(
    201       DestVolumeFP,
    202       &gEfiFileSystemInfoGuid,
    203       &DestVolumeInfoSize,
    204       DestVolumeInfo
    205       );
    206 
    207     if (Status == EFI_BUFFER_TOO_SMALL) {
    208       DestVolumeInfo = AllocateZeroPool(DestVolumeInfoSize);
    209       Status = DestVolumeFP->GetInfo(
    210         DestVolumeFP,
    211         &gEfiFileSystemInfoGuid,
    212         &DestVolumeInfoSize,
    213         DestVolumeInfo
    214         );
    215     }
    216 
    217     //
    218     //check if enough space available on destination drive to complete copy
    219     //
    220     if (DestVolumeInfo!= NULL && (DestVolumeInfo->FreeSpace < SourceFileSize)) {
    221       //
    222       //not enough space on destination directory to copy file
    223       //
    224       SHELL_FREE_NON_NULL(DestVolumeInfo);
    225       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_FAIL), gShellLevel2HiiHandle, CmdName);
    226       return(SHELL_VOLUME_FULL);
    227     } else {
    228       //
    229       // copy data between files
    230       //
    231       Buffer = AllocateZeroPool(ReadSize);
    232       ASSERT(Buffer != NULL);
    233       while (ReadSize == PcdGet32(PcdShellFileOperationSize) && !EFI_ERROR(Status)) {
    234         Status = ShellReadFile(SourceHandle, &ReadSize, Buffer);
    235         if (!EFI_ERROR(Status)) {
    236           Status = ShellWriteFile(DestHandle, &ReadSize, Buffer);
    237           if (EFI_ERROR(Status)) {
    238             ShellStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
    239             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_WRITE_ERROR), gShellLevel2HiiHandle, CmdName, Dest);
    240             break;
    241           }
    242         } else {
    243           ShellStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
    244           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_READ_ERROR), gShellLevel2HiiHandle, CmdName, Source);
    245           break;
    246         }
    247       }
    248     }
    249     SHELL_FREE_NON_NULL(DestVolumeInfo);
    250   }
    251 
    252   //
    253   // close files
    254   //
    255   if (DestHandle != NULL) {
    256     ShellCloseFile(&DestHandle);
    257     DestHandle   = NULL;
    258   }
    259   if (SourceHandle != NULL) {
    260     ShellCloseFile(&SourceHandle);
    261     SourceHandle = NULL;
    262   }
    263 
    264   //
    265   // return
    266   //
    267   return ShellStatus;
    268 }
    269 
    270 /**
    271   function to take a list of files to copy and a destination location and do
    272   the verification and copying of those files to that location.  This function
    273   will report any errors to the user and halt.
    274 
    275   The key is to have this function called ONLY once.  this allows for the parameter
    276   verification to happen correctly.
    277 
    278   @param[in] FileList           A LIST_ENTRY* based list of files to move.
    279   @param[in] DestDir            The destination location.
    280   @param[in] SilentMode         TRUE to eliminate screen output.
    281   @param[in] RecursiveMode      TRUE to copy directories.
    282   @param[in] Resp               The response to the overwrite query (if always).
    283 
    284   @retval SHELL_SUCCESS             the files were all moved.
    285   @retval SHELL_INVALID_PARAMETER   a parameter was invalid
    286   @retval SHELL_SECURITY_VIOLATION  a security violation ocurred
    287   @retval SHELL_WRITE_PROTECTED     the destination was write protected
    288   @retval SHELL_OUT_OF_RESOURCES    a memory allocation failed
    289 **/
    290 SHELL_STATUS
    291 EFIAPI
    292 ValidateAndCopyFiles(
    293   IN CONST EFI_SHELL_FILE_INFO  *FileList,
    294   IN CONST CHAR16               *DestDir,
    295   IN BOOLEAN                    SilentMode,
    296   IN BOOLEAN                    RecursiveMode,
    297   IN VOID                       **Resp
    298   )
    299 {
    300   CHAR16                    *HiiOutput;
    301   CHAR16                    *HiiResultOk;
    302   CONST EFI_SHELL_FILE_INFO *Node;
    303   SHELL_STATUS              ShellStatus;
    304   EFI_STATUS                Status;
    305   CHAR16                    *DestPath;
    306   VOID                      *Response;
    307   UINTN                     PathSize;
    308   CONST CHAR16              *Cwd;
    309   UINTN                     NewSize;
    310   CHAR16                    *CleanFilePathStr;
    311 
    312   if (Resp == NULL) {
    313     Response = NULL;
    314   } else {
    315     Response = *Resp;
    316   }
    317 
    318   DestPath         = NULL;
    319   ShellStatus      = SHELL_SUCCESS;
    320   PathSize         = 0;
    321   Cwd              = ShellGetCurrentDir(NULL);
    322   CleanFilePathStr = NULL;
    323 
    324   ASSERT(FileList != NULL);
    325   ASSERT(DestDir  != NULL);
    326 
    327 
    328   Status = ShellLevel2StripQuotes (DestDir, &CleanFilePathStr);
    329   if (EFI_ERROR (Status)) {
    330     if (Status == EFI_OUT_OF_RESOURCES) {
    331       return SHELL_OUT_OF_RESOURCES;
    332     } else {
    333       return SHELL_INVALID_PARAMETER;
    334     }
    335   }
    336 
    337   ASSERT (CleanFilePathStr != NULL);
    338 
    339   //
    340   // If we are trying to copy multiple files... make sure we got a directory for the target...
    341   //
    342   if (EFI_ERROR(ShellIsDirectory(CleanFilePathStr)) && FileList->Link.ForwardLink != FileList->Link.BackLink) {
    343     //
    344     // Error for destination not a directory
    345     //
    346     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cp", CleanFilePathStr);
    347     FreePool (CleanFilePathStr);
    348     return (SHELL_INVALID_PARAMETER);
    349   }
    350   for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
    351     ;  !IsNull(&FileList->Link, &Node->Link)
    352     ;  Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
    353     ){
    354     //
    355     // skip the directory traversing stuff...
    356     //
    357     if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
    358       continue;
    359     }
    360 
    361     NewSize =  StrSize(CleanFilePathStr);
    362     NewSize += StrSize(Node->FullName);
    363     NewSize += (Cwd == NULL)? 0 : (StrSize(Cwd) + sizeof(CHAR16));
    364     if (NewSize > PathSize) {
    365       PathSize = NewSize;
    366     }
    367 
    368     //
    369     // Make sure got -r if required
    370     //
    371     if (!RecursiveMode && !EFI_ERROR(ShellIsDirectory(Node->FullName))) {
    372       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DIR_REQ), gShellLevel2HiiHandle, L"cp");
    373       FreePool (CleanFilePathStr);
    374       return (SHELL_INVALID_PARAMETER);
    375     }
    376 
    377     //
    378     // make sure got dest as dir if needed
    379     //
    380     if (!EFI_ERROR(ShellIsDirectory(Node->FullName)) && EFI_ERROR(ShellIsDirectory(CleanFilePathStr))) {
    381       //
    382       // Error for destination not a directory
    383       //
    384       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cp", CleanFilePathStr);
    385       FreePool (CleanFilePathStr);
    386       return (SHELL_INVALID_PARAMETER);
    387     }
    388   }
    389 
    390   HiiOutput   = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_CP_OUTPUT), NULL);
    391   HiiResultOk = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_GEN_RES_OK), NULL);
    392   DestPath    = AllocateZeroPool(PathSize);
    393 
    394   if (DestPath == NULL || HiiOutput == NULL || HiiResultOk == NULL) {
    395     SHELL_FREE_NON_NULL(DestPath);
    396     SHELL_FREE_NON_NULL(HiiOutput);
    397     SHELL_FREE_NON_NULL(HiiResultOk);
    398     FreePool (CleanFilePathStr);
    399     return (SHELL_OUT_OF_RESOURCES);
    400   }
    401 
    402   //
    403   // Go through the list of files to copy...
    404   //
    405   for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
    406     ;  !IsNull(&FileList->Link, &Node->Link)
    407     ;  Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
    408     ){
    409     if (ShellGetExecutionBreakFlag()) {
    410       break;
    411     }
    412     ASSERT(Node->FileName != NULL);
    413     ASSERT(Node->FullName != NULL);
    414 
    415     //
    416     // skip the directory traversing stuff...
    417     //
    418     if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
    419       continue;
    420     }
    421 
    422     if (FileList->Link.ForwardLink == FileList->Link.BackLink // 1 item
    423       && EFI_ERROR(ShellIsDirectory(CleanFilePathStr))                 // not an existing directory
    424       ) {
    425       if (StrStr(CleanFilePathStr, L":") == NULL) {
    426         //
    427         // simple copy of a single file
    428         //
    429         if (Cwd != NULL) {
    430           StrCpyS(DestPath, PathSize / sizeof(CHAR16), Cwd);
    431           StrCatS(DestPath, PathSize / sizeof(CHAR16), L"\\");
    432         } else {
    433           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cp", CleanFilePathStr);
    434           FreePool (CleanFilePathStr);
    435           return (SHELL_INVALID_PARAMETER);
    436         }
    437         if (DestPath[StrLen(DestPath)-1] != L'\\' && CleanFilePathStr[0] != L'\\') {
    438           StrCatS(DestPath, PathSize / sizeof(CHAR16), L"\\");
    439         } else if (DestPath[StrLen(DestPath)-1] == L'\\' && CleanFilePathStr[0] == L'\\') {
    440           ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
    441         }
    442         StrCatS(DestPath, PathSize/sizeof(CHAR16), CleanFilePathStr);
    443       } else {
    444         StrCpyS(DestPath, PathSize/sizeof(CHAR16), CleanFilePathStr);
    445       }
    446     } else {
    447       //
    448       // we have multiple files or a directory in the DestDir
    449       //
    450 
    451       //
    452       // Check for leading slash
    453       //
    454       if (CleanFilePathStr[0] == L'\\') {
    455          //
    456          // Copy to the root of CWD
    457          //
    458         if (Cwd != NULL) {
    459           StrCpyS(DestPath, PathSize/sizeof(CHAR16), Cwd);
    460           StrCatS(DestPath, PathSize/sizeof(CHAR16), L"\\");
    461         } else {
    462           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cp",  CleanFilePathStr);
    463           FreePool(CleanFilePathStr);
    464           return (SHELL_INVALID_PARAMETER);
    465         }
    466         while (PathRemoveLastItem(DestPath));
    467         StrCatS(DestPath, PathSize/sizeof(CHAR16), CleanFilePathStr+1);
    468         StrCatS(DestPath, PathSize/sizeof(CHAR16), Node->FileName);
    469       } else if (StrStr(CleanFilePathStr, L":") == NULL) {
    470         if (Cwd != NULL) {
    471           StrCpyS(DestPath, PathSize/sizeof(CHAR16), Cwd);
    472           StrCatS(DestPath, PathSize/sizeof(CHAR16), L"\\");
    473         } else {
    474           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cp", CleanFilePathStr);
    475           FreePool(CleanFilePathStr);
    476           return (SHELL_INVALID_PARAMETER);
    477         }
    478         if (DestPath[StrLen(DestPath)-1] != L'\\' && CleanFilePathStr[0] != L'\\') {
    479           StrCatS(DestPath, PathSize/sizeof(CHAR16), L"\\");
    480         } else if (DestPath[StrLen(DestPath)-1] == L'\\' && CleanFilePathStr[0] == L'\\') {
    481           ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
    482         }
    483         StrCatS(DestPath, PathSize/sizeof(CHAR16), CleanFilePathStr);
    484         if (CleanFilePathStr[StrLen(CleanFilePathStr)-1] != L'\\' && Node->FileName[0] != L'\\') {
    485           StrCatS(DestPath, PathSize/sizeof(CHAR16), L"\\");
    486         } else if (CleanFilePathStr[StrLen(CleanFilePathStr)-1] == L'\\' && Node->FileName[0] == L'\\') {
    487           ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
    488         }
    489         StrCatS(DestPath, PathSize/sizeof(CHAR16), Node->FileName);
    490 
    491       } else {
    492         StrCpyS(DestPath, PathSize/sizeof(CHAR16), CleanFilePathStr);
    493         if (CleanFilePathStr[StrLen(CleanFilePathStr)-1] != L'\\' && Node->FileName[0] != L'\\') {
    494           StrCatS(DestPath, PathSize/sizeof(CHAR16), L"\\");
    495         } else if (CleanFilePathStr[StrLen(CleanFilePathStr)-1] == L'\\' && Node->FileName[0] == L'\\') {
    496           ((CHAR16*)CleanFilePathStr)[StrLen(CleanFilePathStr)-1] = CHAR_NULL;
    497         }
    498         StrCatS(DestPath, PathSize/sizeof(CHAR16), Node->FileName);
    499       }
    500     }
    501 
    502     //
    503     // Make sure the path exists
    504     //
    505     if (EFI_ERROR(VerifyIntermediateDirectories(DestPath))) {
    506       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DIR_WNF), gShellLevel2HiiHandle, L"cp", DestPath);
    507       ShellStatus = SHELL_DEVICE_ERROR;
    508       break;
    509     }
    510 
    511     if ( !EFI_ERROR(ShellIsDirectory(Node->FullName))
    512       && !EFI_ERROR(ShellIsDirectory(DestPath))
    513       && StrniCmp(Node->FullName, DestPath, StrLen(DestPath)) == NULL
    514       ){
    515       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_SD_PARENT), gShellLevel2HiiHandle, L"cp");
    516       ShellStatus = SHELL_INVALID_PARAMETER;
    517       break;
    518     }
    519     if (StringNoCaseCompare(&Node->FullName, &DestPath) == 0) {
    520       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_SD_SAME), gShellLevel2HiiHandle, L"cp");
    521       ShellStatus = SHELL_INVALID_PARAMETER;
    522       break;
    523     }
    524 
    525     if ((StrniCmp(Node->FullName, DestPath, StrLen(Node->FullName)) == 0)
    526       && (DestPath[StrLen(Node->FullName)] == CHAR_NULL || DestPath[StrLen(Node->FullName)] == L'\\')
    527       ) {
    528       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_SD_SAME), gShellLevel2HiiHandle, L"cp");
    529       ShellStatus = SHELL_INVALID_PARAMETER;
    530       break;
    531     }
    532 
    533     PathCleanUpDirectories(DestPath);
    534 
    535     if (!SilentMode) {
    536       ShellPrintEx(-1, -1, HiiOutput, Node->FullName, DestPath);
    537     }
    538 
    539     //
    540     // copy single file...
    541     //
    542     ShellStatus = CopySingleFile(Node->FullName, DestPath, &Response, SilentMode, L"cp");
    543     if (ShellStatus != SHELL_SUCCESS) {
    544       break;
    545     }
    546   }
    547   if (ShellStatus == SHELL_SUCCESS && Resp == NULL) {
    548     ShellPrintEx(-1, -1, L"%s", HiiResultOk);
    549   }
    550 
    551   SHELL_FREE_NON_NULL(DestPath);
    552   SHELL_FREE_NON_NULL(HiiOutput);
    553   SHELL_FREE_NON_NULL(HiiResultOk);
    554   SHELL_FREE_NON_NULL(CleanFilePathStr);
    555   if (Resp == NULL) {
    556     SHELL_FREE_NON_NULL(Response);
    557   }
    558 
    559   return (ShellStatus);
    560 
    561 }
    562 
    563 /**
    564   Validate and if successful copy all the files from the list into
    565   destination directory.
    566 
    567   @param[in] FileList       The list of files to copy.
    568   @param[in] DestDir        The directory to copy files to.
    569   @param[in] SilentMode     TRUE to eliminate screen output.
    570   @param[in] RecursiveMode  TRUE to copy directories.
    571 
    572   @retval SHELL_INVALID_PARAMETER   A parameter was invalid.
    573   @retval SHELL_SUCCESS             The operation was successful.
    574 **/
    575 SHELL_STATUS
    576 EFIAPI
    577 ProcessValidateAndCopyFiles(
    578   IN       EFI_SHELL_FILE_INFO  *FileList,
    579   IN CONST CHAR16               *DestDir,
    580   IN BOOLEAN                    SilentMode,
    581   IN BOOLEAN                    RecursiveMode
    582   )
    583 {
    584   SHELL_STATUS        ShellStatus;
    585   EFI_SHELL_FILE_INFO *List;
    586   EFI_FILE_INFO       *FileInfo;
    587   CHAR16              *FullName;
    588 
    589   List      = NULL;
    590   FullName  = NULL;
    591   FileInfo  = NULL;
    592 
    593   ShellOpenFileMetaArg((CHAR16*)DestDir, EFI_FILE_MODE_READ, &List);
    594   if (List != NULL && List->Link.ForwardLink != List->Link.BackLink) {
    595     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, L"cp", DestDir);
    596     ShellStatus = SHELL_INVALID_PARAMETER;
    597     ShellCloseFileMetaArg(&List);
    598   } else if (List != NULL) {
    599     ASSERT(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink) != NULL);
    600     ASSERT(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->FullName != NULL);
    601     FileInfo = gEfiShellProtocol->GetFileInfo(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->Handle);
    602     ASSERT(FileInfo != NULL);
    603     StrnCatGrow(&FullName, NULL, ((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->FullName, 0);
    604     ShellCloseFileMetaArg(&List);
    605     if ((FileInfo->Attribute & EFI_FILE_READ_ONLY) == 0) {
    606       ShellStatus = ValidateAndCopyFiles(FileList, FullName, SilentMode, RecursiveMode, NULL);
    607     } else {
    608       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DEST_ERROR), gShellLevel2HiiHandle, L"cp");
    609       ShellStatus = SHELL_ACCESS_DENIED;
    610     }
    611   } else {
    612     ShellCloseFileMetaArg(&List);
    613     ShellStatus = ValidateAndCopyFiles(FileList, DestDir, SilentMode, RecursiveMode, NULL);
    614   }
    615 
    616   SHELL_FREE_NON_NULL(FileInfo);
    617   SHELL_FREE_NON_NULL(FullName);
    618   return (ShellStatus);
    619 }
    620 
    621 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
    622   {L"-r", TypeFlag},
    623   {L"-q", TypeFlag},
    624   {NULL, TypeMax}
    625   };
    626 
    627 /**
    628   Function for 'cp' command.
    629 
    630   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
    631   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
    632 **/
    633 SHELL_STATUS
    634 EFIAPI
    635 ShellCommandRunCp (
    636   IN EFI_HANDLE        ImageHandle,
    637   IN EFI_SYSTEM_TABLE  *SystemTable
    638   )
    639 {
    640   EFI_STATUS          Status;
    641   LIST_ENTRY          *Package;
    642   CHAR16              *ProblemParam;
    643   SHELL_STATUS        ShellStatus;
    644   UINTN               ParamCount;
    645   UINTN               LoopCounter;
    646   EFI_SHELL_FILE_INFO *FileList;
    647   BOOLEAN             SilentMode;
    648   BOOLEAN             RecursiveMode;
    649   CONST CHAR16        *Cwd;
    650   CHAR16              *FullCwd;
    651 
    652   ProblemParam        = NULL;
    653   ShellStatus         = SHELL_SUCCESS;
    654   ParamCount          = 0;
    655   FileList            = NULL;
    656 
    657   //
    658   // initialize the shell lib (we must be in non-auto-init...)
    659   //
    660   Status = ShellInitialize();
    661   ASSERT_EFI_ERROR(Status);
    662 
    663   Status = CommandInit();
    664   ASSERT_EFI_ERROR(Status);
    665 
    666   //
    667   // parse the command line
    668   //
    669   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
    670   if (EFI_ERROR(Status)) {
    671     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
    672       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"cp", ProblemParam);
    673       FreePool(ProblemParam);
    674       ShellStatus = SHELL_INVALID_PARAMETER;
    675     } else {
    676       ASSERT(FALSE);
    677     }
    678   } else {
    679     //
    680     // check for "-?"
    681     //
    682     if (ShellCommandLineGetFlag(Package, L"-?")) {
    683       ASSERT(FALSE);
    684     }
    685 
    686     //
    687     // Initialize SilentMode and RecursiveMode
    688     //
    689     if (gEfiShellProtocol->BatchIsActive()) {
    690       SilentMode = TRUE;
    691     } else {
    692       SilentMode = ShellCommandLineGetFlag(Package, L"-q");
    693     }
    694     RecursiveMode = ShellCommandLineGetFlag(Package, L"-r");
    695 
    696     switch (ParamCount = ShellCommandLineGetCount(Package)) {
    697       case 0:
    698       case 1:
    699         //
    700         // we have insufficient parameters
    701         //
    702         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"cp");
    703         ShellStatus = SHELL_INVALID_PARAMETER;
    704         break;
    705       case 2:
    706         //
    707         // must have valid CWD for single parameter...
    708         //
    709         Cwd = ShellGetCurrentDir(NULL);
    710         if (Cwd == NULL){
    711           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cp");
    712           ShellStatus = SHELL_INVALID_PARAMETER;
    713         } else {
    714           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
    715           if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
    716             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, 1));
    717             ShellStatus = SHELL_NOT_FOUND;
    718           } else  {
    719             FullCwd = AllocateZeroPool(StrSize(Cwd) + sizeof(CHAR16));
    720             ASSERT (FullCwd != NULL);
    721             StrCpyS(FullCwd, StrSize(Cwd)/sizeof(CHAR16)+1, Cwd);
    722             ShellStatus = ProcessValidateAndCopyFiles(FileList, FullCwd, SilentMode, RecursiveMode);
    723             FreePool(FullCwd);
    724           }
    725         }
    726 
    727         break;
    728       default:
    729         //
    730         // Make a big list of all the files...
    731         //
    732         for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount && ShellStatus == SHELL_SUCCESS ; LoopCounter++) {
    733           if (ShellGetExecutionBreakFlag()) {
    734             break;
    735           }
    736           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
    737           if (EFI_ERROR(Status) || FileList == NULL || IsListEmpty(&FileList->Link)) {
    738             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, LoopCounter));
    739             ShellStatus = SHELL_NOT_FOUND;
    740           }
    741         }
    742         if (ShellStatus != SHELL_SUCCESS) {
    743           Status = ShellCloseFileMetaArg(&FileList);
    744         } else {
    745           //
    746           // now copy them all...
    747           //
    748           if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
    749             ShellStatus = ProcessValidateAndCopyFiles(FileList, PathCleanUpDirectories((CHAR16*)ShellCommandLineGetRawValue(Package, ParamCount)), SilentMode, RecursiveMode);
    750             Status = ShellCloseFileMetaArg(&FileList);
    751             if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
    752               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, ParamCount), ShellStatus|MAX_BIT);
    753               ShellStatus = SHELL_ACCESS_DENIED;
    754             }
    755           }
    756         }
    757         break;
    758     } // switch on parameter count
    759 
    760     if (FileList != NULL) {
    761       ShellCloseFileMetaArg(&FileList);
    762     }
    763 
    764     //
    765     // free the command line package
    766     //
    767     ShellCommandLineFreeVarList (Package);
    768   }
    769 
    770   if (ShellGetExecutionBreakFlag()) {
    771     return (SHELL_ABORTED);
    772   }
    773 
    774   return (ShellStatus);
    775 }
    776 
    777