]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
ShellPkg: Fixes for the ‘ls’ command:
[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 FreePool(ListHead);
397 return (ShellStatus);
398 }
399
400 STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
401 {L"-r", TypeFlag},
402 {L"-a", TypeStart},
403 {L"-sfo", TypeFlag},
404 {NULL, TypeMax}
405 };
406
407 /**
408 Function for 'ls' command.
409
410 @param[in] ImageHandle Handle to the Image (NULL if Internal).
411 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
412 **/
413 SHELL_STATUS
414 EFIAPI
415 ShellCommandRunLs (
416 IN EFI_HANDLE ImageHandle,
417 IN EFI_SYSTEM_TABLE *SystemTable
418 )
419 {
420 EFI_STATUS Status;
421 LIST_ENTRY *Package;
422 CHAR16 *ProblemParam;
423 CONST CHAR16 *Attribs;
424 SHELL_STATUS ShellStatus;
425 UINT64 RequiredAttributes;
426 CONST CHAR16 *PathName;
427 CONST CHAR16 *CurDir;
428 UINTN Count;
429 CHAR16 *FullPath;
430 UINTN Size;
431 EFI_TIME TheTime;
432 BOOLEAN SfoMode;
433
434 Size = 0;
435 FullPath = NULL;
436 ProblemParam = NULL;
437 Attribs = NULL;
438 ShellStatus = SHELL_SUCCESS;
439 RequiredAttributes = 0;
440 PathName = NULL;
441 CurDir = NULL;
442 Count = 0;
443
444 //
445 // initialize the shell lib (we must be in non-auto-init...)
446 //
447 Status = ShellInitialize();
448 ASSERT_EFI_ERROR(Status);
449
450 //
451 // Fix local copies of the protocol pointers
452 //
453 Status = CommandInit();
454 ASSERT_EFI_ERROR(Status);
455
456 //
457 // parse the command line
458 //
459 Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
460 if (EFI_ERROR(Status)) {
461 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
462 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, ProblemParam);
463 FreePool(ProblemParam);
464 ShellStatus = SHELL_INVALID_PARAMETER;
465 } else {
466 ASSERT(FALSE);
467 }
468 } else {
469 //
470 // check for "-?"
471 //
472 if (ShellCommandLineGetFlag(Package, L"-?")) {
473 ASSERT(FALSE);
474 }
475
476 if (ShellCommandLineGetCount(Package) > 2) {
477 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle);
478 ShellStatus = SHELL_INVALID_PARAMETER;
479 } else {
480 //
481 // check for -a
482 //
483 if (ShellCommandLineGetFlag(Package, L"-a")) {
484 for ( Attribs = ShellCommandLineGetValue(Package, L"-a")
485 ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
486 ; Attribs++
487 ){
488 switch (*Attribs) {
489 case L'a':
490 case L'A':
491 RequiredAttributes |= EFI_FILE_ARCHIVE;
492 Count++;
493 continue;
494 case L's':
495 case L'S':
496 RequiredAttributes |= EFI_FILE_SYSTEM;
497 Count++;
498 continue;
499 case L'h':
500 case L'H':
501 RequiredAttributes |= EFI_FILE_HIDDEN;
502 Count++;
503 continue;
504 case L'r':
505 case L'R':
506 RequiredAttributes |= EFI_FILE_READ_ONLY;
507 Count++;
508 continue;
509 case L'd':
510 case L'D':
511 RequiredAttributes |= EFI_FILE_DIRECTORY;
512 Count++;
513 continue;
514 default:
515 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, ShellCommandLineGetValue(Package, L"-a"));
516 ShellStatus = SHELL_INVALID_PARAMETER;
517 break;
518 } // switch
519 } // for loop
520 //
521 // if nothing is specified all are specified
522 //
523 if (RequiredAttributes == 0) {
524 RequiredAttributes = EFI_FILE_VALID_ATTR;
525 }
526 } // if -a present
527 if (ShellStatus == SHELL_SUCCESS) {
528 PathName = ShellCommandLineGetRawValue(Package, 1);
529 if (PathName == NULL) {
530 CurDir = gEfiShellProtocol->GetCurDir(NULL);
531 if (CurDir == NULL) {
532 ShellStatus = SHELL_NOT_FOUND;
533 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
534 }
535 }
536 if (PathName != NULL) {
537 if (StrStr(PathName, L":") == NULL && gEfiShellProtocol->GetCurDir(NULL) == NULL) {
538 ShellStatus = SHELL_NOT_FOUND;
539 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
540 } else {
541 ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));
542 StrnCatGrow(&FullPath, &Size, PathName, 0);
543 if (ShellIsDirectory(PathName) == EFI_SUCCESS) {
544 if (PathName[StrLen (PathName) - 1] == '\\') {
545 //
546 // For path ending with '\', just append '*'.
547 //
548 StrnCatGrow (&FullPath, &Size, L"*", 0);
549 } else if (PathName[StrLen (PathName) - 1] == '*') {
550 //
551 // For path ending with '*', do nothing.
552 //
553 } else {
554 //
555 // Otherwise, append '\*' to directory name.
556 //
557 StrnCatGrow (&FullPath, &Size, L"\\*", 0);
558 }
559 }
560 }
561 } else {
562 ASSERT(FullPath == NULL);
563 StrnCatGrow(&FullPath, NULL, L"*", 0);
564 }
565 Status = gRT->GetTime(&TheTime, NULL);
566 if (EFI_ERROR(Status)) {
567 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN), gShellLevel2HiiHandle, L"gRT->GetTime", Status);
568 TheTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
569 }
570
571 SfoMode = ShellCommandLineGetFlag(Package, L"-sfo");
572 if (ShellStatus == SHELL_SUCCESS) {
573 ShellStatus = PrintLsOutput(
574 ShellCommandLineGetFlag(Package, L"-r"),
575 RequiredAttributes,
576 SfoMode,
577 FullPath,
578 TRUE,
579 Count,
580 (INT16)(TheTime.TimeZone==EFI_UNSPECIFIED_TIMEZONE?0:TheTime.TimeZone)
581 );
582 if (ShellStatus == SHELL_NOT_FOUND) {
583 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LS_FILE_NOT_FOUND), gShellLevel2HiiHandle);
584 } else if (ShellStatus == SHELL_INVALID_PARAMETER) {
585 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
586 } else if (ShellStatus == SHELL_ABORTED) {
587 //
588 // Ignore aborting.
589 //
590 } else if (ShellStatus != SHELL_SUCCESS) {
591 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
592 }
593 }
594 }
595 }
596 }
597
598 if (FullPath != NULL) {
599 FreePool(FullPath);
600 }
601 //
602 // free the command line package
603 //
604 ShellCommandLineFreeVarList (Package);
605
606 return (ShellStatus);
607 }