]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLevel2CommandsLib/Mv.c
5afcea23107fd62d9955d4447eaa171b25dfa170
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Mv.c
1 /** @file
2 Main file for mv shell level 2 function.
3
4 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "UefiShellLevel2CommandsLib.h"
16
17 /**
18 Function to validate that moving a specific file (FileName) to a specific
19 location (DestPath) is valid.
20
21 This function will verify that the destination is not a subdirectory of
22 FullName, that the Current working Directory is not being moved, and that
23 the directory is not read only.
24
25 if the move is invalid this function will report the error to StdOut.
26
27 @param FullName [in] The name of the file to move.
28 @param Cwd [in] The current working directory
29 @param DestPath [in] The target location to move to
30 @param Attribute[in] The Attribute of the file
31
32 @retval TRUE The move is valid
33 @retval FALSE The move is not
34 **/
35 BOOLEAN
36 EFIAPI
37 IsValidMove(
38 IN CONST CHAR16 *FullName,
39 IN CONST CHAR16 *Cwd,
40 IN CONST CHAR16 *DestPath,
41 IN CONST UINT64 Attribute
42 )
43 {
44 CHAR16 *Test;
45 CHAR16 *Test1;
46 CHAR16 *TestWalker;
47 INTN Result;
48 UINTN TempLen;
49 if (Cwd != NULL && StrCmp(FullName, Cwd) == 0) {
50 //
51 // Invalid move
52 //
53 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_CWD), gShellLevel2HiiHandle);
54 return (FALSE);
55 }
56 Test = NULL;
57 Test = StrnCatGrow(&Test, NULL, DestPath, 0);
58 TestWalker = Test;
59 ASSERT(TestWalker != NULL);
60 while(*TestWalker == L'\\') {
61 TestWalker++;
62 }
63 while(TestWalker != NULL && TestWalker[StrLen(TestWalker)-1] == L'\\') {
64 TestWalker[StrLen(TestWalker)-1] = CHAR_NULL;
65 }
66 ASSERT(TestWalker != NULL);
67 ASSERT(FullName != NULL);
68 if (StrStr(FullName, TestWalker) != 0) {
69 TempLen = StrLen(FullName);
70 if (StrStr(FullName, TestWalker) != FullName // not the first items... (could below it)
71 && TempLen <= (StrLen(TestWalker) + 1)
72 && StrStr(FullName+StrLen(TestWalker) + 1, L"\\") == NULL) {
73 //
74 // Invalid move
75 //
76 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);
77 FreePool(Test);
78 return (FALSE);
79 }
80 }
81 FreePool(Test);
82 if (StrStr(DestPath, FullName) != 0 && StrStr(DestPath, FullName) != DestPath) {
83 //
84 // Invalid move
85 //
86 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_SUB), gShellLevel2HiiHandle);
87 return (FALSE);
88 }
89 if ((Attribute & EFI_FILE_READ_ONLY) != 0) {
90 //
91 // invalid to move read only
92 //
93 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_RO), gShellLevel2HiiHandle);
94 return (FALSE);
95 }
96 Test = StrStr(FullName, L":");
97 Test1 = StrStr(DestPath, L":");
98 if (Test1 != NULL && Test != NULL) {
99 *Test = CHAR_NULL;
100 *Test1 = CHAR_NULL;
101 Result = StringNoCaseCompare(&FullName, &DestPath);
102 *Test = L':';
103 *Test1 = L':';
104 if (Result != 0) {
105 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_MV_INV_FS), gShellLevel2HiiHandle);
106 return (FALSE);
107 }
108 }
109 return (TRUE);
110 }
111
112 /**
113 Function to take a destination path that might contain wildcards and verify
114 that there is only a single possible target (IE we cant have wildcards that
115 have 2 possible destination).
116
117 if the result is sucessful the caller must free *DestPathPointer.
118
119 @param[in] DestDir The original path to the destination.
120 @param[in,out] DestPathPointer A pointer to the callee allocated final path.
121 @param[in] Cwd A pointer to the current working directory.
122
123 @retval EFI_INVALID_PARAMETR The DestDir could not be resolved to a location.
124 @retval EFI_INVALID_PARAMETR The DestDir could be resolved to more than 1 location.
125 @retval EFI_SUCCESS The operation was sucessful.
126 **/
127 SHELL_STATUS
128 EFIAPI
129 GetDestinationLocation(
130 IN CONST CHAR16 *DestDir,
131 IN OUT CHAR16 **DestPathPointer,
132 IN CONST CHAR16 *Cwd
133 )
134 {
135 EFI_SHELL_FILE_INFO *DestList;
136 EFI_SHELL_FILE_INFO *Node;
137 EFI_STATUS Status;
138 CHAR16 *DestPath;
139 CHAR16 *TempLocation;
140 UINTN NewSize;
141
142 DestList = NULL;
143 DestPath = NULL;
144
145 if (StrStr(DestDir, L"\\") == DestDir) {
146 DestPath = AllocateZeroPool(StrSize(Cwd));
147 if (DestPath == NULL) {
148 return (SHELL_OUT_OF_RESOURCES);
149 }
150 StrCpy(DestPath, Cwd);
151 while (PathRemoveLastItem(DestPath)) ;
152 *DestPathPointer = DestPath;
153 return (SHELL_SUCCESS);
154 }
155 //
156 // get the destination path
157 //
158 Status = ShellOpenFileMetaArg((CHAR16*)DestDir, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, &DestList);
159 if (DestList == NULL || IsListEmpty(&DestList->Link)) {
160 //
161 // Not existing... must be renaming
162 //
163 if ((TempLocation = StrStr(DestDir, L":")) == NULL) {
164 NewSize = StrSize(Cwd);
165 NewSize += StrSize(DestDir);
166 DestPath = AllocateZeroPool(NewSize);
167 if (DestPath == NULL) {
168 ShellCloseFileMetaArg(&DestList);
169 return (SHELL_OUT_OF_RESOURCES);
170 }
171 StrCpy(DestPath, Cwd);
172 if (DestPath[StrLen(DestPath)-1] != L'\\' && DestDir[0] != L'\\') {
173 StrCat(DestPath, L"\\");
174 } else if (DestPath[StrLen(DestPath)-1] == L'\\' && DestDir[0] == L'\\') {
175 ((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
176 }
177 StrCat(DestPath, DestDir);
178 } else {
179 ASSERT(DestPath == NULL);
180 DestPath = StrnCatGrow(&DestPath, NULL, DestDir, 0);
181 if (DestPath == NULL) {
182 ShellCloseFileMetaArg(&DestList);
183 return (SHELL_OUT_OF_RESOURCES);
184 }
185 }
186 } else {
187 Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&DestList->Link);
188 //
189 // Make sure there is only 1 node in the list.
190 //
191 if (!IsNodeAtEnd(&DestList->Link, &Node->Link)) {
192 ShellCloseFileMetaArg(&DestList);
193 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_MARG_ERROR), gShellLevel2HiiHandle, DestDir);
194 return (SHELL_INVALID_PARAMETER);
195 }
196 if (ShellIsDirectory(Node->FullName)==EFI_SUCCESS) {
197 DestPath = AllocateZeroPool(StrSize(Node->FullName)+sizeof(CHAR16));
198 if (DestPath == NULL) {
199 ShellCloseFileMetaArg(&DestList);
200 return (SHELL_OUT_OF_RESOURCES);
201 }
202 StrCpy(DestPath, Node->FullName);
203 StrCat(DestPath, L"\\");
204 } else {
205 //
206 // cant move onto another file.
207 //
208 ShellCloseFileMetaArg(&DestList);
209 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_ERROR), gShellLevel2HiiHandle, DestDir);
210 return (SHELL_INVALID_PARAMETER);
211 }
212 }
213
214 *DestPathPointer = DestPath;
215 ShellCloseFileMetaArg(&DestList);
216
217 return (SHELL_SUCCESS);
218 }
219
220 /**
221 function to take a list of files to move and a destination location and do
222 the verification and moving of those files to that location. This function
223 will report any errors to the user and continue to move the rest of the files.
224
225 @param[in] FileList A LIST_ENTRY* based list of files to move
226 @param[out] Resp pointer to response from question. Pass back on looped calling
227 @param[in] DestDir the destination location
228
229 @retval SHELL_SUCCESS the files were all moved.
230 @retval SHELL_INVALID_PARAMETER a parameter was invalid
231 @retval SHELL_SECURITY_VIOLATION a security violation ocurred
232 @retval SHELL_WRITE_PROTECTED the destination was write protected
233 @retval SHELL_OUT_OF_RESOURCES a memory allocation failed
234 **/
235 SHELL_STATUS
236 EFIAPI
237 ValidateAndMoveFiles(
238 IN CONST EFI_SHELL_FILE_INFO *FileList,
239 OUT VOID **Resp,
240 IN CONST CHAR16 *DestDir
241 )
242 {
243 EFI_STATUS Status;
244 CHAR16 *HiiOutput;
245 CHAR16 *HiiResultOk;
246 CHAR16 *DestPath;
247 CONST CHAR16 *Cwd;
248 SHELL_STATUS ShellStatus;
249 CONST EFI_SHELL_FILE_INFO *Node;
250 EFI_FILE_INFO *NewFileInfo;
251 CHAR16 *TempLocation;
252 UINTN NewSize;
253 UINTN Length;
254 VOID *Response;
255 SHELL_FILE_HANDLE DestHandle;
256
257 ASSERT(FileList != NULL);
258 ASSERT(DestDir != NULL);
259
260 DestPath = NULL;
261 Cwd = ShellGetCurrentDir(NULL);
262 Response = *Resp;
263
264 //
265 // Get and validate the destination location
266 //
267 ShellStatus = GetDestinationLocation(DestDir, &DestPath, Cwd);
268 if (ShellStatus != SHELL_SUCCESS) {
269 return (ShellStatus);
270 }
271 DestPath = PathCleanUpDirectories(DestPath);
272
273 HiiOutput = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_MV_OUTPUT), NULL);
274 HiiResultOk = HiiGetString (gShellLevel2HiiHandle, STRING_TOKEN (STR_GEN_RES_OK), NULL);
275 ASSERT (DestPath != NULL);
276 ASSERT (HiiResultOk != NULL);
277 ASSERT (HiiOutput != NULL);
278 // ASSERT (Cwd != NULL);
279
280 //
281 // Go through the list of files and directories to move...
282 //
283 for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
284 ; !IsNull(&FileList->Link, &Node->Link)
285 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
286 ){
287 if (ShellGetExecutionBreakFlag()) {
288 break;
289 }
290 ASSERT(Node->FileName != NULL);
291 ASSERT(Node->FullName != NULL);
292
293 //
294 // skip the directory traversing stuff...
295 //
296 if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
297 continue;
298 }
299
300 //
301 // Validate that the move is valid
302 //
303 if (!IsValidMove(Node->FullName, Cwd, DestPath, Node->Info->Attribute)) {
304 ShellStatus = SHELL_INVALID_PARAMETER;
305 continue;
306 }
307
308 //
309 // Chop off map info from "DestPath"
310 //
311 if ((TempLocation = StrStr(DestPath, L":")) != NULL) {
312 CopyMem(DestPath, TempLocation+1, StrSize(TempLocation+1));
313 }
314
315 //
316 // construct the new file info block
317 //
318 NewSize = StrSize(DestPath);
319 NewSize += StrSize(Node->FileName) + sizeof(EFI_FILE_INFO) + sizeof(CHAR16);
320 NewFileInfo = AllocateZeroPool(NewSize);
321 if (NewFileInfo == NULL) {
322 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle);
323 ShellStatus = SHELL_OUT_OF_RESOURCES;
324 } else {
325 CopyMem(NewFileInfo, Node->Info, sizeof(EFI_FILE_INFO));
326 if (DestPath[0] != L'\\') {
327 StrCpy(NewFileInfo->FileName, L"\\");
328 StrCat(NewFileInfo->FileName, DestPath);
329 } else {
330 StrCpy(NewFileInfo->FileName, DestPath);
331 }
332 Length = StrLen(NewFileInfo->FileName);
333 if (Length > 0) {
334 Length--;
335 }
336 if (NewFileInfo->FileName[Length] == L'\\') {
337 if (Node->FileName[0] == L'\\') {
338 //
339 // Don't allow for double slashes. Eliminate one of them.
340 //
341 NewFileInfo->FileName[Length] = CHAR_NULL;
342 }
343 StrCat(NewFileInfo->FileName, Node->FileName);
344 }
345 NewFileInfo->Size = sizeof(EFI_FILE_INFO) + StrSize(NewFileInfo->FileName);
346 ShellPrintEx(-1, -1, HiiOutput, Node->FullName, NewFileInfo->FileName);
347
348 if (!EFI_ERROR(ShellFileExists(NewFileInfo->FileName))) {
349 if (Response == NULL) {
350 ShellPromptForResponseHii(ShellPromptResponseTypeYesNoAllCancel, STRING_TOKEN (STR_GEN_DEST_EXIST_OVR), gShellLevel2HiiHandle, &Response);
351 }
352 switch (*(SHELL_PROMPT_RESPONSE*)Response) {
353 case ShellPromptResponseNo:
354 FreePool(NewFileInfo);
355 continue;
356 case ShellPromptResponseCancel:
357 *Resp = Response;
358 //
359 // indicate to stop everything
360 //
361 FreePool(NewFileInfo);
362 FreePool(DestPath);
363 FreePool(HiiOutput);
364 FreePool(HiiResultOk);
365 return (SHELL_ABORTED);
366 case ShellPromptResponseAll:
367 *Resp = Response;
368 break;
369 case ShellPromptResponseYes:
370 FreePool(Response);
371 break;
372 default:
373 FreePool(Response);
374 FreePool(NewFileInfo);
375 FreePool(DestPath);
376 FreePool(HiiOutput);
377 FreePool(HiiResultOk);
378 return SHELL_ABORTED;
379 }
380 Status = ShellOpenFileByName(NewFileInfo->FileName, &DestHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0);
381 ShellDeleteFile(&DestHandle);
382 }
383
384
385 //
386 // Perform the move operation
387 //
388 Status = ShellSetFileInfo(Node->Handle, NewFileInfo);
389
390 //
391 // Free the info object we used...
392 //
393 FreePool(NewFileInfo);
394
395 //
396 // Check our result
397 //
398 if (EFI_ERROR(Status)) {
399 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_UK), gShellLevel2HiiHandle, Status);
400 //
401 // move failed
402 //
403 switch(Status){
404 default:
405 ShellStatus = SHELL_INVALID_PARAMETER;
406 case EFI_SECURITY_VIOLATION:
407 ShellStatus = SHELL_SECURITY_VIOLATION;
408 case EFI_WRITE_PROTECTED:
409 ShellStatus = SHELL_WRITE_PROTECTED;
410 case EFI_OUT_OF_RESOURCES:
411 ShellStatus = SHELL_OUT_OF_RESOURCES;
412 case EFI_DEVICE_ERROR:
413 ShellStatus = SHELL_DEVICE_ERROR;
414 case EFI_ACCESS_DENIED:
415 ShellStatus = SHELL_ACCESS_DENIED;
416 } // switch
417 } else {
418 ShellPrintEx(-1, -1, L"%s", HiiResultOk);
419 }
420 }
421 } // for loop
422
423 FreePool(DestPath);
424 FreePool(HiiOutput);
425 FreePool(HiiResultOk);
426 return (ShellStatus);
427 }
428
429 /**
430 Function for 'mv' command.
431
432 @param[in] ImageHandle Handle to the Image (NULL if Internal).
433 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
434 **/
435 SHELL_STATUS
436 EFIAPI
437 ShellCommandRunMv (
438 IN EFI_HANDLE ImageHandle,
439 IN EFI_SYSTEM_TABLE *SystemTable
440 )
441 {
442 EFI_STATUS Status;
443 LIST_ENTRY *Package;
444 CHAR16 *ProblemParam;
445 SHELL_STATUS ShellStatus;
446 UINTN ParamCount;
447 UINTN LoopCounter;
448 EFI_SHELL_FILE_INFO *FileList;
449 VOID *Response;
450
451 ProblemParam = NULL;
452 ShellStatus = SHELL_SUCCESS;
453 ParamCount = 0;
454 FileList = NULL;
455 Response = NULL;
456
457 //
458 // initialize the shell lib (we must be in non-auto-init...)
459 //
460 Status = ShellInitialize();
461 ASSERT_EFI_ERROR(Status);
462
463 //
464 // parse the command line
465 //
466 Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
467 if (EFI_ERROR(Status)) {
468 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
469 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, ProblemParam);
470 FreePool(ProblemParam);
471 ShellStatus = SHELL_INVALID_PARAMETER;
472 } else {
473 ASSERT(FALSE);
474 }
475 } else {
476 //
477 // check for "-?"
478 //
479 if (ShellCommandLineGetFlag(Package, L"-?")) {
480 ASSERT(FALSE);
481 }
482
483 switch (ParamCount = ShellCommandLineGetCount(Package)) {
484 case 0:
485 case 1:
486 //
487 // we have insufficient parameters
488 //
489 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle);
490 ShellStatus = SHELL_INVALID_PARAMETER;
491 break;
492 case 2:
493 //
494 // must have valid CWD for single parameter...
495 //
496 if (ShellGetCurrentDir(NULL) == NULL){
497 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
498 ShellStatus = SHELL_INVALID_PARAMETER;
499 } else {
500 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
501 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
502 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, 1));
503 ShellStatus = SHELL_NOT_FOUND;
504 } else {
505 //
506 // ValidateAndMoveFiles will report errors to the screen itself
507 //
508 ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellGetCurrentDir(NULL));
509 }
510 }
511
512 break;
513 default:
514 ///@todo make sure this works with error half way through and continues...
515 for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount ; LoopCounter++) {
516 if (ShellGetExecutionBreakFlag()) {
517 break;
518 }
519 Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
520 if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
521 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_NF), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, LoopCounter));
522 ShellStatus = SHELL_NOT_FOUND;
523 } else {
524 //
525 // ValidateAndMoveFiles will report errors to the screen itself
526 // Only change ShellStatus if it's sucessful
527 //
528 if (ShellStatus == SHELL_SUCCESS) {
529 ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
530 } else {
531 ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
532 }
533 }
534 if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
535 Status = ShellCloseFileMetaArg(&FileList);
536 if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
537 ShellStatus = SHELL_ACCESS_DENIED;
538 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ERR_FILE), gShellLevel2HiiHandle, ShellCommandLineGetRawValue(Package, 1), ShellStatus|MAX_BIT);
539 }
540 }
541 }
542 break;
543 } // switch on parameter count
544
545 if (FileList != NULL) {
546 ShellCloseFileMetaArg(&FileList);
547 }
548
549 //
550 // free the command line package
551 //
552 ShellCommandLineFreeVarList (Package);
553 }
554
555 SHELL_FREE_NON_NULL(Response);
556
557 if (ShellGetExecutionBreakFlag()) {
558 return (SHELL_ABORTED);
559 }
560
561 return (ShellStatus);
562 }