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 - 2016, Intel Corporation. All rights reserved.<BR>
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "UefiShellLevel2CommandsLib.h"
17 #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 ValidateAndCopyFiles(
39   IN CONST EFI_SHELL_FILE_INFO  *FileList,
40   IN CONST CHAR16               *DestDir,
41   IN BOOLEAN                    SilentMode,
42   IN BOOLEAN                    RecursiveMode,
43   IN VOID                       **Resp
44   );
45 
46 /**
47   Function to Copy one file to another location
48 
49   If the destination exists the user will be prompted and the result put into *resp
50 
51   @param[in] Source     pointer to source file name
52   @param[in] Dest       pointer to destination file name
53   @param[out] Resp      pointer to response from question.  Pass back on looped calling
54   @param[in] SilentMode whether to run in quiet mode or not
55   @param[in] CmdName    Source command name requesting single file copy
56 
57   @retval SHELL_SUCCESS   The source file was copied to the destination
58 **/
59 SHELL_STATUS
CopySingleFile(IN CONST CHAR16 * Source,IN CONST CHAR16 * Dest,OUT VOID ** Resp,IN BOOLEAN SilentMode,IN CONST CHAR16 * CmdName)60 CopySingleFile(
61   IN CONST CHAR16 *Source,
62   IN CONST CHAR16 *Dest,
63   OUT VOID        **Resp,
64   IN BOOLEAN      SilentMode,
65   IN CONST CHAR16 *CmdName
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, CmdName, 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, CmdName, 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     if (EFI_ERROR (Status)) {
173       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_CP_SRC_OPEN_FAIL), gShellLevel2HiiHandle, CmdName, Source);
174       return (SHELL_ACCESS_DENIED);
175     }
176 
177     //
178     //get file size of source file and freespace available on destination volume
179     //
180     ShellGetFileSize(SourceHandle, &SourceFileSize);
181     ShellGetFileSize(DestHandle, &DestFileSize);
182 
183     //
184     //if the destination file already exists then it will be replaced, meaning the sourcefile effectively needs less storage space
185     //
186     if(DestFileSize < SourceFileSize){
187       SourceFileSize -= DestFileSize;
188     } else {
189       SourceFileSize = 0;
190     }
191 
192     //
193     //get the system volume info to check the free space
194     //
195     DestVolumeFP = ConvertShellHandleToEfiFileProtocol(DestHandle);
196     DestVolumeInfo = NULL;
197     DestVolumeInfoSize = 0;
198     Status = DestVolumeFP->GetInfo(
199       DestVolumeFP,
200       &gEfiFileSystemInfoGuid,
201       &DestVolumeInfoSize,
202       DestVolumeInfo
203       );
204 
205     if (Status == EFI_BUFFER_TOO_SMALL) {
206       DestVolumeInfo = AllocateZeroPool(DestVolumeInfoSize);
207       Status = DestVolumeFP->GetInfo(
208         DestVolumeFP,
209         &gEfiFileSystemInfoGuid,
210         &DestVolumeInfoSize,
211         DestVolumeInfo
212         );
213     }
214 
215     //
216     //check if enough space available on destination drive to complete copy
217     //
218     if (DestVolumeInfo!= NULL && (DestVolumeInfo->FreeSpace < SourceFileSize)) {
219       //
220       //not enough space on destination directory to copy file
221       //
222       SHELL_FREE_NON_NULL(DestVolumeInfo);
223       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_FAIL), gShellLevel2HiiHandle, CmdName);
224       return(SHELL_VOLUME_FULL);
225     } else {
226       //
227       // copy data between files
228       //
229       Buffer = AllocateZeroPool(ReadSize);
230       if (Buffer == NULL) {
231         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellLevel2HiiHandle, CmdName);
232         return SHELL_OUT_OF_RESOURCES;
233       }
234       while (ReadSize == PcdGet32(PcdShellFileOperationSize) && !EFI_ERROR(Status)) {
235         Status = ShellReadFile(SourceHandle, &ReadSize, Buffer);
236         if (!EFI_ERROR(Status)) {
237           Status = ShellWriteFile(DestHandle, &ReadSize, Buffer);
238           if (EFI_ERROR(Status)) {
239             ShellStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
240             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_WRITE_ERROR), gShellLevel2HiiHandle, CmdName, Dest);
241             break;
242           }
243         } else {
244           ShellStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
245           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_CPY_READ_ERROR), gShellLevel2HiiHandle, CmdName, Source);
246           break;
247         }
248       }
249     }
250     SHELL_FREE_NON_NULL(DestVolumeInfo);
251   }
252 
253   //
254   // close files
255   //
256   if (DestHandle != NULL) {
257     ShellCloseFile(&DestHandle);
258     DestHandle   = NULL;
259   }
260   if (SourceHandle != NULL) {
261     ShellCloseFile(&SourceHandle);
262     SourceHandle = NULL;
263   }
264 
265   //
266   // return
267   //
268   return ShellStatus;
269 }
270 
271 /**
272   function to take a list of files to copy and a destination location and do
273   the verification and copying of those files to that location.  This function
274   will report any errors to the user and halt.
275 
276   The key is to have this function called ONLY once.  this allows for the parameter
277   verification to happen correctly.
278 
279   @param[in] FileList           A LIST_ENTRY* based list of files to move.
280   @param[in] DestDir            The destination location.
281   @param[in] SilentMode         TRUE to eliminate screen output.
282   @param[in] RecursiveMode      TRUE to copy directories.
283   @param[in] Resp               The response to the overwrite query (if always).
284 
285   @retval SHELL_SUCCESS             the files were all moved.
286   @retval SHELL_INVALID_PARAMETER   a parameter was invalid
287   @retval SHELL_SECURITY_VIOLATION  a security violation ocurred
288   @retval SHELL_WRITE_PROTECTED     the destination was write protected
289   @retval SHELL_OUT_OF_RESOURCES    a memory allocation failed
290 **/
291 SHELL_STATUS
ValidateAndCopyFiles(IN CONST EFI_SHELL_FILE_INFO * FileList,IN CONST CHAR16 * DestDir,IN BOOLEAN SilentMode,IN BOOLEAN RecursiveMode,IN VOID ** Resp)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
ProcessValidateAndCopyFiles(IN EFI_SHELL_FILE_INFO * FileList,IN CONST CHAR16 * DestDir,IN BOOLEAN SilentMode,IN BOOLEAN RecursiveMode)576 ProcessValidateAndCopyFiles(
577   IN       EFI_SHELL_FILE_INFO  *FileList,
578   IN CONST CHAR16               *DestDir,
579   IN BOOLEAN                    SilentMode,
580   IN BOOLEAN                    RecursiveMode
581   )
582 {
583   SHELL_STATUS        ShellStatus;
584   EFI_SHELL_FILE_INFO *List;
585   EFI_FILE_INFO       *FileInfo;
586   CHAR16              *FullName;
587 
588   List      = NULL;
589   FullName  = NULL;
590   FileInfo  = NULL;
591 
592   ShellOpenFileMetaArg((CHAR16*)DestDir, EFI_FILE_MODE_READ, &List);
593   if (List != NULL && List->Link.ForwardLink != List->Link.BackLink) {
594     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, L"cp", DestDir);
595     ShellStatus = SHELL_INVALID_PARAMETER;
596     ShellCloseFileMetaArg(&List);
597   } else if (List != NULL) {
598     ASSERT(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink) != NULL);
599     ASSERT(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->FullName != NULL);
600     FileInfo = gEfiShellProtocol->GetFileInfo(((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->Handle);
601     ASSERT(FileInfo != NULL);
602     StrnCatGrow(&FullName, NULL, ((EFI_SHELL_FILE_INFO *)List->Link.ForwardLink)->FullName, 0);
603     ShellCloseFileMetaArg(&List);
604     if ((FileInfo->Attribute & EFI_FILE_READ_ONLY) == 0) {
605       ShellStatus = ValidateAndCopyFiles(FileList, FullName, SilentMode, RecursiveMode, NULL);
606     } else {
607       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CP_DEST_ERROR), gShellLevel2HiiHandle, L"cp");
608       ShellStatus = SHELL_ACCESS_DENIED;
609     }
610   } else {
611     ShellCloseFileMetaArg(&List);
612     ShellStatus = ValidateAndCopyFiles(FileList, DestDir, SilentMode, RecursiveMode, NULL);
613   }
614 
615   SHELL_FREE_NON_NULL(FileInfo);
616   SHELL_FREE_NON_NULL(FullName);
617   return (ShellStatus);
618 }
619 
620 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
621   {L"-r", TypeFlag},
622   {L"-q", TypeFlag},
623   {NULL, TypeMax}
624   };
625 
626 /**
627   Function for 'cp' command.
628 
629   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
630   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
631 **/
632 SHELL_STATUS
633 EFIAPI
ShellCommandRunCp(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)634 ShellCommandRunCp (
635   IN EFI_HANDLE        ImageHandle,
636   IN EFI_SYSTEM_TABLE  *SystemTable
637   )
638 {
639   EFI_STATUS          Status;
640   LIST_ENTRY          *Package;
641   CHAR16              *ProblemParam;
642   SHELL_STATUS        ShellStatus;
643   UINTN               ParamCount;
644   UINTN               LoopCounter;
645   EFI_SHELL_FILE_INFO *FileList;
646   BOOLEAN             SilentMode;
647   BOOLEAN             RecursiveMode;
648   CONST CHAR16        *Cwd;
649   CHAR16              *FullCwd;
650 
651   ProblemParam        = NULL;
652   ShellStatus         = SHELL_SUCCESS;
653   ParamCount          = 0;
654   FileList            = NULL;
655 
656   //
657   // initialize the shell lib (we must be in non-auto-init...)
658   //
659   Status = ShellInitialize();
660   ASSERT_EFI_ERROR(Status);
661 
662   Status = CommandInit();
663   ASSERT_EFI_ERROR(Status);
664 
665   //
666   // parse the command line
667   //
668   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
669   if (EFI_ERROR(Status)) {
670     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
671       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"cp", ProblemParam);
672       FreePool(ProblemParam);
673       ShellStatus = SHELL_INVALID_PARAMETER;
674     } else {
675       ASSERT(FALSE);
676     }
677   } else {
678     //
679     // check for "-?"
680     //
681     if (ShellCommandLineGetFlag(Package, L"-?")) {
682       ASSERT(FALSE);
683     }
684 
685     //
686     // Initialize SilentMode and RecursiveMode
687     //
688     if (gEfiShellProtocol->BatchIsActive()) {
689       SilentMode = TRUE;
690     } else {
691       SilentMode = ShellCommandLineGetFlag(Package, L"-q");
692     }
693     RecursiveMode = ShellCommandLineGetFlag(Package, L"-r");
694 
695     switch (ParamCount = ShellCommandLineGetCount(Package)) {
696       case 0:
697       case 1:
698         //
699         // we have insufficient parameters
700         //
701         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"cp");
702         ShellStatus = SHELL_INVALID_PARAMETER;
703         break;
704       case 2:
705         //
706         // must have valid CWD for single parameter...
707         //
708         Cwd = ShellGetCurrentDir(NULL);
709         if (Cwd == NULL){
710           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cp");
711           ShellStatus = SHELL_INVALID_PARAMETER;
712         } else {
713           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
714           if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
715             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, 1));
716             ShellStatus = SHELL_NOT_FOUND;
717           } else  {
718             FullCwd = AllocateZeroPool(StrSize(Cwd) + sizeof(CHAR16));
719             if (FullCwd == NULL) {
720               ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellLevel2HiiHandle, L"cp");
721               ShellStatus = SHELL_OUT_OF_RESOURCES;
722             } else {
723               StrCpyS (FullCwd, StrSize (Cwd) / sizeof (CHAR16) + 1, Cwd);
724               ShellStatus = ProcessValidateAndCopyFiles (FileList, FullCwd, SilentMode, RecursiveMode);
725               FreePool (FullCwd);
726             }
727           }
728         }
729 
730         break;
731       default:
732         //
733         // Make a big list of all the files...
734         //
735         for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount && ShellStatus == SHELL_SUCCESS ; LoopCounter++) {
736           if (ShellGetExecutionBreakFlag()) {
737             break;
738           }
739           Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
740           if (EFI_ERROR(Status) || FileList == NULL || IsListEmpty(&FileList->Link)) {
741             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, LoopCounter));
742             ShellStatus = SHELL_NOT_FOUND;
743           }
744         }
745         if (ShellStatus != SHELL_SUCCESS) {
746           Status = ShellCloseFileMetaArg(&FileList);
747         } else {
748           //
749           // now copy them all...
750           //
751           if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
752             ShellStatus = ProcessValidateAndCopyFiles(FileList, PathCleanUpDirectories((CHAR16*)ShellCommandLineGetRawValue(Package, ParamCount)), SilentMode, RecursiveMode);
753             Status = ShellCloseFileMetaArg(&FileList);
754             if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
755               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, L"cp", ShellCommandLineGetRawValue(Package, ParamCount), ShellStatus|MAX_BIT);
756               ShellStatus = SHELL_ACCESS_DENIED;
757             }
758           }
759         }
760         break;
761     } // switch on parameter count
762 
763     if (FileList != NULL) {
764       ShellCloseFileMetaArg(&FileList);
765     }
766 
767     //
768     // free the command line package
769     //
770     ShellCommandLineFreeVarList (Package);
771   }
772 
773   if (ShellGetExecutionBreakFlag()) {
774     return (SHELL_ABORTED);
775   }
776 
777   return (ShellStatus);
778 }
779 
780