]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
ShellPkg: remove double free operation
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Ls.c
1 /** @file
2 Main file for ls shell level 2 function.
3
4 Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
5 Copyright (c) 2009 - 2012, 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
19 /**
20 print out the list of files and directories from the LS command
21
22 @param[in] Rec TRUE to automatically recurse into each found directory
23 FALSE to only list the specified directory.
24 @param[in] Attribs List of required Attribute for display.
25 If 0 then all non-system and non-hidden files will be printed.
26 @param[in] Sfo TRUE to use Standard Format Output, FALSE otherwise
27 @param[in] Path String with starting path.
28 @param[in] First TRUE for the original and FALSE for any recursion spawned instances.
29 @param[in] Count The count of bits enabled in Attribs.
30 @param[in] TimeZone The current time zone offset.
31
32 @retval SHELL_SUCCESS the printing was sucessful.
33 **/
34 SHELL_STATUS
35 EFIAPI
36 PrintLsOutput(
37 IN CONST BOOLEAN Rec,
38 IN CONST UINT64 Attribs,
39 IN CONST BOOLEAN Sfo,
40 IN CONST CHAR16 *Path,
41 IN CONST BOOLEAN First,
42 IN CONST UINTN Count,
43 IN CONST INT16 TimeZone
44 )
45 {
46 EFI_STATUS Status;
47 EFI_SHELL_FILE_INFO *ListHead;
48 EFI_SHELL_FILE_INFO *Node;
49 SHELL_STATUS ShellStatus;
50 UINT64 FileCount;
51 UINT64 DirCount;
52 UINT64 FileSize;
53 CHAR16 *DirectoryName;
54 UINTN LongestPath;
55 EFI_FILE_SYSTEM_INFO *SysInfo;
56 UINTN SysInfoSize;
57 SHELL_FILE_HANDLE ShellFileHandle;
58 CHAR16 *CorrectedPath;
59 EFI_FILE_PROTOCOL *EfiFpHandle;
60
61 FileCount = 0;
62 DirCount = 0;
63 FileSize = 0;
64 ListHead = NULL;
65 ShellStatus = SHELL_SUCCESS;
66 LongestPath = 0;
67 CorrectedPath = NULL;
68
69 CorrectedPath = StrnCatGrow(&CorrectedPath, NULL, Path, 0);
70 if (CorrectedPath == NULL) {
71 return (SHELL_OUT_OF_RESOURCES);
72 }
73
74 PathCleanUpDirectories(CorrectedPath);
75
76 if (!Sfo) {
77 //
78 // get directory name from path...
79 //
80 DirectoryName = GetFullyQualifiedPath(CorrectedPath);
81
82 //
83 // print header
84 //
85 ShellPrintHiiEx (
86 0,
87 gST->ConOut->Mode->CursorRow,
88 NULL,
89 STRING_TOKEN (STR_LS_HEADER_LINE1),
90 gShellLevel2HiiHandle,
91 DirectoryName
92 );
93 FreePool(DirectoryName);
94 }
95
96 Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
97 if (EFI_ERROR(Status)) {
98 SHELL_FREE_NON_NULL(CorrectedPath);
99 if(Status == EFI_NOT_FOUND){
100 return (SHELL_NOT_FOUND);
101 }
102 return (SHELL_DEVICE_ERROR);
103 }
104 if (ListHead == NULL || IsListEmpty(&ListHead->Link)) {
105 SHELL_FREE_NON_NULL(CorrectedPath);
106 //
107 // On the first one only we expect to find something...
108 // do we find the . and .. directories otherwise?
109 //
110 if (First) {
111 return (SHELL_NOT_FOUND);
112 }
113 return (SHELL_SUCCESS);
114 }
115
116 if (Sfo && First) {
117 //
118 // Get the first valid handle (directories)
119 //
120 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
121 ; !IsNull(&ListHead->Link, &Node->Link) && Node->Handle == NULL
122 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
123 );
124
125 if (Node->Handle == NULL) {
126 DirectoryName = GetFullyQualifiedPath(((EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link))->FullName);
127
128 //
129 // We need to open something up to get system information
130 //
131 Status = gEfiShellProtocol->OpenFileByName(
132 DirectoryName,
133 &ShellFileHandle,
134 EFI_FILE_MODE_READ);
135
136 ASSERT_EFI_ERROR(Status);
137 FreePool(DirectoryName);
138
139 //
140 // Get the Volume Info from ShellFileHandle
141 //
142 SysInfo = NULL;
143 SysInfoSize = 0;
144 EfiFpHandle = ConvertShellHandleToEfiFileProtocol(ShellFileHandle);
145 Status = EfiFpHandle->GetInfo(
146 EfiFpHandle,
147 &gEfiFileSystemInfoGuid,
148 &SysInfoSize,
149 SysInfo);
150
151 if (Status == EFI_BUFFER_TOO_SMALL) {
152 SysInfo = AllocateZeroPool(SysInfoSize);
153 Status = EfiFpHandle->GetInfo(
154 EfiFpHandle,
155 &gEfiFileSystemInfoGuid,
156 &SysInfoSize,
157 SysInfo);
158 }
159
160 ASSERT_EFI_ERROR(Status);
161
162 gEfiShellProtocol->CloseFile(ShellFileHandle);
163 } else {
164 //
165 // Get the Volume Info from Node->Handle
166 //
167 SysInfo = NULL;
168 SysInfoSize = 0;
169 EfiFpHandle = ConvertShellHandleToEfiFileProtocol(Node->Handle);
170 Status = EfiFpHandle->GetInfo(
171 EfiFpHandle,
172 &gEfiFileSystemInfoGuid,
173 &SysInfoSize,
174 SysInfo);
175
176 if (Status == EFI_BUFFER_TOO_SMALL) {
177 SysInfo = AllocateZeroPool(SysInfoSize);
178 Status = EfiFpHandle->GetInfo(
179 EfiFpHandle,
180 &gEfiFileSystemInfoGuid,
181 &SysInfoSize,
182 SysInfo);
183 }
184
185 ASSERT_EFI_ERROR(Status);
186 }
187
188 ShellPrintHiiEx (
189 -1,
190 -1,
191 NULL,
192 STRING_TOKEN (STR_GEN_SFO_HEADER),
193 gShellLevel2HiiHandle,
194 L"ls");
195 //
196 // print VolumeInfo table
197 //
198 ASSERT(SysInfo != NULL);
199 ShellPrintHiiEx (
200 0,
201 gST->ConOut->Mode->CursorRow,
202 NULL,
203 STRING_TOKEN (STR_LS_SFO_VOLINFO),
204 gShellLevel2HiiHandle,
205 SysInfo->VolumeLabel,
206 SysInfo->VolumeSize,
207 SysInfo->ReadOnly?L"TRUE":L"FALSE",
208 SysInfo->FreeSpace,
209 SysInfo->BlockSize
210 );
211 if (SysInfo != NULL) {
212 FreePool(SysInfo);
213 }
214 }
215
216 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
217 ; !IsNull(&ListHead->Link, &Node->Link)
218 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
219 ){
220 ASSERT(Node != NULL);
221 if (LongestPath < StrSize(Node->FullName)) {
222 LongestPath = StrSize(Node->FullName);
223 }
224 ASSERT(Node->Info != NULL);
225 ASSERT((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);
226 if (Attribs == 0) {
227 //
228 // NOT system & NOT hidden
229 //
230 if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)
231 || (Node->Info->Attribute & EFI_FILE_HIDDEN)
232 ){
233 continue;
234 }
235 } else if ((Attribs != EFI_FILE_VALID_ATTR) ||
236 (Count == 5)) {
237 //
238 // Only matches the bits which "Attribs" contains, not
239 // all files/directories with any of the bits.
240 // Count == 5 is used to tell the difference between a user
241 // specifying all bits (EX: -arhsda) and just specifying
242 // -a (means display all files with any attribute).
243 //
244 if ( (Node->Info->Attribute & Attribs) != Attribs) {
245 continue;
246 }
247 }
248
249 if (Sfo) {
250 //
251 // Print the FileInfo Table
252 //
253 ShellPrintHiiEx (
254 0,
255 gST->ConOut->Mode->CursorRow,
256 NULL,
257 STRING_TOKEN (STR_LS_SFO_FILEINFO),
258 gShellLevel2HiiHandle,
259 Node->FullName,
260 Node->Info->FileSize,
261 Node->Info->PhysicalSize,
262 (Node->Info->Attribute & EFI_FILE_ARCHIVE) != 0?L"a":L"",
263 (Node->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"d":L"",
264 (Node->Info->Attribute & EFI_FILE_HIDDEN) != 0?L"h":L"",
265 (Node->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L"r":L"",
266 (Node->Info->Attribute & EFI_FILE_SYSTEM) != 0?L"s":L"",
267 Node->Info->CreateTime.Hour,
268 Node->Info->CreateTime.Minute,
269 Node->Info->CreateTime.Second,
270 Node->Info->CreateTime.Day,
271 Node->Info->CreateTime.Month,
272 Node->Info->CreateTime.Year,
273 Node->Info->LastAccessTime.Hour,
274 Node->Info->LastAccessTime.Minute,
275 Node->Info->LastAccessTime.Second,
276 Node->Info->LastAccessTime.Day,
277 Node->Info->LastAccessTime.Month,
278 Node->Info->LastAccessTime.Year,
279 Node->Info->ModificationTime.Hour,
280 Node->Info->ModificationTime.Minute,
281 Node->Info->ModificationTime.Second,
282 Node->Info->ModificationTime.Day,
283 Node->Info->ModificationTime.Month,
284 Node->Info->ModificationTime.Year
285 );
286 } else {
287 //
288 // print this one out...
289 // first print the universal start, next print the type specific name format, last print the CRLF
290 //
291 ShellPrintHiiEx (
292 -1,
293 -1,
294 NULL,
295 STRING_TOKEN (STR_LS_LINE_START_ALL),
296 gShellLevel2HiiHandle,
297 &Node->Info->ModificationTime,
298 (Node->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"<DIR>":L"",
299 (Node->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L'r':L' ',
300 Node->Info->FileSize
301 );
302 if (Node->Info->Attribute & EFI_FILE_DIRECTORY) {
303 DirCount++;
304 ShellPrintHiiEx (
305 -1,
306 -1,
307 NULL,
308 STRING_TOKEN (STR_LS_LINE_END_DIR),
309 gShellLevel2HiiHandle,
310 Node->FileName
311 );
312 } else {
313 FileCount++;
314 FileSize += Node->Info->FileSize;
315 if ( (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".nsh", (CHAR16*)&(Node->FileName[StrLen (Node->FileName) - 4])) == 0)
316 || (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".efi", (CHAR16*)&(Node->FileName[StrLen (Node->FileName) - 4])) == 0)
317 ){
318 ShellPrintHiiEx (
319 -1,
320 -1,
321 NULL,
322 STRING_TOKEN (STR_LS_LINE_END_EXE),
323 gShellLevel2HiiHandle,
324 Node->FileName
325 );
326 } else {
327 ShellPrintHiiEx (
328 -1,
329 -1,
330 NULL,
331 STRING_TOKEN (STR_LS_LINE_END_FILE),
332 gShellLevel2HiiHandle,
333 Node->FileName
334 );
335 }
336 }
337 }
338 }
339
340 if (!Sfo) {
341 //
342 // print footer
343 //
344 ShellPrintHiiEx (
345 -1,
346 -1,
347 NULL,
348 STRING_TOKEN (STR_LS_FOOTER_LINE),
349 gShellLevel2HiiHandle,
350 FileCount,
351 FileSize,
352 DirCount
353 );
354 }
355
356 if (Rec){
357 DirectoryName = AllocateZeroPool(LongestPath + 2*sizeof(CHAR16));
358 if (DirectoryName == NULL) {
359 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle);
360 ShellStatus = SHELL_OUT_OF_RESOURCES;
361 } else {
362 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
363 ; !IsNull(&ListHead->Link, &Node->Link) && ShellStatus == SHELL_SUCCESS
364 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
365 ){
366 if (ShellGetExecutionBreakFlag ()) {
367 ShellStatus = SHELL_ABORTED;
368 break;
369 }
370
371 //
372 // recurse on any directory except the traversing ones...
373 //
374 if (((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)
375 && StrCmp(Node->FileName, L".") != 0
376 && StrCmp(Node->FileName, L"..") != 0
377 ){
378 StrCpy(DirectoryName, Node->FullName);
379 StrCat(DirectoryName, L"\\*");
380 ShellStatus = PrintLsOutput(
381 Rec,
382 Attribs,
383 Sfo,
384 DirectoryName,
385 FALSE,
386 Count,
387 TimeZone);
388 }
389 }
390 FreePool(DirectoryName);
391 }
392 }
393
394 FreePool(CorrectedPath);
395 ShellCloseFileMetaArg(&ListHead);
396 return (ShellStatus);
397 }
398
399 STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
400 {L"-r", TypeFlag},
401 {L"-a", TypeStart},
402 {L"-sfo", TypeFlag},
403 {NULL, TypeMax}
404 };
405
406 /**
407 Function for 'ls' command.
408
409 @param[in] ImageHandle Handle to the Image (NULL if Internal).
410 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
411 **/
412 SHELL_STATUS
413 EFIAPI
414 ShellCommandRunLs (
415 IN EFI_HANDLE ImageHandle,
416 IN EFI_SYSTEM_TABLE *SystemTable
417 )
418 {
419 EFI_STATUS Status;
420 LIST_ENTRY *Package;
421 CHAR16 *ProblemParam;
422 CONST CHAR16 *Attribs;
423 SHELL_STATUS ShellStatus;
424 UINT64 RequiredAttributes;
425 CONST CHAR16 *PathName;
426 CONST CHAR16 *CurDir;
427 UINTN Count;
428 CHAR16 *FullPath;
429 UINTN Size;
430 EFI_TIME TheTime;
431 BOOLEAN SfoMode;
432
433 Size = 0;
434 FullPath = NULL;
435 ProblemParam = NULL;
436 Attribs = NULL;
437 ShellStatus = SHELL_SUCCESS;
438 RequiredAttributes = 0;
439 PathName = NULL;
440 CurDir = NULL;
441 Count = 0;
442
443 //
444 // initialize the shell lib (we must be in non-auto-init...)
445 //
446 Status = ShellInitialize();
447 ASSERT_EFI_ERROR(Status);
448
449 //
450 // Fix local copies of the protocol pointers
451 //
452 Status = CommandInit();
453 ASSERT_EFI_ERROR(Status);
454
455 //
456 // parse the command line
457 //
458 Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
459 if (EFI_ERROR(Status)) {
460 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
461 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, ProblemParam);
462 FreePool(ProblemParam);
463 ShellStatus = SHELL_INVALID_PARAMETER;
464 } else {
465 ASSERT(FALSE);
466 }
467 } else {
468 //
469 // check for "-?"
470 //
471 if (ShellCommandLineGetFlag(Package, L"-?")) {
472 ASSERT(FALSE);
473 }
474
475 if (ShellCommandLineGetCount(Package) > 2) {
476 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle);
477 ShellStatus = SHELL_INVALID_PARAMETER;
478 } else {
479 //
480 // check for -a
481 //
482 if (ShellCommandLineGetFlag(Package, L"-a")) {
483 for ( Attribs = ShellCommandLineGetValue(Package, L"-a")
484 ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
485 ; Attribs++
486 ){
487 switch (*Attribs) {
488 case L'a':
489 case L'A':
490 RequiredAttributes |= EFI_FILE_ARCHIVE;
491 Count++;
492 continue;
493 case L's':
494 case L'S':
495 RequiredAttributes |= EFI_FILE_SYSTEM;
496 Count++;
497 continue;
498 case L'h':
499 case L'H':
500 RequiredAttributes |= EFI_FILE_HIDDEN;
501 Count++;
502 continue;
503 case L'r':
504 case L'R':
505 RequiredAttributes |= EFI_FILE_READ_ONLY;
506 Count++;
507 continue;
508 case L'd':
509 case L'D':
510 RequiredAttributes |= EFI_FILE_DIRECTORY;
511 Count++;
512 continue;
513 default:
514 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, ShellCommandLineGetValue(Package, L"-a"));
515 ShellStatus = SHELL_INVALID_PARAMETER;
516 break;
517 } // switch
518 } // for loop
519 //
520 // if nothing is specified all are specified
521 //
522 if (RequiredAttributes == 0) {
523 RequiredAttributes = EFI_FILE_VALID_ATTR;
524 }
525 } // if -a present
526 if (ShellStatus == SHELL_SUCCESS) {
527 PathName = ShellCommandLineGetRawValue(Package, 1);
528 if (PathName == NULL) {
529 CurDir = gEfiShellProtocol->GetCurDir(NULL);
530 if (CurDir == NULL) {
531 ShellStatus = SHELL_NOT_FOUND;
532 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
533 }
534 }
535 if (PathName != NULL) {
536 if (StrStr(PathName, L":") == NULL && gEfiShellProtocol->GetCurDir(NULL) == NULL) {
537 ShellStatus = SHELL_NOT_FOUND;
538 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
539 } else {
540 ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));
541 StrnCatGrow(&FullPath, &Size, PathName, 0);
542 if (ShellIsDirectory(PathName) == EFI_SUCCESS) {
543 if (PathName[StrLen (PathName) - 1] == '\\') {
544 //
545 // For path ending with '\', just append '*'.
546 //
547 StrnCatGrow (&FullPath, &Size, L"*", 0);
548 } else if (PathName[StrLen (PathName) - 1] == '*') {
549 //
550 // For path ending with '*', do nothing.
551 //
552 } else {
553 //
554 // Otherwise, append '\*' to directory name.
555 //
556 StrnCatGrow (&FullPath, &Size, L"\\*", 0);
557 }
558 }
559 }
560 } else {
561 ASSERT(FullPath == NULL);
562 StrnCatGrow(&FullPath, NULL, L"*", 0);
563 }
564 Status = gRT->GetTime(&TheTime, NULL);
565 if (EFI_ERROR(Status)) {
566 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN), gShellLevel2HiiHandle, L"gRT->GetTime", Status);
567 TheTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
568 }
569
570 SfoMode = ShellCommandLineGetFlag(Package, L"-sfo");
571 if (ShellStatus == SHELL_SUCCESS) {
572 ShellStatus = PrintLsOutput(
573 ShellCommandLineGetFlag(Package, L"-r"),
574 RequiredAttributes,
575 SfoMode,
576 FullPath,
577 TRUE,
578 Count,
579 (INT16)(TheTime.TimeZone==EFI_UNSPECIFIED_TIMEZONE?0:TheTime.TimeZone)
580 );
581 if (ShellStatus == SHELL_NOT_FOUND) {
582 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LS_FILE_NOT_FOUND), gShellLevel2HiiHandle);
583 } else if (ShellStatus == SHELL_INVALID_PARAMETER) {
584 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
585 } else if (ShellStatus == SHELL_ABORTED) {
586 //
587 // Ignore aborting.
588 //
589 } else if (ShellStatus != SHELL_SUCCESS) {
590 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
591 }
592 }
593 }
594 }
595 }
596
597 if (FullPath != NULL) {
598 FreePool(FullPath);
599 }
600 //
601 // free the command line package
602 //
603 ShellCommandLineFreeVarList (Package);
604
605 return (ShellStatus);
606 }