2 Main file for mv shell level 2 function.
4 (C) Copyright 2013-2014, Hewlett-Packard Development Company, L.P.
5 Copyright (c) 2009 - 2014, 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
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.
16 #include "UefiShellLevel2CommandsLib.h"
19 Function to validate that moving a specific file (FileName) to a specific
20 location (DestPath) is valid.
22 This function will verify that the destination is not a subdirectory of
23 FullName, that the Current working Directory is not being moved, and that
24 the directory is not read only.
26 if the move is invalid this function will report the error to StdOut.
28 @param FullName [in] The name of the file to move.
29 @param Cwd [in] The current working directory
30 @param DestPath [in] The target location to move to
31 @param Attribute[in] The Attribute of the file
32 @param FileStatus[in] The Status of the file when opened
34 @retval TRUE The move is valid
35 @retval FALSE The move is not
40 IN CONST CHAR16
*FullName
,
42 IN CONST CHAR16
*DestPath
,
43 IN CONST UINT64 Attribute
,
44 IN CONST EFI_STATUS FileStatus
52 if (Cwd
!= NULL
&& StrCmp(FullName
, Cwd
) == 0) {
56 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_MV_INV_CWD
), gShellLevel2HiiHandle
);
60 Test
= StrnCatGrow(&Test
, NULL
, DestPath
, 0);
62 ASSERT(TestWalker
!= NULL
);
63 while(*TestWalker
== L
'\\') {
66 while(TestWalker
!= NULL
&& TestWalker
[StrLen(TestWalker
)-1] == L
'\\') {
67 TestWalker
[StrLen(TestWalker
)-1] = CHAR_NULL
;
69 ASSERT(TestWalker
!= NULL
);
70 ASSERT(FullName
!= NULL
);
71 if (StrStr(FullName
, TestWalker
) != 0) {
72 TempLen
= StrLen(FullName
);
73 if (StrStr(FullName
, TestWalker
) != FullName
// not the first items... (could below it)
74 && TempLen
<= (StrLen(TestWalker
) + 1)
75 && StrStr(FullName
+StrLen(TestWalker
) + 1, L
"\\") == NULL
) {
79 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_MV_INV_SUB
), gShellLevel2HiiHandle
);
85 if (StrStr(DestPath
, FullName
) != 0 && StrStr(DestPath
, FullName
) != DestPath
) {
89 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_MV_INV_SUB
), gShellLevel2HiiHandle
);
92 if (((Attribute
& EFI_FILE_READ_ONLY
) != 0) || (FileStatus
== EFI_WRITE_PROTECTED
)) {
94 // invalid to move read only
96 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_MV_INV_RO
), gShellLevel2HiiHandle
, FullName
);
99 Test
= StrStr(FullName
, L
":");
100 Test1
= StrStr(DestPath
, L
":");
101 if (Test1
!= NULL
&& Test
!= NULL
) {
104 Result
= StringNoCaseCompare(&FullName
, &DestPath
);
108 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_MV_INV_FS
), gShellLevel2HiiHandle
);
116 Function to take a destination path that might contain wildcards and verify
117 that there is only a single possible target (IE we cant have wildcards that
118 have 2 possible destination).
120 if the result is sucessful the caller must free *DestPathPointer.
122 @param[in] DestDir The original path to the destination.
123 @param[in, out] DestPathPointer A pointer to the callee allocated final path.
124 @param[in] Cwd A pointer to the current working directory.
126 @retval SHELL_INVALID_PARAMETER The DestDir could not be resolved to a location.
127 @retval SHELL_INVALID_PARAMETER The DestDir could be resolved to more than 1 location.
128 @retval SHELL_INVALID_PARAMETER Cwd is required and is NULL.
129 @retval SHELL_SUCCESS The operation was sucessful.
133 GetDestinationLocation(
134 IN CONST CHAR16
*DestDir
,
135 IN OUT CHAR16
**DestPathPointer
,
139 EFI_SHELL_FILE_INFO
*DestList
;
140 EFI_SHELL_FILE_INFO
*Node
;
148 if (StrStr(DestDir
, L
"\\") == DestDir
) {
150 return SHELL_INVALID_PARAMETER
;
152 DestPath
= AllocateZeroPool(StrSize(Cwd
));
153 if (DestPath
== NULL
) {
154 return (SHELL_OUT_OF_RESOURCES
);
156 StrCpy(DestPath
, Cwd
);
157 while (PathRemoveLastItem(DestPath
)) ;
160 // Append DestDir beyond '\' which may be present
162 CurrentSize
= StrSize(DestPath
);
163 StrnCatGrow(&DestPath
, &CurrentSize
, &DestDir
[1], 0);
165 *DestPathPointer
= DestPath
;
166 return (SHELL_SUCCESS
);
169 // get the destination path
171 ShellOpenFileMetaArg((CHAR16
*)DestDir
, EFI_FILE_MODE_WRITE
|EFI_FILE_MODE_READ
|EFI_FILE_MODE_CREATE
, &DestList
);
172 if (DestList
== NULL
|| IsListEmpty(&DestList
->Link
)) {
174 // Not existing... must be renaming
176 if (StrStr(DestDir
, L
":") == NULL
) {
178 ShellCloseFileMetaArg(&DestList
);
179 return (SHELL_INVALID_PARAMETER
);
181 NewSize
= StrSize(Cwd
);
182 NewSize
+= StrSize(DestDir
);
183 DestPath
= AllocateZeroPool(NewSize
);
184 if (DestPath
== NULL
) {
185 ShellCloseFileMetaArg(&DestList
);
186 return (SHELL_OUT_OF_RESOURCES
);
188 StrCpy(DestPath
, Cwd
);
189 if (DestPath
[StrLen(DestPath
)-1] != L
'\\' && DestDir
[0] != L
'\\') {
190 StrCat(DestPath
, L
"\\");
191 } else if (DestPath
[StrLen(DestPath
)-1] == L
'\\' && DestDir
[0] == L
'\\') {
192 ((CHAR16
*)DestPath
)[StrLen(DestPath
)-1] = CHAR_NULL
;
194 StrCat(DestPath
, DestDir
);
196 ASSERT(DestPath
== NULL
);
197 DestPath
= StrnCatGrow(&DestPath
, NULL
, DestDir
, 0);
198 if (DestPath
== NULL
) {
199 ShellCloseFileMetaArg(&DestList
);
200 return (SHELL_OUT_OF_RESOURCES
);
204 Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&DestList
->Link
);
206 // Make sure there is only 1 node in the list.
208 if (!IsNodeAtEnd(&DestList
->Link
, &Node
->Link
)) {
209 ShellCloseFileMetaArg(&DestList
);
210 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_MARG_ERROR
), gShellLevel2HiiHandle
, DestDir
);
211 return (SHELL_INVALID_PARAMETER
);
213 if (ShellIsDirectory(Node
->FullName
)==EFI_SUCCESS
) {
214 DestPath
= AllocateZeroPool(StrSize(Node
->FullName
)+sizeof(CHAR16
));
215 if (DestPath
== NULL
) {
216 ShellCloseFileMetaArg(&DestList
);
217 return (SHELL_OUT_OF_RESOURCES
);
219 StrCpy(DestPath
, Node
->FullName
);
220 StrCat(DestPath
, L
"\\");
223 // cant move onto another file.
225 ShellCloseFileMetaArg(&DestList
);
226 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_FILE_ERROR
), gShellLevel2HiiHandle
, DestDir
);
227 return (SHELL_INVALID_PARAMETER
);
231 *DestPathPointer
= DestPath
;
232 ShellCloseFileMetaArg(&DestList
);
234 return (SHELL_SUCCESS
);
238 function to take a list of files to move and a destination location and do
239 the verification and moving of those files to that location. This function
240 will report any errors to the user and continue to move the rest of the files.
242 @param[in] FileList A LIST_ENTRY* based list of files to move
243 @param[out] Resp pointer to response from question. Pass back on looped calling
244 @param[in] DestDir the destination location
246 @retval SHELL_SUCCESS the files were all moved.
247 @retval SHELL_INVALID_PARAMETER a parameter was invalid
248 @retval SHELL_SECURITY_VIOLATION a security violation ocurred
249 @retval SHELL_WRITE_PROTECTED the destination was write protected
250 @retval SHELL_OUT_OF_RESOURCES a memory allocation failed
254 ValidateAndMoveFiles(
255 IN CONST EFI_SHELL_FILE_INFO
*FileList
,
257 IN CONST CHAR16
*DestDir
265 SHELL_STATUS ShellStatus
;
266 CONST EFI_SHELL_FILE_INFO
*Node
;
267 EFI_FILE_INFO
*NewFileInfo
;
268 CHAR16
*TempLocation
;
272 SHELL_FILE_HANDLE DestHandle
;
273 CHAR16
*CleanFilePathStr
;
275 ASSERT(FileList
!= NULL
);
276 ASSERT(DestDir
!= NULL
);
279 Cwd
= ShellGetCurrentDir(NULL
);
281 CleanFilePathStr
= NULL
;
283 Status
= ShellLevel2StripQuotes (DestDir
, &CleanFilePathStr
);
284 if (EFI_ERROR (Status
)) {
285 if (Status
== EFI_OUT_OF_RESOURCES
) {
286 return SHELL_OUT_OF_RESOURCES
;
288 return SHELL_INVALID_PARAMETER
;
293 // Get and validate the destination location
295 ShellStatus
= GetDestinationLocation(CleanFilePathStr
, &DestPath
, Cwd
);
296 FreePool (CleanFilePathStr
);
297 if (ShellStatus
!= SHELL_SUCCESS
) {
298 return (ShellStatus
);
300 DestPath
= PathCleanUpDirectories(DestPath
);
302 HiiOutput
= HiiGetString (gShellLevel2HiiHandle
, STRING_TOKEN (STR_MV_OUTPUT
), NULL
);
303 HiiResultOk
= HiiGetString (gShellLevel2HiiHandle
, STRING_TOKEN (STR_GEN_RES_OK
), NULL
);
304 ASSERT (DestPath
!= NULL
);
305 ASSERT (HiiResultOk
!= NULL
);
306 ASSERT (HiiOutput
!= NULL
);
309 // Go through the list of files and directories to move...
311 for (Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&FileList
->Link
)
312 ; !IsNull(&FileList
->Link
, &Node
->Link
)
313 ; Node
= (EFI_SHELL_FILE_INFO
*)GetNextNode(&FileList
->Link
, &Node
->Link
)
315 if (ShellGetExecutionBreakFlag()) {
320 // These should never be NULL
322 ASSERT(Node
->FileName
!= NULL
);
323 ASSERT(Node
->FullName
!= NULL
);
324 ASSERT(Node
->Info
!= NULL
);
327 // skip the directory traversing stuff...
329 if (StrCmp(Node
->FileName
, L
".") == 0 || StrCmp(Node
->FileName
, L
"..") == 0) {
334 // Validate that the move is valid
336 if (!IsValidMove(Node
->FullName
, Cwd
, DestPath
, Node
->Info
->Attribute
, Node
->Status
)) {
337 ShellStatus
= SHELL_INVALID_PARAMETER
;
342 // Chop off map info from "DestPath"
344 if ((TempLocation
= StrStr(DestPath
, L
":")) != NULL
) {
345 CopyMem(DestPath
, TempLocation
+1, StrSize(TempLocation
+1));
349 // construct the new file info block
351 NewSize
= StrSize(DestPath
);
352 NewSize
+= StrSize(Node
->FileName
) + SIZE_OF_EFI_FILE_INFO
+ sizeof(CHAR16
);
353 NewFileInfo
= AllocateZeroPool(NewSize
);
354 if (NewFileInfo
== NULL
) {
355 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_NO_MEM
), gShellLevel2HiiHandle
);
356 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
358 CopyMem(NewFileInfo
, Node
->Info
, SIZE_OF_EFI_FILE_INFO
);
359 if (DestPath
[0] != L
'\\') {
360 StrCpy(NewFileInfo
->FileName
, L
"\\");
361 StrCat(NewFileInfo
->FileName
, DestPath
);
363 StrCpy(NewFileInfo
->FileName
, DestPath
);
365 Length
= StrLen(NewFileInfo
->FileName
);
369 if (NewFileInfo
->FileName
[Length
] == L
'\\') {
370 if (Node
->FileName
[0] == L
'\\') {
372 // Don't allow for double slashes. Eliminate one of them.
374 NewFileInfo
->FileName
[Length
] = CHAR_NULL
;
376 StrCat(NewFileInfo
->FileName
, Node
->FileName
);
378 NewFileInfo
->Size
= SIZE_OF_EFI_FILE_INFO
+ StrSize(NewFileInfo
->FileName
);
379 ShellPrintEx(-1, -1, HiiOutput
, Node
->FullName
, NewFileInfo
->FileName
);
381 if (!EFI_ERROR(ShellFileExists(NewFileInfo
->FileName
))) {
382 if (Response
== NULL
) {
383 ShellPromptForResponseHii(ShellPromptResponseTypeYesNoAllCancel
, STRING_TOKEN (STR_GEN_DEST_EXIST_OVR
), gShellLevel2HiiHandle
, &Response
);
385 switch (*(SHELL_PROMPT_RESPONSE
*)Response
) {
386 case ShellPromptResponseNo
:
387 FreePool(NewFileInfo
);
389 case ShellPromptResponseCancel
:
392 // indicate to stop everything
394 FreePool(NewFileInfo
);
397 FreePool(HiiResultOk
);
398 return (SHELL_ABORTED
);
399 case ShellPromptResponseAll
:
402 case ShellPromptResponseYes
:
407 FreePool(NewFileInfo
);
410 FreePool(HiiResultOk
);
411 return SHELL_ABORTED
;
413 Status
= ShellOpenFileByName(NewFileInfo
->FileName
, &DestHandle
, EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0);
414 ShellDeleteFile(&DestHandle
);
419 // Perform the move operation
421 Status
= ShellSetFileInfo(Node
->Handle
, NewFileInfo
);
424 // Free the info object we used...
426 FreePool(NewFileInfo
);
431 if (EFI_ERROR(Status
)) {
432 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_ERR_UK
), gShellLevel2HiiHandle
, Status
);
433 ShellStatus
= SHELL_INVALID_PARAMETER
;
434 if (Status
== EFI_SECURITY_VIOLATION
) {
435 ShellStatus
= SHELL_SECURITY_VIOLATION
;
436 } else if (Status
== EFI_WRITE_PROTECTED
) {
437 ShellStatus
= SHELL_WRITE_PROTECTED
;
438 } else if (Status
== EFI_OUT_OF_RESOURCES
) {
439 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
440 } else if (Status
== EFI_DEVICE_ERROR
) {
441 ShellStatus
= SHELL_DEVICE_ERROR
;
442 } else if (Status
== EFI_ACCESS_DENIED
) {
443 ShellStatus
= SHELL_ACCESS_DENIED
;
446 ShellPrintEx(-1, -1, L
"%s", HiiResultOk
);
453 FreePool(HiiResultOk
);
454 return (ShellStatus
);
458 Function for 'mv' command.
460 @param[in] ImageHandle Handle to the Image (NULL if Internal).
461 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
466 IN EFI_HANDLE ImageHandle
,
467 IN EFI_SYSTEM_TABLE
*SystemTable
472 CHAR16
*ProblemParam
;
473 SHELL_STATUS ShellStatus
;
476 EFI_SHELL_FILE_INFO
*FileList
;
480 ShellStatus
= SHELL_SUCCESS
;
486 // initialize the shell lib (we must be in non-auto-init...)
488 Status
= ShellInitialize();
489 ASSERT_EFI_ERROR(Status
);
492 // parse the command line
494 Status
= ShellCommandLineParse (EmptyParamList
, &Package
, &ProblemParam
, TRUE
);
495 if (EFI_ERROR(Status
)) {
496 if (Status
== EFI_VOLUME_CORRUPTED
&& ProblemParam
!= NULL
) {
497 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), gShellLevel2HiiHandle
, ProblemParam
);
498 FreePool(ProblemParam
);
499 ShellStatus
= SHELL_INVALID_PARAMETER
;
507 if (ShellCommandLineGetFlag(Package
, L
"-?")) {
511 switch (ParamCount
= ShellCommandLineGetCount(Package
)) {
515 // we have insufficient parameters
517 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_FEW
), gShellLevel2HiiHandle
);
518 ShellStatus
= SHELL_INVALID_PARAMETER
;
522 // must have valid CWD for single parameter...
524 if (ShellGetCurrentDir(NULL
) == NULL
){
525 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_NO_CWD
), gShellLevel2HiiHandle
);
526 ShellStatus
= SHELL_INVALID_PARAMETER
;
528 Status
= ShellOpenFileMetaArg((CHAR16
*)ShellCommandLineGetRawValue(Package
, 1), EFI_FILE_MODE_WRITE
|EFI_FILE_MODE_READ
, &FileList
);
529 if (FileList
== NULL
|| IsListEmpty(&FileList
->Link
) || EFI_ERROR(Status
)) {
530 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_FILE_NF
), gShellLevel2HiiHandle
, ShellCommandLineGetRawValue(Package
, 1));
531 ShellStatus
= SHELL_NOT_FOUND
;
534 // ValidateAndMoveFiles will report errors to the screen itself
536 ShellStatus
= ValidateAndMoveFiles(FileList
, &Response
, ShellGetCurrentDir(NULL
));
542 ///@todo make sure this works with error half way through and continues...
543 for (ParamCount
--, LoopCounter
= 1 ; LoopCounter
< ParamCount
; LoopCounter
++) {
544 if (ShellGetExecutionBreakFlag()) {
547 Status
= ShellOpenFileMetaArg((CHAR16
*)ShellCommandLineGetRawValue(Package
, LoopCounter
), EFI_FILE_MODE_WRITE
|EFI_FILE_MODE_READ
, &FileList
);
548 if (FileList
== NULL
|| IsListEmpty(&FileList
->Link
) || EFI_ERROR(Status
)) {
549 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_FILE_NF
), gShellLevel2HiiHandle
, ShellCommandLineGetRawValue(Package
, LoopCounter
));
550 ShellStatus
= SHELL_NOT_FOUND
;
553 // ValidateAndMoveFiles will report errors to the screen itself
554 // Only change ShellStatus if it's sucessful
556 if (ShellStatus
== SHELL_SUCCESS
) {
557 ShellStatus
= ValidateAndMoveFiles(FileList
, &Response
, ShellCommandLineGetRawValue(Package
, ParamCount
));
559 ValidateAndMoveFiles(FileList
, &Response
, ShellCommandLineGetRawValue(Package
, ParamCount
));
562 if (FileList
!= NULL
&& !IsListEmpty(&FileList
->Link
)) {
563 Status
= ShellCloseFileMetaArg(&FileList
);
564 if (EFI_ERROR(Status
) && ShellStatus
== SHELL_SUCCESS
) {
565 ShellStatus
= SHELL_ACCESS_DENIED
;
566 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_ERR_FILE
), gShellLevel2HiiHandle
, ShellCommandLineGetRawValue(Package
, 1), ShellStatus
|MAX_BIT
);
571 } // switch on parameter count
573 if (FileList
!= NULL
) {
574 ShellCloseFileMetaArg(&FileList
);
578 // free the command line package
580 ShellCommandLineFreeVarList (Package
);
583 SHELL_FREE_NON_NULL(Response
);
585 if (ShellGetExecutionBreakFlag()) {
586 return (SHELL_ABORTED
);
589 return (ShellStatus
);