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