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