1 /** @file
2   Main file for ls shell level 2 function.
3 
4   (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "UefiShellLevel2CommandsLib.h"
11 #include <Guid/FileSystemInfo.h>
12 
13 UINTN     mDayOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30};
14 
15 /**
16   print out the standard format output volume entry.
17 
18   @param[in] TheList           a list of files from the volume.
19 **/
20 EFI_STATUS
PrintSfoVolumeInfoTableEntry(IN CONST EFI_SHELL_FILE_INFO * TheList)21 PrintSfoVolumeInfoTableEntry(
22   IN CONST EFI_SHELL_FILE_INFO *TheList
23   )
24 {
25   EFI_STATUS            Status;
26   EFI_SHELL_FILE_INFO   *Node;
27   CHAR16                *DirectoryName;
28   EFI_FILE_SYSTEM_INFO  *SysInfo;
29   UINTN                 SysInfoSize;
30   SHELL_FILE_HANDLE     ShellFileHandle;
31   EFI_FILE_PROTOCOL     *EfiFpHandle;
32 
33   //
34   // Get the first valid handle (directories)
35   //
36   for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&TheList->Link)
37       ; !IsNull(&TheList->Link, &Node->Link) && Node->Handle == NULL
38       ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&TheList->Link, &Node->Link)
39      );
40 
41   if (Node->Handle == NULL) {
42     DirectoryName = GetFullyQualifiedPath(((EFI_SHELL_FILE_INFO *)GetFirstNode(&TheList->Link))->FullName);
43 
44     //
45     // We need to open something up to get system information
46     //
47     Status = gEfiShellProtocol->OpenFileByName(
48       DirectoryName,
49       &ShellFileHandle,
50       EFI_FILE_MODE_READ
51       );
52 
53     ASSERT_EFI_ERROR(Status);
54     FreePool(DirectoryName);
55 
56     //
57     // Get the Volume Info from ShellFileHandle
58     //
59     SysInfo     = NULL;
60     SysInfoSize = 0;
61     EfiFpHandle = ConvertShellHandleToEfiFileProtocol(ShellFileHandle);
62     Status = EfiFpHandle->GetInfo(
63       EfiFpHandle,
64       &gEfiFileSystemInfoGuid,
65       &SysInfoSize,
66       SysInfo
67       );
68 
69     if (Status == EFI_BUFFER_TOO_SMALL) {
70       SysInfo = AllocateZeroPool(SysInfoSize);
71       Status = EfiFpHandle->GetInfo(
72         EfiFpHandle,
73         &gEfiFileSystemInfoGuid,
74         &SysInfoSize,
75         SysInfo
76         );
77     }
78 
79     ASSERT_EFI_ERROR(Status);
80 
81     gEfiShellProtocol->CloseFile(ShellFileHandle);
82   } else {
83     //
84     // Get the Volume Info from Node->Handle
85     //
86     SysInfo = NULL;
87     SysInfoSize = 0;
88     EfiFpHandle = ConvertShellHandleToEfiFileProtocol(Node->Handle);
89     Status = EfiFpHandle->GetInfo(
90       EfiFpHandle,
91       &gEfiFileSystemInfoGuid,
92       &SysInfoSize,
93       SysInfo
94       );
95 
96     if (Status == EFI_BUFFER_TOO_SMALL) {
97       SysInfo = AllocateZeroPool(SysInfoSize);
98       Status = EfiFpHandle->GetInfo(
99         EfiFpHandle,
100         &gEfiFileSystemInfoGuid,
101         &SysInfoSize,
102         SysInfo
103         );
104     }
105 
106     ASSERT_EFI_ERROR(Status);
107   }
108 
109   ShellPrintHiiEx (
110     -1,
111     -1,
112     NULL,
113     STRING_TOKEN (STR_GEN_SFO_HEADER),
114     gShellLevel2HiiHandle,
115     L"ls"
116     );
117   //
118   // print VolumeInfo table
119   //
120   ASSERT(SysInfo != NULL);
121   ShellPrintHiiEx (
122     0,
123     gST->ConOut->Mode->CursorRow,
124     NULL,
125     STRING_TOKEN (STR_LS_SFO_VOLINFO),
126     gShellLevel2HiiHandle,
127     SysInfo->VolumeLabel,
128     SysInfo->VolumeSize,
129     SysInfo->ReadOnly?L"TRUE":L"FALSE",
130     SysInfo->FreeSpace,
131     SysInfo->BlockSize
132     );
133 
134   SHELL_FREE_NON_NULL(SysInfo);
135 
136   return (Status);
137 }
138 
139 /**
140   print out the info on a single file.
141 
142   @param[in] Sfo      TRUE if in SFO, false otherwise.
143   @param[in] TheNode  the EFI_SHELL_FILE_INFO node to print out information on.
144   @param[in] Files    incremented if a file is printed.
145   @param[in] Size     incremented by file size.
146   @param[in] Dirs     incremented if a directory is printed.
147 
148 **/
149 VOID
PrintFileInformation(IN CONST BOOLEAN Sfo,IN CONST EFI_SHELL_FILE_INFO * TheNode,IN UINT64 * Files,IN UINT64 * Size,IN UINT64 * Dirs)150 PrintFileInformation(
151   IN CONST BOOLEAN              Sfo,
152   IN CONST EFI_SHELL_FILE_INFO  *TheNode,
153   IN UINT64                     *Files,
154   IN UINT64                     *Size,
155   IN UINT64                     *Dirs
156   )
157 {
158   ASSERT(Files    != NULL);
159   ASSERT(Size     != NULL);
160   ASSERT(Dirs     != NULL);
161   ASSERT(TheNode  != NULL);
162 
163   if (Sfo) {
164     //
165     // Print the FileInfo Table
166     //
167     ShellPrintHiiEx (
168       0,
169       gST->ConOut->Mode->CursorRow,
170       NULL,
171       STRING_TOKEN (STR_LS_SFO_FILEINFO),
172       gShellLevel2HiiHandle,
173       TheNode->FullName,
174       TheNode->Info->FileSize,
175       TheNode->Info->PhysicalSize,
176       (TheNode->Info->Attribute & EFI_FILE_ARCHIVE)   != 0?L"a":L"",
177       (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"d":L"",
178       (TheNode->Info->Attribute & EFI_FILE_HIDDEN)    != 0?L"h":L"",
179       (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L"r":L"",
180       (TheNode->Info->Attribute & EFI_FILE_SYSTEM)    != 0?L"s":L"",
181       TheNode->Info->CreateTime.Hour,
182       TheNode->Info->CreateTime.Minute,
183       TheNode->Info->CreateTime.Second,
184       TheNode->Info->CreateTime.Day,
185       TheNode->Info->CreateTime.Month,
186       TheNode->Info->CreateTime.Year,
187       TheNode->Info->LastAccessTime.Hour,
188       TheNode->Info->LastAccessTime.Minute,
189       TheNode->Info->LastAccessTime.Second,
190       TheNode->Info->LastAccessTime.Day,
191       TheNode->Info->LastAccessTime.Month,
192       TheNode->Info->LastAccessTime.Year,
193       TheNode->Info->ModificationTime.Hour,
194       TheNode->Info->ModificationTime.Minute,
195       TheNode->Info->ModificationTime.Second,
196       TheNode->Info->ModificationTime.Day,
197       TheNode->Info->ModificationTime.Month,
198       TheNode->Info->ModificationTime.Year
199       );
200   } else {
201     //
202     // print this one out...
203     // first print the universal start, next print the type specific name format, last print the CRLF
204     //
205     ShellPrintHiiEx (
206       -1,
207       -1,
208       NULL,
209       STRING_TOKEN (STR_LS_LINE_START_ALL),
210       gShellLevel2HiiHandle,
211       &TheNode->Info->ModificationTime,
212       (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"<DIR>":L"",
213       (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L'r':L' ',
214       TheNode->Info->FileSize
215       );
216     if (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) {
217       (*Dirs)++;
218       ShellPrintHiiEx (
219         -1,
220         -1,
221         NULL,
222         STRING_TOKEN (STR_LS_LINE_END_DIR),
223         gShellLevel2HiiHandle,
224         TheNode->FileName
225         );
226     } else {
227       (*Files)++;
228       (*Size) += TheNode->Info->FileSize;
229       if ( (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".nsh", (CHAR16*)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
230         || (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".efi", (CHAR16*)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
231        ){
232         ShellPrintHiiEx (
233           -1,
234           -1,
235           NULL,
236           STRING_TOKEN (STR_LS_LINE_END_EXE),
237           gShellLevel2HiiHandle,
238           TheNode->FileName
239           );
240       } else {
241         ShellPrintHiiEx (
242           -1,
243           -1,
244           NULL,
245           STRING_TOKEN (STR_LS_LINE_END_FILE),
246           gShellLevel2HiiHandle,
247           TheNode->FileName
248           );
249       }
250     }
251   }
252 }
253 
254 /**
255   print out the header when not using standard format output.
256 
257   @param[in] Path           String with starting path.
258 **/
259 VOID
PrintNonSfoHeader(IN CONST CHAR16 * Path)260 PrintNonSfoHeader(
261   IN CONST CHAR16 *Path
262   )
263 {
264   CHAR16 *DirectoryName;
265 
266   //
267   // get directory name from path...
268   //
269   DirectoryName = GetFullyQualifiedPath(Path);
270 
271   if (DirectoryName != NULL) {
272     //
273     // print header
274     //
275     ShellPrintHiiEx (
276       0,
277       gST->ConOut->Mode->CursorRow,
278       NULL,
279       STRING_TOKEN (STR_LS_HEADER_LINE1),
280       gShellLevel2HiiHandle,
281       DirectoryName
282       );
283 
284     SHELL_FREE_NON_NULL(DirectoryName);
285   }
286 }
287 
288 /**
289   print out the footer when not using standard format output.
290 
291   @param[in] Files            The number of files.
292   @param[in] Size             The size of files in bytes.
293   @param[in] Dirs             The number of directories.
294 **/
295 VOID
PrintNonSfoFooter(IN UINT64 Files,IN UINT64 Size,IN UINT64 Dirs)296 PrintNonSfoFooter(
297   IN UINT64                     Files,
298   IN UINT64                     Size,
299   IN UINT64                     Dirs
300   )
301 {
302   //
303   // print footer
304   //
305   ShellPrintHiiEx (
306     -1,
307     -1,
308     NULL,
309     STRING_TOKEN (STR_LS_FOOTER_LINE),
310     gShellLevel2HiiHandle,
311     Files,
312     Size,
313     Dirs
314    );
315 }
316 
317 /**
318   Change the file time to local time based on the timezone.
319 
320   @param[in] Time               The file time.
321   @param[in] LocalTimeZone      Local time zone.
322 **/
323 VOID
FileTimeToLocalTime(IN EFI_TIME * Time,IN INT16 LocalTimeZone)324 FileTimeToLocalTime (
325   IN EFI_TIME             *Time,
326   IN INT16                LocalTimeZone
327   )
328 {
329   INTN                    MinuteDiff;
330   INTN                    TempMinute;
331   INTN                    HourNumberOfTempMinute;
332   INTN                    TempHour;
333   INTN                    DayNumberOfTempHour;
334   INTN                    TempDay;
335   INTN                    MonthNumberOfTempDay;
336   INTN                    TempMonth;
337   INTN                    YearNumberOfTempMonth;
338   INTN                    MonthRecord;
339 
340   ASSERT ((Time->TimeZone >= -1440) && (Time->TimeZone <=1440));
341   ASSERT ((LocalTimeZone >= -1440) && (LocalTimeZone <=1440));
342   ASSERT ((Time->Month >= 1) && (Time->Month <= 12));
343 
344   if(Time->TimeZone == LocalTimeZone) {
345     //
346     //if the file timezone is equal to the local timezone, there is no need to adjust the file time.
347     //
348     return;
349   }
350 
351   if((Time->Year % 4 == 0 && Time->Year / 100 != 0)||(Time->Year % 400 == 0)) {
352     //
353     // Day in February of leap year is 29.
354     //
355     mDayOfMonth[1] = 29;
356   }
357 
358   MinuteDiff = Time->TimeZone - LocalTimeZone;
359   TempMinute = Time->Minute + MinuteDiff;
360 
361   //
362   // Calculate Time->Minute
363   // TempHour will be used to calculate Time->Hour
364   //
365   HourNumberOfTempMinute = TempMinute / 60;
366   if(TempMinute < 0) {
367     HourNumberOfTempMinute --;
368   }
369   TempHour = Time->Hour + HourNumberOfTempMinute;
370   Time->Minute = (UINT8)(TempMinute - 60 * HourNumberOfTempMinute);
371 
372   //
373   // Calculate Time->Hour
374   // TempDay will be used to calculate Time->Day
375   //
376   DayNumberOfTempHour = TempHour / 24 ;
377   if(TempHour < 0){
378     DayNumberOfTempHour--;
379   }
380   TempDay = Time->Day + DayNumberOfTempHour;
381   Time->Hour = (UINT8)(TempHour - 24 * DayNumberOfTempHour);
382 
383   //
384   // Calculate Time->Day
385   // TempMonth will be used to calculate Time->Month
386   //
387   MonthNumberOfTempDay = (TempDay - 1) / (INTN)mDayOfMonth[Time->Month - 1];
388   MonthRecord = (INTN)(Time->Month) ;
389   if(TempDay - 1 < 0){
390     MonthNumberOfTempDay -- ;
391     MonthRecord -- ;
392   }
393   TempMonth = Time->Month + MonthNumberOfTempDay;
394   Time->Day = (UINT8)(TempDay - (INTN)mDayOfMonth[(MonthRecord - 1 + 12) % 12] * MonthNumberOfTempDay);
395 
396   //
397   // Calculate Time->Month, Time->Year
398   //
399   YearNumberOfTempMonth = (TempMonth - 1) / 12;
400   if(TempMonth - 1 < 0){
401     YearNumberOfTempMonth --;
402   }
403   Time->Month = (UINT8)(TempMonth - 12 * (YearNumberOfTempMonth));
404   Time->Year = (UINT16)(Time->Year + YearNumberOfTempMonth);
405 }
406 
407 /**
408   print out the list of files and directories from the LS command
409 
410   @param[in] Rec            TRUE to automatically recurse into each found directory
411                             FALSE to only list the specified directory.
412   @param[in] Attribs        List of required Attribute for display.
413                             If 0 then all non-system and non-hidden files will be printed.
414   @param[in] Sfo            TRUE to use Standard Format Output, FALSE otherwise
415   @param[in] RootPath       String with starting path to search in.
416   @param[in] SearchString   String with search string.
417   @param[in] Found          Set to TRUE, if anyone were found.
418   @param[in] Count          The count of bits enabled in Attribs.
419   @param[in] TimeZone       The current time zone offset.
420 
421   @retval SHELL_SUCCESS     the printing was sucessful.
422 **/
423 SHELL_STATUS
PrintLsOutput(IN CONST BOOLEAN Rec,IN CONST UINT64 Attribs,IN CONST BOOLEAN Sfo,IN CONST CHAR16 * RootPath,IN CONST CHAR16 * SearchString,IN BOOLEAN * Found,IN CONST UINTN Count,IN CONST INT16 TimeZone)424 PrintLsOutput(
425   IN CONST BOOLEAN Rec,
426   IN CONST UINT64  Attribs,
427   IN CONST BOOLEAN Sfo,
428   IN CONST CHAR16  *RootPath,
429   IN CONST CHAR16  *SearchString,
430   IN       BOOLEAN *Found,
431   IN CONST UINTN   Count,
432   IN CONST INT16   TimeZone
433   )
434 {
435   EFI_STATUS            Status;
436   EFI_SHELL_FILE_INFO   *ListHead;
437   EFI_SHELL_FILE_INFO   *Node;
438   SHELL_STATUS          ShellStatus;
439   UINT64                FileCount;
440   UINT64                DirCount;
441   UINT64                FileSize;
442   UINTN                 LongestPath;
443   CHAR16                *CorrectedPath;
444   BOOLEAN               FoundOne;
445   BOOLEAN               HeaderPrinted;
446   EFI_TIME              LocalTime;
447 
448   HeaderPrinted = FALSE;
449   FileCount     = 0;
450   DirCount      = 0;
451   FileSize      = 0;
452   ListHead      = NULL;
453   ShellStatus   = SHELL_SUCCESS;
454   LongestPath   = 0;
455   CorrectedPath = NULL;
456 
457   if (Found != NULL) {
458     FoundOne = *Found;
459   } else {
460     FoundOne = FALSE;
461   }
462 
463   CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, RootPath,     0);
464   if (CorrectedPath == NULL) {
465     return SHELL_OUT_OF_RESOURCES;
466   }
467   if (CorrectedPath[StrLen(CorrectedPath)-1] != L'\\'
468     &&CorrectedPath[StrLen(CorrectedPath)-1] != L'/') {
469     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"\\",     0);
470   }
471   CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, SearchString, 0);
472   if (CorrectedPath == NULL) {
473     return (SHELL_OUT_OF_RESOURCES);
474   }
475 
476   PathCleanUpDirectories(CorrectedPath);
477 
478   Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
479   if (!EFI_ERROR(Status)) {
480     if (ListHead == NULL || IsListEmpty(&ListHead->Link)) {
481       SHELL_FREE_NON_NULL(CorrectedPath);
482       return (SHELL_SUCCESS);
483     }
484 
485     if (Sfo && Found == NULL) {
486       PrintSfoVolumeInfoTableEntry(ListHead);
487     }
488 
489     for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link), LongestPath = 0
490         ; !IsNull(&ListHead->Link, &Node->Link)
491         ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
492         ){
493       if (ShellGetExecutionBreakFlag ()) {
494         ShellStatus = SHELL_ABORTED;
495         break;
496       }
497       ASSERT(Node != NULL);
498 
499       //
500       // Change the file time to local time.
501       //
502       Status = gRT->GetTime(&LocalTime, NULL);
503       if (!EFI_ERROR (Status)) {
504         if ((Node->Info->CreateTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
505             (Node->Info->CreateTime.Month >= 1 && Node->Info->CreateTime.Month <= 12)) {
506           //
507           // FileTimeToLocalTime () requires Month is in a valid range, other buffer out-of-band access happens.
508           //
509           FileTimeToLocalTime (&Node->Info->CreateTime, LocalTime.TimeZone);
510         }
511         if ((Node->Info->LastAccessTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
512             (Node->Info->LastAccessTime.Month >= 1 && Node->Info->LastAccessTime.Month <= 12)) {
513           FileTimeToLocalTime (&Node->Info->LastAccessTime, LocalTime.TimeZone);
514         }
515         if ((Node->Info->ModificationTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
516             (Node->Info->ModificationTime.Month >= 1 && Node->Info->ModificationTime.Month <= 12)) {
517           FileTimeToLocalTime (&Node->Info->ModificationTime, LocalTime.TimeZone);
518         }
519       }
520 
521       if (LongestPath < StrSize(Node->FullName)) {
522         LongestPath = StrSize(Node->FullName);
523       }
524       ASSERT(Node->Info != NULL);
525       ASSERT((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);
526       if (Attribs == 0) {
527         //
528         // NOT system & NOT hidden
529         //
530         if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)
531           || (Node->Info->Attribute & EFI_FILE_HIDDEN)
532          ){
533           continue;
534         }
535       } else if ((Attribs != EFI_FILE_VALID_ATTR) ||
536                  (Count == 5)) {
537         //
538         // Only matches the bits which "Attribs" contains, not
539         // all files/directories with any of the bits.
540         // Count == 5 is used to tell the difference between a user
541         // specifying all bits (EX: -arhsda) and just specifying
542         // -a (means display all files with any attribute).
543         //
544         if ( (Node->Info->Attribute & Attribs) != Attribs) {
545           continue;
546         }
547       }
548 
549       if (!Sfo && !HeaderPrinted) {
550         PathRemoveLastItem (CorrectedPath);
551         PrintNonSfoHeader(CorrectedPath);
552       }
553       PrintFileInformation(Sfo, Node, &FileCount, &FileSize, &DirCount);
554       FoundOne = TRUE;
555       HeaderPrinted = TRUE;
556     }
557 
558     if (!Sfo && ShellStatus != SHELL_ABORTED) {
559       PrintNonSfoFooter(FileCount, FileSize, DirCount);
560     }
561   }
562 
563   if (Rec && ShellStatus != SHELL_ABORTED) {
564     //
565     // Re-Open all the files under the starting path for directories that didnt necessarily match our file filter
566     //
567     ShellCloseFileMetaArg(&ListHead);
568     CorrectedPath[0] = CHAR_NULL;
569     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, RootPath, 0);
570     if (CorrectedPath == NULL) {
571       return SHELL_OUT_OF_RESOURCES;
572     }
573     if (CorrectedPath[StrLen(CorrectedPath)-1] != L'\\'
574       &&CorrectedPath[StrLen(CorrectedPath)-1] != L'/') {
575       CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"\\",     0);
576     }
577     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"*",     0);
578     Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
579 
580     if (!EFI_ERROR(Status)) {
581       for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
582           ; !IsNull(&ListHead->Link, &Node->Link) && ShellStatus == SHELL_SUCCESS
583           ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
584          ){
585         if (ShellGetExecutionBreakFlag ()) {
586           ShellStatus = SHELL_ABORTED;
587           break;
588         }
589 
590         //
591         // recurse on any directory except the traversing ones...
592         //
593         if (((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)
594           && StrCmp(Node->FileName, L".") != 0
595           && StrCmp(Node->FileName, L"..") != 0
596          ){
597           ShellStatus = PrintLsOutput(
598             Rec,
599             Attribs,
600             Sfo,
601             Node->FullName,
602             SearchString,
603             &FoundOne,
604             Count,
605             TimeZone);
606 
607           //
608           // Since it's running recursively, we have to break immediately when returned SHELL_ABORTED
609           //
610           if (ShellStatus == SHELL_ABORTED) {
611             break;
612           }
613         }
614       }
615     }
616   }
617 
618   SHELL_FREE_NON_NULL(CorrectedPath);
619   ShellCloseFileMetaArg(&ListHead);
620 
621   if (Found == NULL && !FoundOne) {
622     return (SHELL_NOT_FOUND);
623   }
624 
625   if (Found != NULL) {
626     *Found = FoundOne;
627   }
628 
629   return (ShellStatus);
630 }
631 
632 STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
633   {L"-r", TypeFlag},
634   {L"-a", TypeStart},
635   {L"-sfo", TypeFlag},
636   {NULL, TypeMax}
637   };
638 
639 /**
640   Function for 'ls' command.
641 
642   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
643   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
644 **/
645 SHELL_STATUS
646 EFIAPI
ShellCommandRunLs(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)647 ShellCommandRunLs (
648   IN EFI_HANDLE        ImageHandle,
649   IN EFI_SYSTEM_TABLE  *SystemTable
650   )
651 {
652   EFI_STATUS    Status;
653   LIST_ENTRY    *Package;
654   CHAR16        *ProblemParam;
655   CONST CHAR16  *Attribs;
656   SHELL_STATUS  ShellStatus;
657   UINT64        RequiredAttributes;
658   CONST CHAR16  *PathName;
659   CONST CHAR16  *CurDir;
660   UINTN         Count;
661   CHAR16        *FullPath;
662   UINTN         Size;
663   EFI_TIME      TheTime;
664   CHAR16        *SearchString;
665 
666   Size                = 0;
667   FullPath            = NULL;
668   ProblemParam        = NULL;
669   Attribs             = NULL;
670   ShellStatus         = SHELL_SUCCESS;
671   RequiredAttributes  = 0;
672   PathName            = NULL;
673   SearchString        = NULL;
674   CurDir              = NULL;
675   Count               = 0;
676 
677   //
678   // initialize the shell lib (we must be in non-auto-init...)
679   //
680   Status = ShellInitialize();
681   ASSERT_EFI_ERROR(Status);
682 
683   //
684   // Fix local copies of the protocol pointers
685   //
686   Status = CommandInit();
687   ASSERT_EFI_ERROR(Status);
688 
689   //
690   // parse the command line
691   //
692   Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
693   if (EFI_ERROR(Status)) {
694     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
695       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"ls", ProblemParam);
696       FreePool(ProblemParam);
697       ShellStatus = SHELL_INVALID_PARAMETER;
698     } else {
699       ASSERT(FALSE);
700     }
701   } else {
702     //
703     // check for "-?"
704     //
705     if (ShellCommandLineGetFlag(Package, L"-?")) {
706       ASSERT(FALSE);
707     }
708 
709     if (ShellCommandLineGetCount(Package) > 2) {
710       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"ls");
711       ShellStatus = SHELL_INVALID_PARAMETER;
712     } else {
713       //
714       // check for -a
715       //
716       if (ShellCommandLineGetFlag(Package, L"-a")) {
717         for ( Attribs = ShellCommandLineGetValue(Package, L"-a")
718             ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
719             ; Attribs++
720            ){
721           switch (*Attribs) {
722             case L'a':
723             case L'A':
724               RequiredAttributes |= EFI_FILE_ARCHIVE;
725               Count++;
726               continue;
727             case L's':
728             case L'S':
729               RequiredAttributes |= EFI_FILE_SYSTEM;
730               Count++;
731               continue;
732             case L'h':
733             case L'H':
734               RequiredAttributes |= EFI_FILE_HIDDEN;
735               Count++;
736               continue;
737             case L'r':
738             case L'R':
739               RequiredAttributes |= EFI_FILE_READ_ONLY;
740               Count++;
741               continue;
742             case L'd':
743             case L'D':
744               RequiredAttributes |= EFI_FILE_DIRECTORY;
745               Count++;
746               continue;
747             default:
748               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, L"ls", ShellCommandLineGetValue(Package, L"-a"));
749               ShellStatus = SHELL_INVALID_PARAMETER;
750               break;
751           } // switch
752         } // for loop
753         //
754         // if nothing is specified all are specified
755         //
756         if (RequiredAttributes == 0) {
757           RequiredAttributes = EFI_FILE_VALID_ATTR;
758         }
759       } // if -a present
760       if (ShellStatus == SHELL_SUCCESS) {
761         PathName = ShellCommandLineGetRawValue(Package, 1);
762         if (PathName == NULL) {
763           //
764           // Nothing specified... must start from current directory
765           //
766           CurDir = gEfiShellProtocol->GetCurDir(NULL);
767           if (CurDir == NULL) {
768             ShellStatus = SHELL_NOT_FOUND;
769             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
770           }
771           //
772           // Copy to the 2 strings for starting path and file search string
773           //
774           ASSERT(SearchString == NULL);
775           ASSERT(FullPath == NULL);
776           StrnCatGrow(&SearchString, NULL, L"*", 0);
777           StrnCatGrow(&FullPath, NULL, CurDir, 0);
778           Size = FullPath != NULL? StrSize(FullPath) : 0;
779           StrnCatGrow(&FullPath, &Size, L"\\", 0);
780         } else {
781           if (StrStr(PathName, L":") == NULL && gEfiShellProtocol->GetCurDir(NULL) == NULL) {
782             //
783             // If we got something and it doesnt have a fully qualified path, then we needed to have a CWD.
784             //
785             ShellStatus = SHELL_NOT_FOUND;
786             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
787           } else {
788             //
789             // We got a valid fully qualified path or we have a CWD
790             //
791             ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));
792             if (StrStr(PathName, L":") == NULL) {
793               StrnCatGrow(&FullPath, &Size, gEfiShellProtocol->GetCurDir(NULL), 0);
794               if (FullPath == NULL) {
795                 ShellCommandLineFreeVarList (Package);
796                 return SHELL_OUT_OF_RESOURCES;
797               }
798               Size = FullPath != NULL? StrSize(FullPath) : 0;
799               StrnCatGrow(&FullPath, &Size, L"\\", 0);
800             }
801             StrnCatGrow(&FullPath, &Size, PathName, 0);
802             if (FullPath == NULL) {
803                 ShellCommandLineFreeVarList (Package);
804                 return SHELL_OUT_OF_RESOURCES;
805             }
806 
807             if  (ShellIsDirectory(PathName) == EFI_SUCCESS) {
808               //
809               // is listing ends with a directory, then we list all files in that directory
810               //
811               StrnCatGrow(&SearchString, NULL, L"*", 0);
812             } else {
813               //
814               // must split off the search part that applies to files from the end of the directory part
815               //
816               StrnCatGrow(&SearchString, NULL, FullPath, 0);
817               if (SearchString == NULL) {
818                 FreePool (FullPath);
819                 ShellCommandLineFreeVarList (Package);
820                 return SHELL_OUT_OF_RESOURCES;
821               }
822               PathRemoveLastItem (FullPath);
823               CopyMem (SearchString, SearchString + StrLen (FullPath), StrSize (SearchString + StrLen (FullPath)));
824             }
825           }
826         }
827         Status = gRT->GetTime(&TheTime, NULL);
828         if (EFI_ERROR(Status)) {
829           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN), gShellLevel2HiiHandle, L"ls", L"gRT->GetTime", Status);
830           TheTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
831         }
832 
833         if (ShellStatus == SHELL_SUCCESS) {
834           ShellStatus = PrintLsOutput(
835             ShellCommandLineGetFlag(Package, L"-r"),
836             RequiredAttributes,
837             ShellCommandLineGetFlag(Package, L"-sfo"),
838             FullPath,
839             SearchString,
840             NULL,
841             Count,
842             TheTime.TimeZone
843            );
844           if (ShellStatus == SHELL_NOT_FOUND) {
845             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LS_FILE_NOT_FOUND), gShellLevel2HiiHandle, L"ls", FullPath);
846           } else if (ShellStatus == SHELL_INVALID_PARAMETER) {
847             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);
848           } else if (ShellStatus == SHELL_ABORTED) {
849             //
850             // Ignore aborting.
851             //
852           } else if (ShellStatus != SHELL_SUCCESS) {
853             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);
854           }
855         }
856       }
857     }
858   }
859 
860   //
861   // Free memory allocated
862   //
863   SHELL_FREE_NON_NULL(SearchString);
864   SHELL_FREE_NON_NULL(FullPath);
865   ShellCommandLineFreeVarList (Package);
866 
867   return (ShellStatus);
868 }
869