1 /** @file
2 Main file for mv shell level 2 function.
3
4 (C) Copyright 2013-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
12 /**
13 function to determine if a move is between file systems.
14
15 @param FullName [in] The name of the file to move.
16 @param Cwd [in] The current working directory
17 @param DestPath [in] The target location to move to
18
19 @retval TRUE The move is across file system.
20 @retval FALSE The move is within a file system.
21 **/
22 BOOLEAN
IsBetweenFileSystem(IN CONST CHAR16 * FullName,IN CONST CHAR16 * Cwd,IN CONST CHAR16 * DestPath)23 IsBetweenFileSystem(
24 IN CONST CHAR16 *FullName,
25 IN CONST CHAR16 *Cwd,
26 IN CONST CHAR16 *DestPath
27 )
28 {
29 CHAR16 *Test;
30 CHAR16 *Test1;
31 UINTN Result;
32
33 Test = StrStr(FullName, L":");
34 if (Test == NULL && Cwd != NULL) {
35 Test = StrStr(Cwd, L":");
36 }
37 Test1 = StrStr(DestPath, L":");
38 if (Test1 == NULL && Cwd != NULL) {
39 Test1 = StrStr(Cwd, L":");
40 }
41 if (Test1 != NULL && Test != NULL) {
42 *Test = CHAR_NULL;
43 *Test1 = CHAR_NULL;
44 Result = StringNoCaseCompare(&FullName, &DestPath);
45 *Test = L':';
46 *Test1 = L':';
47 if (Result != 0) {
48 return (TRUE);
49 }
50 }
51 return (FALSE);
52 }
53
54 /**
55 function to determine if SrcPath is valid to mv.
56
57 if SrcPath equal CWD then it's invalid.
58 if SrcPath is the parent path of CWD then it's invalid.
59 is SrcPath is NULL return FALSE.
60
61 if CwdPath is NULL then ASSERT()
62
63 @param SrcPath [in] The source path.
64 @param CwdPath [in] The current working directory.
65
66 @retval TRUE The source path is valid.
67 @retval FALSE The source path is invalid.
68 **/
69 BOOLEAN
IsSoucePathValid(IN CONST CHAR16 * SrcPath,IN CONST CHAR16 * CwdPath)70 IsSoucePathValid(
71 IN CONST CHAR16* SrcPath,
72 IN CONST CHAR16* CwdPath
73 )
74 {
75 CHAR16* SrcPathBuffer;
76 CHAR16* CwdPathBuffer;
77 BOOLEAN Ret;
78
79 ASSERT (CwdPath != NULL);
80 if (SrcPath == NULL) {
81 return FALSE;
82 }
83
84 Ret = TRUE;
85
86 SrcPathBuffer = AllocateCopyPool (StrSize (SrcPath), SrcPath);
87 if (SrcPathBuffer == NULL) {
88 return FALSE;
89 }
90
91 CwdPathBuffer = AllocateCopyPool (StrSize (CwdPath), CwdPath);
92 if (CwdPathBuffer == NULL) {
93 FreePool(SrcPathBuffer);
94 return FALSE;
95 }
96
97 gUnicodeCollation->StrUpr (gUnicodeCollation, SrcPathBuffer);
98 gUnicodeCollation->StrUpr (gUnicodeCollation, CwdPathBuffer);
99
100 if (SrcPathBuffer[StrLen (SrcPathBuffer) -1 ] == L'\\') {
101 SrcPathBuffer[StrLen (SrcPathBuffer) - 1] = CHAR_NULL;
102 }
103
104 if (CwdPathBuffer[StrLen (CwdPathBuffer) - 1] == L'\\') {
105 CwdPathBuffer[StrLen (CwdPathBuffer) - 1] = CHAR_NULL;
106 }
107
108 if (StrCmp (CwdPathBuffer, SrcPathBuffer) == 0 ||
109 ((StrStr (CwdPathBuffer, SrcPathBuffer) == CwdPathBuffer) &&
110 (CwdPathBuffer[StrLen (SrcPathBuffer)] == L'\\'))
111 ) {
112 Ret = FALSE;
113 }
114
115 FreePool (SrcPathBuffer);
116 FreePool (CwdPathBuffer);
117
118 return Ret;
119 }
120
121 /**
122 Function to validate that moving a specific file (FileName) to a specific
123 location (DestPath) is valid.
124
125 This function will verify that the destination is not a subdirectory of
126 FullName, that the Current working Directory is not being moved, and that
127 the directory is not read only.
128
129 if the move is invalid this function will report the error to StdOut.
130
131 @param SourcePath [in] The name of the file to move.
132 @param Cwd [in] The current working directory
133 @param DestPath [in] The target location to move to
134 @param Attribute [in] The Attribute of the file
135 @param DestAttr [in] The Attribute of the destination
136 @param FileStatus [in] The Status of the file when opened
137
138 @retval TRUE The move is valid
139 @retval FALSE The move is not
140 **/
141 BOOLEAN
IsValidMove(IN CONST CHAR16 * SourcePath,IN CONST CHAR16 * Cwd,IN CONST CHAR16 * DestPath,IN CONST UINT64 Attribute,IN CONST UINT64 DestAttr,IN CONST EFI_STATUS FileStatus)142 IsValidMove(
143 IN CONST CHAR16 *SourcePath,
144 IN CONST CHAR16 *Cwd,
145 IN CONST CHAR16 *DestPath,
146 IN CONST UINT64 Attribute,
147 IN CONST UINT64 DestAttr,
148 IN CONST EFI_STATUS FileStatus
149 )
150 {
151 CHAR16 *DestPathCopy;
152 CHAR16 *DestPathWalker;
153
154 if ((Cwd != NULL) && ((Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)) {
155 if (!IsSoucePathValid (SourcePath, Cwd)) {
156 //
157 // Invalid move
158 //
159 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_MV_INV_CWD), gShellLevel2HiiHandle);
160 return FALSE;
161 }
162 }
163
164 //
165 // invalid to move read only or move to a read only destination
166 //
167 if (((Attribute & EFI_FILE_READ_ONLY) != 0)
168 || (FileStatus == EFI_WRITE_PROTECTED)
169 || ((DestAttr & EFI_FILE_READ_ONLY) != 0)
170 ) {
171 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_RO), gShellLevel2HiiHandle, SourcePath);
172 return (FALSE);
173 }
174
175 DestPathCopy = AllocateCopyPool(StrSize(DestPath), DestPath);
176 if (DestPathCopy == NULL) {
177 return (FALSE);
178 }
179
180 for (DestPathWalker = DestPathCopy; *DestPathWalker == L'\\'; DestPathWalker++) ;
181
182 while(DestPathWalker != NULL && DestPathWalker[StrLen(DestPathWalker)-1] == L'\\') {
183 DestPathWalker[StrLen(DestPathWalker)-1] = CHAR_NULL;
184 }
185
186 ASSERT(DestPathWalker != NULL);
187 ASSERT(SourcePath != NULL);
188
189 //
190 // If they're the same, or if source is "above" dest on file path tree
191 //
192 if ( StringNoCaseCompare (&DestPathWalker, &SourcePath) == 0 ||
193 ((StrStr(DestPathWalker, SourcePath) == DestPathWalker) &&
194 (DestPathWalker[StrLen(SourcePath)] == '\\')
195 )
196 ) {
197 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);
198 FreePool(DestPathCopy);
199 return (FALSE);
200 }
201 FreePool(DestPathCopy);
202
203 return (TRUE);
204 }
205
206 /**
207 Function to take a destination path that might contain wildcards and verify
208 that there is only a single possible target (IE we cant have wildcards that
209 have 2 possible destination).
210
211 if the result is sucessful the caller must free *DestPathPointer.
212
213 @param[in] DestParameter The original path to the destination.
214 @param[in, out] DestPathPointer A pointer to the callee allocated final path.
215 @param[in] Cwd A pointer to the current working directory.
216 @param[in] SingleSource TRUE to have only one source file.
217 @param[in, out] DestAttr A pointer to the destination information attribute.
218
219 @retval SHELL_INVALID_PARAMETER The DestParameter could not be resolved to a location.
220 @retval SHELL_INVALID_PARAMETER The DestParameter could be resolved to more than 1 location.
221 @retval SHELL_INVALID_PARAMETER Cwd is required and is NULL.
222 @retval SHELL_SUCCESS The operation was sucessful.
223 **/
224 SHELL_STATUS
GetDestinationLocation(IN CONST CHAR16 * DestParameter,IN OUT CHAR16 ** DestPathPointer,IN CONST CHAR16 * Cwd,IN CONST BOOLEAN SingleSource,IN OUT UINT64 * DestAttr)225 GetDestinationLocation(
226 IN CONST CHAR16 *DestParameter,
227 IN OUT CHAR16 **DestPathPointer,
228 IN CONST CHAR16 *Cwd,
229 IN CONST BOOLEAN SingleSource,
230 IN OUT UINT64 *DestAttr
231 )
232 {
233 EFI_SHELL_FILE_INFO *DestList;
234 EFI_SHELL_FILE_INFO *Node;
235 CHAR16 *DestPath;
236 UINTN NewSize;
237 UINTN CurrentSize;
238
239 DestList = NULL;
240 DestPath = NULL;
241
242 ASSERT(DestAttr != NULL);
243
244 if (StrStr(DestParameter, L"\\") == DestParameter) {
245 if (Cwd == NULL) {
246 return SHELL_INVALID_PARAMETER;
247 }
248 DestPath = AllocateZeroPool(StrSize(Cwd));
249 if (DestPath == NULL) {
250 return (SHELL_OUT_OF_RESOURCES);
251 }
252 StrCpyS(DestPath, StrSize(Cwd) / sizeof(CHAR16), Cwd);
253 while (PathRemoveLastItem(DestPath)) ;
254
255 //
256 // Append DestParameter beyond '\' which may be present
257 //
258 CurrentSize = StrSize(DestPath);
259 StrnCatGrow(&DestPath, &CurrentSize, &DestParameter[1], 0);
260
261 *DestPathPointer = DestPath;
262 return (SHELL_SUCCESS);
263 }
264 //
265 // get the destination path
266 //
267 ShellOpenFileMetaArg((CHAR16*)DestParameter, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, &DestList);
268 if (DestList == NULL || IsListEmpty(&DestList->Link)) {
269 //
270 // Not existing... must be renaming
271 //
272 if (StrStr(DestParameter, L":") == NULL) {
273 if (Cwd == NULL) {
274 ShellCloseFileMetaArg(&DestList);
275 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
276 return (SHELL_INVALID_PARAMETER);
277 }
278 NewSize = StrSize(Cwd);
279 NewSize += StrSize(DestParameter);
280 DestPath = AllocateZeroPool(NewSize);
281 if (DestPath == NULL) {
282 ShellCloseFileMetaArg(&DestList);
283 return (SHELL_OUT_OF_RESOURCES);
284 }
285 StrCpyS(DestPath, NewSize / sizeof(CHAR16), Cwd);
286 if (DestPath[StrLen(DestPath)-1] != L'\\' && DestParameter[0] != L'\\') {
287 StrCatS(DestPath, NewSize / sizeof(CHAR16), L"\\");
288 } else if (DestPath[StrLen(DestPath)-1] == L'\\' && DestParameter[0] == L'\\') {
289 ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
290 }
291 StrCatS(DestPath, NewSize / sizeof(CHAR16), DestParameter);
292 } else {
293 ASSERT(DestPath == NULL);
294 DestPath = StrnCatGrow(&DestPath, NULL, DestParameter, 0);
295 if (DestPath == NULL) {
296 ShellCloseFileMetaArg(&DestList);
297 return (SHELL_OUT_OF_RESOURCES);
298 }
299 }
300 } else {
301 Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&DestList->Link);
302 *DestAttr = Node->Info->Attribute;
303 //
304 // Make sure there is only 1 node in the list.
305 //
306 if (!IsNodeAtEnd(&DestList->Link, &Node->Link)) {
307 ShellCloseFileMetaArg(&DestList);
308 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, L"mv", DestParameter);
309 return (SHELL_INVALID_PARAMETER);
310 }
311
312 //
313 // If we are a directory or a single file, then one node is fine.
314 //
315 if (ShellIsDirectory(Node->FullName)==EFI_SUCCESS || SingleSource) {
316 DestPath = AllocateZeroPool(StrSize(Node->FullName)+sizeof(CHAR16));
317 if (DestPath == NULL) {
318 ShellCloseFileMetaArg(&DestList);
319 return (SHELL_OUT_OF_RESOURCES);
320 }
321 StrCpyS(DestPath, (StrSize(Node->FullName)+sizeof(CHAR16)) / sizeof(CHAR16), Node->FullName);
322 StrCatS(DestPath, (StrSize(Node->FullName)+sizeof(CHAR16)) / sizeof(CHAR16), L"\\");
323 } else {
324 //
325 // cant move multiple files onto a single file.
326 //
327 ShellCloseFileMetaArg(&DestList);
328 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_ERROR), gShellLevel2HiiHandle, L"mv", DestParameter);
329 return (SHELL_INVALID_PARAMETER);
330 }
331 }
332
333 *DestPathPointer = DestPath;
334 ShellCloseFileMetaArg(&DestList);
335
336 return (SHELL_SUCCESS);
337 }
338
339 /**
340 Function to do a move across file systems.
341
342 @param[in] Node A pointer to the file to be removed.
343 @param[in] DestPath A pointer to the destination file path.
344 @param[out] Resp A pointer to response from question. Pass back on looped calling
345
346 @retval SHELL_SUCCESS The source file was moved to the destination.
347 **/
348 EFI_STATUS
MoveBetweenFileSystems(IN EFI_SHELL_FILE_INFO * Node,IN CONST CHAR16 * DestPath,OUT VOID ** Resp)349 MoveBetweenFileSystems(
350 IN EFI_SHELL_FILE_INFO *Node,
351 IN CONST CHAR16 *DestPath,
352 OUT VOID **Resp
353 )
354 {
355 SHELL_STATUS ShellStatus;
356
357 //
358 // First we copy the file
359 //
360 ShellStatus = CopySingleFile (Node->FullName, DestPath, Resp, TRUE, L"mv");
361
362 //
363 // Check our result
364 //
365 if (ShellStatus == SHELL_SUCCESS) {
366 //
367 // The copy was successful. delete the source file.
368 //
369 CascadeDelete(Node, TRUE);
370 Node->Handle = NULL;
371 } else if (ShellStatus == SHELL_ABORTED) {
372 return EFI_ABORTED;
373 } else if (ShellStatus == SHELL_ACCESS_DENIED) {
374 return EFI_ACCESS_DENIED;
375 } else if (ShellStatus == SHELL_VOLUME_FULL) {
376 return EFI_VOLUME_FULL;
377 } else {
378 return EFI_UNSUPPORTED;
379 }
380
381 return (EFI_SUCCESS);
382 }
383
384 /**
385 Function to take the destination path and target file name to generate the full destination path.
386
387 @param[in] DestPath A pointer to the destination file path string.
388 @param[out] FullDestPath A pointer to the full destination path string.
389 @param[in] FileName Name string of the targe file.
390
391 @retval SHELL_SUCCESS the files were all moved.
392 @retval SHELL_INVALID_PARAMETER a parameter was invalid
393 @retval SHELL_OUT_OF_RESOURCES a memory allocation failed
394 **/
395 EFI_STATUS
CreateFullDestPath(IN CONST CHAR16 ** DestPath,OUT CHAR16 ** FullDestPath,IN CONST CHAR16 * FileName)396 CreateFullDestPath(
397 IN CONST CHAR16 **DestPath,
398 OUT CHAR16 **FullDestPath,
399 IN CONST CHAR16 *FileName
400 )
401 {
402 UINTN Size;
403 if (FullDestPath == NULL || FileName == NULL || DestPath == NULL || *DestPath == NULL){
404 return (EFI_INVALID_PARAMETER);
405 }
406
407 Size = StrSize(*DestPath) + StrSize(FileName);
408
409 *FullDestPath = AllocateZeroPool(Size);
410 if (*FullDestPath == NULL){
411 return (EFI_OUT_OF_RESOURCES);
412 }
413
414 StrCpyS(*FullDestPath, Size / sizeof(CHAR16), *DestPath);
415 if ((*FullDestPath)[StrLen(*FullDestPath)-1] != L'\\' && FileName[0] != L'\\') {
416 StrCatS(*FullDestPath, Size / sizeof(CHAR16), L"\\");
417 }
418 StrCatS(*FullDestPath, Size / sizeof(CHAR16), FileName);
419
420 return (EFI_SUCCESS);
421 }
422
423 /**
424 Function to do a move within a file system.
425
426 @param[in] Node A pointer to the file to be removed.
427 @param[in] DestPath A pointer to the destination file path.
428 @param[out] Resp A pointer to response from question. Pass back on looped calling.
429
430 @retval SHELL_SUCCESS The source file was moved to the destination.
431 @retval SHELL_OUT_OF_RESOURCES A memory allocation failed.
432 **/
433 EFI_STATUS
MoveWithinFileSystems(IN EFI_SHELL_FILE_INFO * Node,IN CHAR16 * DestPath,OUT VOID ** Resp)434 MoveWithinFileSystems(
435 IN EFI_SHELL_FILE_INFO *Node,
436 IN CHAR16 *DestPath,
437 OUT VOID **Resp
438 )
439 {
440 EFI_FILE_INFO *NewFileInfo;
441 CHAR16 *TempLocation;
442 UINTN NewSize;
443 UINTN Length;
444 EFI_STATUS Status;
445
446 //
447 // Chop off map info from DestPath
448 //
449 if ((TempLocation = StrStr(DestPath, L":")) != NULL) {
450 CopyMem(DestPath, TempLocation+1, StrSize(TempLocation+1));
451 }
452
453 //
454 // construct the new file info block
455 //
456 NewSize = StrSize(DestPath);
457 NewSize += StrSize(Node->FileName) + SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
458 NewFileInfo = AllocateZeroPool(NewSize);
459 if (NewFileInfo == NULL) {
460 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle);
461 Status = EFI_OUT_OF_RESOURCES;
462 } else {
463 CopyMem(NewFileInfo, Node->Info, SIZE_OF_EFI_FILE_INFO);
464 if (DestPath[0] != L'\\') {
465 StrCpyS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), L"\\");
466 StrCatS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), DestPath);
467 } else {
468 StrCpyS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), DestPath);
469 }
470 Length = StrLen(NewFileInfo->FileName);
471 if (Length > 0) {
472 Length--;
473 }
474 if (NewFileInfo->FileName[Length] == L'\\') {
475 if (Node->FileName[0] == L'\\') {
476 //
477 // Don't allow for double slashes. Eliminate one of them.
478 //
479 NewFileInfo->FileName[Length] = CHAR_NULL;
480 }
481 StrCatS(NewFileInfo->FileName, (NewSize - SIZE_OF_EFI_FILE_INFO) / sizeof(CHAR16), Node->FileName);
482 }
483 NewFileInfo->Size = SIZE_OF_EFI_FILE_INFO + StrSize(NewFileInfo->FileName);
484
485 //
486 // Perform the move operation
487 //
488 Status = ShellSetFileInfo(Node->Handle, NewFileInfo);
489
490 //
491 // Free the info object we used...
492 //
493 FreePool(NewFileInfo);
494 }
495
496 return (Status);
497 }
498 /**
499 function to take a list of files to move and a destination location and do
500 the verification and moving of those files to that location. This function
501 will report any errors to the user and continue to move the rest of the files.
502
503 @param[in] FileList A LIST_ENTRY* based list of files to move
504 @param[out] Resp pointer to response from question. Pass back on looped calling
505 @param[in] DestParameter the originally specified destination location
506
507 @retval SHELL_SUCCESS the files were all moved.
508 @retval SHELL_INVALID_PARAMETER a parameter was invalid
509 @retval SHELL_SECURITY_VIOLATION a security violation ocurred
510 @retval SHELL_WRITE_PROTECTED the destination was write protected
511 @retval SHELL_OUT_OF_RESOURCES a memory allocation failed
512 **/
513 SHELL_STATUS
ValidateAndMoveFiles(IN EFI_SHELL_FILE_INFO * FileList,OUT VOID ** Resp,IN CONST CHAR16 * DestParameter)514 ValidateAndMoveFiles(
515 IN EFI_SHELL_FILE_INFO *FileList,
516 OUT VOID **Resp,
517 IN CONST CHAR16 *DestParameter
518 )
519 {
520 EFI_STATUS Status;
521 CHAR16 *HiiOutput;
522 CHAR16 *HiiResultOk;
523 CHAR16 *DestPath;
524 CHAR16 *FullDestPath;
525 CONST CHAR16 *Cwd;
526 CHAR16 *FullCwd;
527 SHELL_STATUS ShellStatus;
528 EFI_SHELL_FILE_INFO *Node;
529 VOID *Response;
530 UINT64 Attr;
531 CHAR16 *CleanFilePathStr;
532
533 ASSERT(FileList != NULL);
534 ASSERT(DestParameter != NULL);
535
536 DestPath = NULL;
537 FullDestPath = NULL;
538 Cwd = ShellGetCurrentDir(NULL);
539 Response = *Resp;
540 Attr = 0;
541 CleanFilePathStr = NULL;
542 FullCwd = NULL;
543
544 if (Cwd != NULL) {
545 FullCwd = AllocateZeroPool(StrSize(Cwd) + sizeof(CHAR16));
546 if (FullCwd == NULL) {
547 return SHELL_OUT_OF_RESOURCES;
548 } else {
549 StrCpyS(FullCwd, StrSize(Cwd)/sizeof(CHAR16)+1, Cwd);
550 StrCatS(FullCwd, StrSize(Cwd)/sizeof(CHAR16)+1, L"\\");
551 }
552 }
553
554 Status = ShellLevel2StripQuotes (DestParameter, &CleanFilePathStr);
555 if (EFI_ERROR (Status)) {
556 SHELL_FREE_NON_NULL(FullCwd);
557 if (Status == EFI_OUT_OF_RESOURCES) {
558 return SHELL_OUT_OF_RESOURCES;
559 } else {
560 return SHELL_INVALID_PARAMETER;
561 }
562 }
563
564 ASSERT (CleanFilePathStr != NULL);
565
566 //
567 // Get and validate the destination location
568 //
569 ShellStatus = GetDestinationLocation(CleanFilePathStr, &DestPath, FullCwd, (BOOLEAN)(FileList->Link.ForwardLink == FileList->Link.BackLink), &Attr);
570 FreePool (CleanFilePathStr);
571
572 if (ShellStatus != SHELL_SUCCESS) {
573 SHELL_FREE_NON_NULL (FullCwd);
574 return (ShellStatus);
575 }
576 DestPath = PathCleanUpDirectories(DestPath);
577 if (DestPath == NULL) {
578 FreePool (FullCwd);
579 return (SHELL_OUT_OF_RESOURCES);
580 }
581
582 HiiOutput = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_MV_OUTPUT), NULL);
583 HiiResultOk = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_GEN_RES_OK), NULL);
584 if (HiiOutput == NULL || HiiResultOk == NULL) {
585 SHELL_FREE_NON_NULL(DestPath);
586 SHELL_FREE_NON_NULL(HiiOutput);
587 SHELL_FREE_NON_NULL(HiiResultOk);
588 SHELL_FREE_NON_NULL(FullCwd);
589 return (SHELL_OUT_OF_RESOURCES);
590 }
591
592 //
593 // Go through the list of files and directories to move...
594 //
595 for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
596 ; !IsNull(&FileList->Link, &Node->Link)
597 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
598 ){
599 if (ShellGetExecutionBreakFlag()) {
600 break;
601 }
602
603 //
604 // These should never be NULL
605 //
606 ASSERT(Node->FileName != NULL);
607 ASSERT(Node->FullName != NULL);
608 ASSERT(Node->Info != NULL);
609
610 //
611 // skip the directory traversing stuff...
612 //
613 if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
614 continue;
615 }
616
617 SHELL_FREE_NON_NULL(FullDestPath);
618 FullDestPath = NULL;
619 if (ShellIsDirectory(DestPath)==EFI_SUCCESS) {
620 CreateFullDestPath((CONST CHAR16 **)&DestPath, &FullDestPath, Node->FileName);
621 }
622
623 //
624 // Validate that the move is valid
625 //
626 if (!IsValidMove(Node->FullName, FullCwd, FullDestPath!=NULL? FullDestPath:DestPath, Node->Info->Attribute, Attr, Node->Status)) {
627 ShellStatus = SHELL_INVALID_PARAMETER;
628 continue;
629 }
630
631 ShellPrintEx(-1, -1, HiiOutput, Node->FullName, FullDestPath!=NULL? FullDestPath:DestPath);
632
633 //
634 // See if destination exists
635 //
636 if (!EFI_ERROR(ShellFileExists(FullDestPath!=NULL? FullDestPath:DestPath))) {
637 if (Response == NULL) {
638 ShellPromptForResponseHii(ShellPromptResponseTypeYesNoAllCancel, STRING_TOKEN (STR_GEN_DEST_EXIST_OVR), gShellLevel2HiiHandle, &Response);
639 }
640 if (Response == NULL) {
641 return SHELL_ABORTED;
642 }
643 switch (*(SHELL_PROMPT_RESPONSE*)Response) {
644 case ShellPromptResponseNo:
645 FreePool(Response);
646 Response = NULL;
647 continue;
648 case ShellPromptResponseCancel:
649 *Resp = Response;
650 //
651 // indicate to stop everything
652 //
653 SHELL_FREE_NON_NULL(FullCwd);
654 return (SHELL_ABORTED);
655 case ShellPromptResponseAll:
656 *Resp = Response;
657 break;
658 case ShellPromptResponseYes:
659 FreePool(Response);
660 Response = NULL;
661 break;
662 default:
663 FreePool(Response);
664 SHELL_FREE_NON_NULL(FullCwd);
665 return SHELL_ABORTED;
666 }
667 Status = ShellDeleteFileByName(FullDestPath!=NULL? FullDestPath:DestPath);
668 }
669
670 if (IsBetweenFileSystem(Node->FullName, FullCwd, DestPath)) {
671 while (FullDestPath == NULL && DestPath != NULL && DestPath[0] != CHAR_NULL && DestPath[StrLen(DestPath) - 1] == L'\\') {
672 DestPath[StrLen(DestPath) - 1] = CHAR_NULL;
673 }
674 Status = MoveBetweenFileSystems(Node, FullDestPath!=NULL? FullDestPath:DestPath, &Response);
675 } else {
676 Status = MoveWithinFileSystems(Node, DestPath, &Response);
677 //
678 // Display error status
679 //
680 if (EFI_ERROR(Status)) {
681 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_UK), gShellLevel2HiiHandle, L"mv", Status);
682 }
683 }
684
685 //
686 // Check our result
687 //
688 if (EFI_ERROR(Status)) {
689 ShellStatus = SHELL_INVALID_PARAMETER;
690 if (Status == EFI_SECURITY_VIOLATION) {
691 ShellStatus = SHELL_SECURITY_VIOLATION;
692 } else if (Status == EFI_WRITE_PROTECTED) {
693 ShellStatus = SHELL_WRITE_PROTECTED;
694 } else if (Status == EFI_OUT_OF_RESOURCES) {
695 ShellStatus = SHELL_OUT_OF_RESOURCES;
696 } else if (Status == EFI_DEVICE_ERROR) {
697 ShellStatus = SHELL_DEVICE_ERROR;
698 } else if (Status == EFI_ACCESS_DENIED) {
699 ShellStatus = SHELL_ACCESS_DENIED;
700 }
701 } else {
702 ShellPrintEx(-1, -1, L"%s", HiiResultOk);
703 }
704
705 } // main for loop
706
707 SHELL_FREE_NON_NULL(FullDestPath);
708 SHELL_FREE_NON_NULL(DestPath);
709 SHELL_FREE_NON_NULL(HiiOutput);
710 SHELL_FREE_NON_NULL(HiiResultOk);
711 SHELL_FREE_NON_NULL(FullCwd);
712 return (ShellStatus);
713 }
714
715 /**
716 Function for 'mv' command.
717
718 @param[in] ImageHandle Handle to the Image (NULL if Internal).
719 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
720 **/
721 SHELL_STATUS
722 EFIAPI
ShellCommandRunMv(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)723 ShellCommandRunMv (
724 IN EFI_HANDLE ImageHandle,
725 IN EFI_SYSTEM_TABLE *SystemTable
726 )
727 {
728 EFI_STATUS Status;
729 LIST_ENTRY *Package;
730 CHAR16 *ProblemParam;
731 CHAR16 *Cwd;
732 UINTN CwdSize;
733 SHELL_STATUS ShellStatus;
734 UINTN ParamCount;
735 UINTN LoopCounter;
736 EFI_SHELL_FILE_INFO *FileList;
737 VOID *Response;
738
739 ProblemParam = NULL;
740 ShellStatus = SHELL_SUCCESS;
741 ParamCount = 0;
742 FileList = NULL;
743 Response = NULL;
744
745 //
746 // initialize the shell lib (we must be in non-auto-init...)
747 //
748 Status = ShellInitialize();
749 ASSERT_EFI_ERROR(Status);
750
751 //
752 // parse the command line
753 //
754 Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
755 if (EFI_ERROR(Status)) {
756 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
757 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"mv", ProblemParam);
758 FreePool(ProblemParam);
759 ShellStatus = SHELL_INVALID_PARAMETER;
760 } else {
761 ASSERT(FALSE);
762 }
763 } else {
764 //
765 // check for "-?"
766 //
767 if (ShellCommandLineGetFlag(Package, L"-?")) {
768 ASSERT(FALSE);
769 }
770
771 switch (ParamCount = ShellCommandLineGetCount(Package)) {
772 case 0:
773 case 1:
774 //
775 // we have insufficient parameters
776 //
777 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"mv");
778 ShellStatus = SHELL_INVALID_PARAMETER;
779 break;
780 case 2:
781 //
782 // must have valid CWD for single parameter...
783 //
784 if (ShellGetCurrentDir(NULL) == NULL){
785 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"mv");
786 ShellStatus = SHELL_INVALID_PARAMETER;
787 } else {
788 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
789 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
790 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, 1));
791 ShellStatus = SHELL_NOT_FOUND;
792 } else {
793 //
794 // ValidateAndMoveFiles will report errors to the screen itself
795 //
796 CwdSize = StrSize(ShellGetCurrentDir(NULL)) + sizeof(CHAR16);
797 Cwd = AllocateZeroPool(CwdSize);
798 if (Cwd == NULL) {
799 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellLevel2HiiHandle, L"mv");
800 ShellStatus = SHELL_OUT_OF_RESOURCES;
801 } else {
802 StrCpyS (Cwd, CwdSize / sizeof (CHAR16), ShellGetCurrentDir (NULL));
803 StrCatS (Cwd, CwdSize / sizeof (CHAR16), L"\\");
804 ShellStatus = ValidateAndMoveFiles (FileList, &Response, Cwd);
805 FreePool (Cwd);
806 }
807 }
808 }
809
810 break;
811 default:
812 ///@todo make sure this works with error half way through and continues...
813 for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount ; LoopCounter++) {
814 if (ShellGetExecutionBreakFlag()) {
815 break;
816 }
817 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
818 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
819 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, LoopCounter));
820 ShellStatus = SHELL_NOT_FOUND;
821 } else {
822 //
823 // ValidateAndMoveFiles will report errors to the screen itself
824 // Only change ShellStatus if it's sucessful
825 //
826 if (ShellStatus == SHELL_SUCCESS) {
827 ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
828 } else {
829 ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
830 }
831 }
832 if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
833 Status = ShellCloseFileMetaArg(&FileList);
834 if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
835 ShellStatus = SHELL_ACCESS_DENIED;
836 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, L"mv", ShellCommandLineGetRawValue(Package, 1), ShellStatus|MAX_BIT);
837 }
838 }
839 }
840 break;
841 } // switch on parameter count
842
843 if (FileList != NULL) {
844 ShellCloseFileMetaArg(&FileList);
845 }
846
847 //
848 // free the command line package
849 //
850 ShellCommandLineFreeVarList (Package);
851 }
852
853 SHELL_FREE_NON_NULL(Response);
854
855 if (ShellGetExecutionBreakFlag()) {
856 return (SHELL_ABORTED);
857 }
858
859 return (ShellStatus);
860 }
861