1 /** @file
2   Functions for performing directory entry io.
3 
4 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "Fat.h"
10 
11 /**
12 
13   Get a directory entry from disk for the Ofile.
14 
15   @param  Parent                - The parent of the OFile which need to update.
16   @param  IoMode                - Indicate whether to read directory entry or write directroy entry.
17   @param  EntryPos              - The position of the directory entry to be accessed.
18   @param  Entry                 - The directory entry read or written.
19 
20   @retval EFI_SUCCESS           - Access the directory entry sucessfully.
21   @return other                 - An error occurred when reading the directory entry.
22 
23 **/
24 STATIC
25 EFI_STATUS
FatAccessEntry(IN FAT_OFILE * Parent,IN IO_MODE IoMode,IN UINTN EntryPos,IN OUT VOID * Entry)26 FatAccessEntry (
27   IN     FAT_OFILE            *Parent,
28   IN     IO_MODE              IoMode,
29   IN     UINTN                EntryPos,
30   IN OUT VOID                 *Entry
31   )
32 {
33   UINTN Position;
34   UINTN BufferSize;
35 
36   Position = EntryPos * sizeof (FAT_DIRECTORY_ENTRY);
37   if (Position >= Parent->FileSize) {
38     //
39     // End of directory
40     //
41     ASSERT (IoMode == ReadData);
42     ((FAT_DIRECTORY_ENTRY *) Entry)->FileName[0] = EMPTY_ENTRY_MARK;
43     ((FAT_DIRECTORY_ENTRY *) Entry)->Attributes  = 0;
44     return EFI_SUCCESS;
45   }
46 
47   BufferSize = sizeof (FAT_DIRECTORY_ENTRY);
48   return FatAccessOFile (Parent, IoMode, Position, &BufferSize, Entry, NULL);
49 }
50 
51 /**
52 
53   Save the directory entry to disk.
54 
55   @param  OFile                 - The parent OFile which needs to update.
56   @param  DirEnt                - The directory entry to be saved.
57 
58   @retval EFI_SUCCESS           - Store the directory entry successfully.
59   @return other                 - An error occurred when writing the directory entry.
60 
61 **/
62 EFI_STATUS
FatStoreDirEnt(IN FAT_OFILE * OFile,IN FAT_DIRENT * DirEnt)63 FatStoreDirEnt (
64   IN FAT_OFILE            *OFile,
65   IN FAT_DIRENT           *DirEnt
66   )
67 {
68   EFI_STATUS        Status;
69   FAT_DIRECTORY_LFN LfnEntry;
70   UINTN             EntryPos;
71   CHAR16            *LfnBufferPointer;
72   CHAR16            LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1];
73   UINT8             EntryCount;
74   UINT8             LfnOrdinal;
75 
76   EntryPos   = DirEnt->EntryPos;
77   EntryCount = DirEnt->EntryCount;
78   //
79   // Write directory entry
80   //
81   Status = FatAccessEntry (OFile, WriteData, EntryPos, &DirEnt->Entry);
82   if (EFI_ERROR (Status)) {
83     return Status;
84   }
85 
86   if (--EntryCount > 0) {
87     //
88     // Write LFN directory entry
89     //
90     SetMem (LfnBuffer, sizeof (CHAR16) * LFN_CHAR_TOTAL * EntryCount, 0xff);
91     Status = StrCpyS (
92                LfnBuffer,
93                ARRAY_SIZE (LfnBuffer),
94                DirEnt->FileString
95                );
96     if (EFI_ERROR (Status)) {
97       return Status;
98     }
99 
100     LfnBufferPointer    = LfnBuffer;
101     LfnEntry.Attributes = FAT_ATTRIBUTE_LFN;
102     LfnEntry.Type       = 0;
103     LfnEntry.MustBeZero = 0;
104     LfnEntry.Checksum   = FatCheckSum (DirEnt->Entry.FileName);
105     for (LfnOrdinal = 1; LfnOrdinal <= EntryCount; LfnOrdinal++) {
106       LfnEntry.Ordinal = LfnOrdinal;
107       if (LfnOrdinal == EntryCount) {
108         LfnEntry.Ordinal |= FAT_LFN_LAST;
109       }
110 
111       CopyMem (LfnEntry.Name1, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR1_LEN);
112       LfnBufferPointer += LFN_CHAR1_LEN;
113       CopyMem (LfnEntry.Name2, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR2_LEN);
114       LfnBufferPointer += LFN_CHAR2_LEN;
115       CopyMem (LfnEntry.Name3, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR3_LEN);
116       LfnBufferPointer += LFN_CHAR3_LEN;
117       EntryPos--;
118       if (DirEnt->Invalid) {
119         LfnEntry.Ordinal = DELETE_ENTRY_MARK;
120       }
121 
122       Status = FatAccessEntry (OFile, WriteData, EntryPos, &LfnEntry);
123       if (EFI_ERROR (Status)) {
124         return Status;
125       }
126     }
127   }
128 
129   return EFI_SUCCESS;
130 }
131 
132 /**
133 
134   Determine whether the directory entry is "." or ".." entry.
135 
136   @param  DirEnt               - The corresponding directory entry.
137 
138   @retval TRUE                 - The directory entry is "." or ".." directory entry
139   @retval FALSE                - The directory entry is not "." or ".." directory entry
140 
141 **/
142 BOOLEAN
FatIsDotDirEnt(IN FAT_DIRENT * DirEnt)143 FatIsDotDirEnt (
144   IN FAT_DIRENT  *DirEnt
145   )
146 {
147   CHAR16  *FileString;
148   FileString = DirEnt->FileString;
149   if (StrCmp (FileString, L".") == 0 || StrCmp (FileString, L"..") == 0) {
150     return TRUE;
151   }
152 
153   return FALSE;
154 }
155 
156 /**
157 
158   Set the OFile's cluster info in its directory entry.
159 
160   @param  OFile                 - The corresponding OFile.
161 
162 **/
163 STATIC
164 VOID
FatSetDirEntCluster(IN FAT_OFILE * OFile)165 FatSetDirEntCluster (
166   IN FAT_OFILE    *OFile
167   )
168 {
169   UINTN       Cluster;
170   FAT_DIRENT  *DirEnt;
171 
172   DirEnt                        = OFile->DirEnt;
173   Cluster                       = OFile->FileCluster;
174   DirEnt->Entry.FileClusterHigh = (UINT16) (Cluster >> 16);
175   DirEnt->Entry.FileCluster     = (UINT16) Cluster;
176 }
177 
178 /**
179 
180   Set the OFile's cluster and size info in its directory entry.
181 
182   @param  OFile                 - The corresponding OFile.
183 
184 **/
185 VOID
FatUpdateDirEntClusterSizeInfo(IN FAT_OFILE * OFile)186 FatUpdateDirEntClusterSizeInfo (
187   IN FAT_OFILE    *OFile
188   )
189 {
190   ASSERT (OFile->ODir == NULL);
191   OFile->DirEnt->Entry.FileSize = (UINT32) OFile->FileSize;
192   FatSetDirEntCluster (OFile);
193 }
194 
195 /**
196 
197   Copy all the information of DirEnt2 to DirEnt1 except for 8.3 name.
198 
199   @param  DirEnt1               - The destination directory entry.
200   @param  DirEnt2               - The source directory entry.
201 
202 **/
203 VOID
FatCloneDirEnt(IN FAT_DIRENT * DirEnt1,IN FAT_DIRENT * DirEnt2)204 FatCloneDirEnt (
205   IN  FAT_DIRENT          *DirEnt1,
206   IN  FAT_DIRENT          *DirEnt2
207   )
208 {
209   UINT8 *Entry1;
210   UINT8 *Entry2;
211   Entry1  = (UINT8 *) &DirEnt1->Entry;
212   Entry2  = (UINT8 *) &DirEnt2->Entry;
213   CopyMem (
214     Entry1 + FAT_ENTRY_INFO_OFFSET,
215     Entry2 + FAT_ENTRY_INFO_OFFSET,
216     sizeof (FAT_DIRECTORY_ENTRY) - FAT_ENTRY_INFO_OFFSET
217     );
218 }
219 
220 /**
221 
222   Get the LFN for the directory entry.
223 
224   @param  Parent                - The parent directory.
225   @param  DirEnt                - The directory entry to get LFN.
226 
227 **/
228 STATIC
229 VOID
FatLoadLongNameEntry(IN FAT_OFILE * Parent,IN FAT_DIRENT * DirEnt)230 FatLoadLongNameEntry (
231   IN FAT_OFILE           *Parent,
232   IN FAT_DIRENT          *DirEnt
233   )
234 {
235   CHAR16            LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1];
236   CHAR16            *LfnBufferPointer;
237   CHAR8             *File8Dot3Name;
238   UINTN             EntryPos;
239   UINT8             LfnOrdinal;
240   UINT8             LfnChecksum;
241   FAT_DIRECTORY_LFN LfnEntry;
242   EFI_STATUS        Status;
243 
244   EntryPos          = DirEnt->EntryPos;
245   File8Dot3Name     = DirEnt->Entry.FileName;
246   LfnBufferPointer  = LfnBuffer;
247   //
248   // Computes checksum for LFN
249   //
250   LfnChecksum = FatCheckSum (File8Dot3Name);
251   LfnOrdinal  = 1;
252   do {
253     if (EntryPos == 0) {
254       LfnBufferPointer = LfnBuffer;
255       break;
256     }
257 
258     EntryPos--;
259     Status = FatAccessEntry (Parent, ReadData, EntryPos, &LfnEntry);
260     if (EFI_ERROR (Status) ||
261         LfnEntry.Attributes != FAT_ATTRIBUTE_LFN ||
262         LfnEntry.MustBeZero != 0 ||
263         LfnEntry.Checksum != LfnChecksum ||
264         (LfnEntry.Ordinal & (~FAT_LFN_LAST)) != LfnOrdinal ||
265         LfnOrdinal > MAX_LFN_ENTRIES
266         ) {
267       //
268       // The directory entry does not have a long file name or
269       // some error occurs when loading long file name for a directory entry,
270       // and then we load the long name from short name
271       //
272       LfnBufferPointer = LfnBuffer;
273       break;
274     }
275 
276     CopyMem (LfnBufferPointer, LfnEntry.Name1, sizeof (CHAR16) * LFN_CHAR1_LEN);
277     LfnBufferPointer += LFN_CHAR1_LEN;
278     CopyMem (LfnBufferPointer, LfnEntry.Name2, sizeof (CHAR16) * LFN_CHAR2_LEN);
279     LfnBufferPointer += LFN_CHAR2_LEN;
280     CopyMem (LfnBufferPointer, LfnEntry.Name3, sizeof (CHAR16) * LFN_CHAR3_LEN);
281     LfnBufferPointer += LFN_CHAR3_LEN;
282     LfnOrdinal++;
283   } while ((LfnEntry.Ordinal & FAT_LFN_LAST) == 0);
284   DirEnt->EntryCount = LfnOrdinal;
285   //
286   // Terminate current Lfnbuffer
287   //
288   *LfnBufferPointer = 0;
289   if (LfnBufferPointer == LfnBuffer) {
290     //
291     // Fail to get the long file name from long file name entry,
292     // get the file name from short name
293     //
294     FatGetFileNameViaCaseFlag (
295       DirEnt,
296       LfnBuffer,
297       ARRAY_SIZE (LfnBuffer)
298       );
299   }
300 
301   DirEnt->FileString = AllocateCopyPool (StrSize (LfnBuffer), LfnBuffer);
302 }
303 
304 /**
305 
306   Add this directory entry node to the list of directory entries and hash table.
307 
308   @param  ODir                  - The parent OFile which needs to be updated.
309   @param  DirEnt                - The directory entry to be added.
310 
311 **/
312 STATIC
313 VOID
FatAddDirEnt(IN FAT_ODIR * ODir,IN FAT_DIRENT * DirEnt)314 FatAddDirEnt (
315   IN FAT_ODIR             *ODir,
316   IN FAT_DIRENT           *DirEnt
317   )
318 {
319   if (DirEnt->Link.BackLink == NULL) {
320     DirEnt->Link.BackLink = &ODir->ChildList;
321   }
322   InsertTailList (DirEnt->Link.BackLink, &DirEnt->Link);
323   FatInsertToHashTable (ODir, DirEnt);
324 }
325 
326 /**
327 
328   Load from disk the next directory entry at current end of directory position.
329 
330   @param  OFile                 - The parent OFile.
331   @param  PtrDirEnt             - The directory entry that is loaded.
332 
333   @retval EFI_SUCCESS           - Load the directory entry successfully.
334   @retval EFI_OUT_OF_RESOURCES  - Out of resource.
335   @return other                 - An error occurred when reading the directory entries.
336 
337 **/
338 STATIC
339 EFI_STATUS
FatLoadNextDirEnt(IN FAT_OFILE * OFile,OUT FAT_DIRENT ** PtrDirEnt)340 FatLoadNextDirEnt (
341   IN  FAT_OFILE           *OFile,
342   OUT FAT_DIRENT          **PtrDirEnt
343   )
344 {
345   EFI_STATUS          Status;
346   FAT_DIRENT          *DirEnt;
347   FAT_ODIR            *ODir;
348   FAT_DIRECTORY_ENTRY Entry;
349 
350   ODir = OFile->ODir;
351   //
352   // Make sure the parent's directory has been opened
353   //
354   ASSERT (ODir != NULL);
355   //
356   // Assert we have not reached the end of directory
357   //
358   ASSERT (!ODir->EndOfDir);
359   DirEnt = NULL;
360 
361   for (;;) {
362     //
363     // Read the next directory entry until we find a valid directory entry (excluding lfn entry)
364     //
365     Status = FatAccessEntry (OFile, ReadData, ODir->CurrentEndPos, &Entry);
366     if (EFI_ERROR (Status)) {
367       return Status;
368     }
369 
370     if (((UINT8) Entry.FileName[0] != DELETE_ENTRY_MARK) && (Entry.Attributes & FAT_ATTRIBUTE_VOLUME_ID) == 0) {
371       //
372       // We get a valid directory entry, then handle it
373       //
374       break;
375     }
376 
377     ODir->CurrentEndPos++;
378   }
379 
380   if (Entry.FileName[0] != EMPTY_ENTRY_MARK) {
381     //
382     // Although FAT spec states this field is always 0 for FAT12 & FAT16, some applications
383     // might use it for some special usage, it is safer to zero it in memory for FAT12 & FAT16.
384     //
385     if (OFile->Volume->FatType != Fat32) {
386       Entry.FileClusterHigh = 0;
387     }
388 
389     //
390     // This is a valid directory entry
391     //
392     DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT));
393     if (DirEnt == NULL) {
394       return EFI_OUT_OF_RESOURCES;
395     }
396 
397     DirEnt->Signature = FAT_DIRENT_SIGNATURE;
398     //
399     // Remember the directory's entry position on disk
400     //
401     DirEnt->EntryPos = (UINT16) ODir->CurrentEndPos;
402     CopyMem (&DirEnt->Entry, &Entry, sizeof (FAT_DIRECTORY_ENTRY));
403     FatLoadLongNameEntry (OFile, DirEnt);
404     if (DirEnt->FileString == NULL) {
405       Status = EFI_OUT_OF_RESOURCES;
406       goto Done;
407     }
408     //
409     // Add this directory entry to directory
410     //
411     FatAddDirEnt (ODir, DirEnt);
412     //
413     // Point to next directory entry
414     //
415     ODir->CurrentEndPos++;
416   } else {
417     ODir->EndOfDir = TRUE;
418   }
419 
420   *PtrDirEnt = DirEnt;
421   return EFI_SUCCESS;
422 
423 Done:
424   FatFreeDirEnt (DirEnt);
425   return Status;
426 }
427 
428 /**
429 
430   Get the directory entry's info into Buffer.
431 
432   @param  Volume                - FAT file system volume.
433   @param  DirEnt                - The corresponding directory entry.
434   @param  BufferSize            - Size of Buffer.
435   @param  Buffer                - Buffer containing file info.
436 
437   @retval EFI_SUCCESS           - Get the file info successfully.
438   @retval EFI_BUFFER_TOO_SMALL  - The buffer is too small.
439 
440 **/
441 EFI_STATUS
FatGetDirEntInfo(IN FAT_VOLUME * Volume,IN FAT_DIRENT * DirEnt,IN OUT UINTN * BufferSize,OUT VOID * Buffer)442 FatGetDirEntInfo (
443   IN     FAT_VOLUME         *Volume,
444   IN     FAT_DIRENT         *DirEnt,
445   IN OUT UINTN              *BufferSize,
446      OUT VOID               *Buffer
447   )
448 {
449   UINTN               Size;
450   UINTN               NameSize;
451   UINTN               ResultSize;
452   UINTN               Cluster;
453   EFI_STATUS          Status;
454   EFI_FILE_INFO       *Info;
455   FAT_DIRECTORY_ENTRY *Entry;
456   FAT_DATE_TIME       FatLastAccess;
457 
458   ASSERT_VOLUME_LOCKED (Volume);
459 
460   Size        = SIZE_OF_EFI_FILE_INFO;
461   NameSize    = StrSize (DirEnt->FileString);
462   ResultSize  = Size + NameSize;
463 
464   Status      = EFI_BUFFER_TOO_SMALL;
465   if (*BufferSize >= ResultSize) {
466     Status      = EFI_SUCCESS;
467     Entry       = &DirEnt->Entry;
468     Info        = Buffer;
469     Info->Size  = ResultSize;
470     if ((Entry->Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) {
471       Cluster             = (Entry->FileClusterHigh << 16) | Entry->FileCluster;
472       Info->PhysicalSize  = FatPhysicalDirSize (Volume, Cluster);
473       Info->FileSize      = Info->PhysicalSize;
474     } else {
475       Info->FileSize      = Entry->FileSize;
476       Info->PhysicalSize  = FatPhysicalFileSize (Volume, Entry->FileSize);
477     }
478 
479     ZeroMem (&FatLastAccess.Time, sizeof (FatLastAccess.Time));
480     CopyMem (&FatLastAccess.Date, &Entry->FileLastAccess, sizeof (FatLastAccess.Date));
481     FatFatTimeToEfiTime (&FatLastAccess, &Info->LastAccessTime);
482     FatFatTimeToEfiTime (&Entry->FileCreateTime, &Info->CreateTime);
483     FatFatTimeToEfiTime (&Entry->FileModificationTime, &Info->ModificationTime);
484     Info->Attribute = Entry->Attributes & EFI_FILE_VALID_ATTR;
485     CopyMem ((CHAR8 *) Buffer + Size, DirEnt->FileString, NameSize);
486   }
487 
488   *BufferSize = ResultSize;
489   return Status;
490 }
491 
492 /**
493 
494   Search the directory for the directory entry whose filename is FileNameString.
495 
496   @param  OFile                 - The parent OFile whose directory is to be searched.
497   @param  FileNameString        - The filename to be searched.
498   @param  PtrDirEnt             - pointer to the directory entry if found.
499 
500   @retval EFI_SUCCESS           - Find the directory entry or not found.
501   @return other                 - An error occurred when reading the directory entries.
502 
503 **/
504 STATIC
505 EFI_STATUS
FatSearchODir(IN FAT_OFILE * OFile,IN CHAR16 * FileNameString,OUT FAT_DIRENT ** PtrDirEnt)506 FatSearchODir (
507   IN  FAT_OFILE      *OFile,
508   IN  CHAR16         *FileNameString,
509   OUT FAT_DIRENT     **PtrDirEnt
510   )
511 {
512   BOOLEAN     PossibleShortName;
513   CHAR8       File8Dot3Name[FAT_NAME_LEN];
514   FAT_ODIR    *ODir;
515   FAT_DIRENT  *DirEnt;
516   EFI_STATUS  Status;
517 
518   ODir = OFile->ODir;
519   ASSERT (ODir != NULL);
520   //
521   // Check if the file name is a valid short name
522   //
523   PossibleShortName = FatCheckIs8Dot3Name (FileNameString, File8Dot3Name);
524   //
525   // Search the hash table first
526   //
527   DirEnt = *FatLongNameHashSearch (ODir, FileNameString);
528   if (DirEnt == NULL && PossibleShortName) {
529       DirEnt = *FatShortNameHashSearch (ODir, File8Dot3Name);
530   }
531   if (DirEnt == NULL) {
532     //
533     // We fail to get the directory entry from hash table; we then
534     // search the rest directory
535     //
536     while (!ODir->EndOfDir) {
537       Status = FatLoadNextDirEnt (OFile, &DirEnt);
538       if (EFI_ERROR (Status)) {
539         return Status;
540       }
541 
542       if (DirEnt != NULL) {
543         if (FatStriCmp (FileNameString, DirEnt->FileString) == 0) {
544           break;
545         }
546 
547         if (PossibleShortName && CompareMem (File8Dot3Name, DirEnt->Entry.FileName, FAT_NAME_LEN) == 0) {
548           break;
549         }
550       }
551     }
552   }
553 
554   *PtrDirEnt = DirEnt;
555   return EFI_SUCCESS;
556 }
557 
558 /**
559 
560   Set the OFile's current directory cursor to the list head.
561 
562   @param OFile                 - The directory OFile whose directory cursor is reset.
563 
564 **/
565 VOID
FatResetODirCursor(IN FAT_OFILE * OFile)566 FatResetODirCursor (
567   IN FAT_OFILE    *OFile
568   )
569 {
570   FAT_ODIR  *ODir;
571 
572   ODir = OFile->ODir;
573   ASSERT (ODir != NULL);
574   ODir->CurrentCursor = &(ODir->ChildList);
575   ODir->CurrentPos    = 0;
576 }
577 
578 /**
579 
580   Set the directory's cursor to the next and get the next directory entry.
581 
582   @param  OFile                 - The parent OFile.
583   @param PtrDirEnt             - The next directory entry.
584 
585   @retval EFI_SUCCESS           - We get the next directory entry successfully.
586   @return other                 - An error occurred when get next directory entry.
587 
588 **/
589 EFI_STATUS
FatGetNextDirEnt(IN FAT_OFILE * OFile,OUT FAT_DIRENT ** PtrDirEnt)590 FatGetNextDirEnt (
591   IN  FAT_OFILE     *OFile,
592   OUT FAT_DIRENT    **PtrDirEnt
593   )
594 {
595   EFI_STATUS  Status;
596   FAT_DIRENT  *DirEnt;
597   FAT_ODIR    *ODir;
598 
599   ODir = OFile->ODir;
600   ASSERT (ODir != NULL);
601   if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) {
602     //
603     // End of directory, we will try one more time
604     //
605     if (!ODir->EndOfDir) {
606       //
607       // Read directory from disk
608       //
609       Status = FatLoadNextDirEnt (OFile, &DirEnt);
610       if (EFI_ERROR (Status)) {
611         return Status;
612       }
613     }
614   }
615 
616   if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) {
617     //
618     // End of directory, return NULL
619     //
620     DirEnt              = NULL;
621     ODir->CurrentPos    = ODir->CurrentEndPos;
622   } else {
623     ODir->CurrentCursor = ODir->CurrentCursor->ForwardLink;
624     DirEnt              = DIRENT_FROM_LINK (ODir->CurrentCursor);
625     ODir->CurrentPos    = DirEnt->EntryPos + 1;
626   }
627 
628   *PtrDirEnt = DirEnt;
629   return EFI_SUCCESS;
630 }
631 
632 /**
633 
634   Set the directory entry count according to the filename.
635 
636   @param  OFile                 - The corresponding OFile.
637   @param  DirEnt                - The directory entry to be set.
638 
639 **/
640 STATIC
641 VOID
FatSetEntryCount(IN FAT_OFILE * OFile,IN FAT_DIRENT * DirEnt)642 FatSetEntryCount (
643   IN FAT_OFILE    *OFile,
644   IN FAT_DIRENT   *DirEnt
645   )
646 {
647   CHAR16  *FileString;
648   CHAR8   *File8Dot3Name;
649 
650   //
651   // Get new entry count and set the 8.3 name
652   //
653   DirEnt->EntryCount  = 1;
654   FileString          = DirEnt->FileString;
655   File8Dot3Name       = DirEnt->Entry.FileName;
656   SetMem (File8Dot3Name, FAT_NAME_LEN, ' ');
657   if (StrCmp (FileString, L".") == 0) {
658     //
659     // "." entry
660     //
661     File8Dot3Name[0] = '.';
662     FatCloneDirEnt (DirEnt, OFile->DirEnt);
663   } else if (StrCmp (FileString, L"..") == 0) {
664     //
665     // ".." entry
666     //
667     File8Dot3Name[0]  = '.';
668     File8Dot3Name[1]  = '.';
669     FatCloneDirEnt (DirEnt, OFile->Parent->DirEnt);
670   } else {
671     //
672     // Normal name
673     //
674     if (FatCheckIs8Dot3Name (FileString, File8Dot3Name)) {
675       //
676       // This file name is a valid 8.3 file name, we need to further check its case flag
677       //
678       FatSetCaseFlag (DirEnt);
679     } else {
680       //
681       // The file name is not a valid 8.3 name we need to generate an 8.3 name for it
682       //
683       FatCreate8Dot3Name (OFile, DirEnt);
684       DirEnt->EntryCount = (UINT8)(LFN_ENTRY_NUMBER (StrLen (FileString)) + DirEnt->EntryCount);
685     }
686   }
687 }
688 
689 /**
690 
691   Append a zero cluster to the current OFile.
692 
693   @param  OFile        - The directory OFile which needs to be updated.
694 
695   @retval EFI_SUCCESS  - Append a zero cluster to the OFile successfully.
696   @return other        - An error occurred when appending the zero cluster.
697 
698 **/
699 STATIC
700 EFI_STATUS
FatExpandODir(IN FAT_OFILE * OFile)701 FatExpandODir (
702   IN FAT_OFILE  *OFile
703   )
704 {
705   return FatExpandOFile (OFile, OFile->FileSize + OFile->Volume->ClusterSize);
706 }
707 
708 /**
709 
710   Search the Root OFile for the possible volume label.
711 
712   @param  Root                  - The Root OFile.
713   @param  DirEnt                - The returned directory entry of volume label.
714 
715   @retval EFI_SUCCESS           - The search process is completed successfully.
716   @return other                 - An error occurred when searching volume label.
717 
718 **/
719 STATIC
720 EFI_STATUS
FatSeekVolumeId(IN FAT_OFILE * Root,OUT FAT_DIRENT * DirEnt)721 FatSeekVolumeId (
722   IN  FAT_OFILE            *Root,
723   OUT FAT_DIRENT           *DirEnt
724   )
725 {
726   EFI_STATUS          Status;
727   UINTN               EntryPos;
728   FAT_DIRECTORY_ENTRY *Entry;
729 
730   EntryPos        = 0;
731   Entry           = &DirEnt->Entry;
732   DirEnt->Invalid = TRUE;
733   do {
734     Status = FatAccessEntry (Root, ReadData, EntryPos, Entry);
735     if (EFI_ERROR (Status)) {
736       return Status;
737     }
738 
739     if (((UINT8) Entry->FileName[0] != DELETE_ENTRY_MARK) && (((Entry->Attributes) & (~FAT_ATTRIBUTE_ARCHIVE)) == FAT_ATTRIBUTE_VOLUME_ID)) {
740       DirEnt->EntryPos   = (UINT16) EntryPos;
741       DirEnt->EntryCount = 1;
742       DirEnt->Invalid    = FALSE;
743       break;
744     }
745 
746     EntryPos++;
747   } while (Entry->FileName[0] != EMPTY_ENTRY_MARK);
748   return EFI_SUCCESS;
749 }
750 
751 /**
752 
753   Use First Fit Algorithm to insert directory entry.
754   Only this function will erase "E5" entries in a directory.
755   In view of safest recovery, this function will only be triggered
756   when maximum directory entry number has reached.
757 
758   @param  OFile                 - The corresponding OFile.
759   @param  DirEnt                - The directory entry to be inserted.
760 
761   @retval EFI_SUCCESS           - The directory entry has been successfully inserted.
762   @retval EFI_VOLUME_FULL       - The directory can not hold more directory entries.
763   @return Others                - Some error occurred when inserting new directory entries.
764 
765 **/
766 STATIC
767 EFI_STATUS
FatFirstFitInsertDirEnt(IN FAT_OFILE * OFile,IN FAT_DIRENT * DirEnt)768 FatFirstFitInsertDirEnt (
769   IN FAT_OFILE    *OFile,
770   IN FAT_DIRENT   *DirEnt
771   )
772 {
773   EFI_STATUS      Status;
774   FAT_ODIR        *ODir;
775   LIST_ENTRY      *CurrentEntry;
776   FAT_DIRENT      *CurrentDirEnt;
777   UINT32          CurrentPos;
778   UINT32          LabelPos;
779   UINT32          NewEntryPos;
780   UINT16          EntryCount;
781   FAT_DIRENT      LabelDirEnt;
782 
783   LabelPos = 0;
784   if (OFile->Parent == NULL) {
785     Status = FatSeekVolumeId (OFile, &LabelDirEnt);
786     if (EFI_ERROR (Status)) {
787       return Status;
788     }
789 
790     if (!LabelDirEnt.Invalid) {
791       LabelPos = LabelDirEnt.EntryPos;
792     }
793   }
794 
795   EntryCount  = DirEnt->EntryCount;
796   NewEntryPos = EntryCount;
797   CurrentPos  = 0;
798   ODir        = OFile->ODir;
799   for (CurrentEntry = ODir->ChildList.ForwardLink;
800        CurrentEntry != &ODir->ChildList;
801        CurrentEntry = CurrentEntry->ForwardLink
802       ) {
803     CurrentDirEnt = DIRENT_FROM_LINK (CurrentEntry);
804     if (NewEntryPos + CurrentDirEnt->EntryCount <= CurrentDirEnt->EntryPos) {
805       if (LabelPos > NewEntryPos || LabelPos <= CurrentPos) {
806         //
807         // first fit succeeded
808         //
809         goto Done;
810       }
811     }
812 
813     CurrentPos  = CurrentDirEnt->EntryPos;
814     NewEntryPos = CurrentPos + EntryCount;
815   }
816 
817   if (NewEntryPos >= ODir->CurrentEndPos) {
818     return EFI_VOLUME_FULL;
819   }
820 
821 Done:
822   DirEnt->EntryPos   = (UINT16) NewEntryPos;
823   DirEnt->Link.BackLink = CurrentEntry;
824   return EFI_SUCCESS;
825 }
826 
827 /**
828 
829   Find the new directory entry position for the directory entry.
830 
831   @param  OFile                 - The corresponding OFile.
832   @param  DirEnt                - The directory entry whose new position is to be set.
833 
834   @retval EFI_SUCCESS           - The new directory entry position is successfully found.
835   @retval EFI_VOLUME_FULL       - The directory has reach its maximum capacity.
836   @return other                 - An error occurred when reading the directory entry.
837 
838 **/
839 STATIC
840 EFI_STATUS
FatNewEntryPos(IN FAT_OFILE * OFile,IN FAT_DIRENT * DirEnt)841 FatNewEntryPos (
842   IN FAT_OFILE    *OFile,
843   IN FAT_DIRENT   *DirEnt
844   )
845 {
846   EFI_STATUS  Status;
847   FAT_ODIR    *ODir;
848   FAT_DIRENT  *TempDirEnt;
849   UINT32      NewEndPos;
850 
851   ODir = OFile->ODir;
852   ASSERT (ODir != NULL);
853   //
854   // Make sure the whole directory has been loaded
855   //
856   while (!ODir->EndOfDir) {
857     Status = FatLoadNextDirEnt (OFile, &TempDirEnt);
858     if (EFI_ERROR (Status)) {
859       return Status;
860     }
861   }
862   //
863   // We will append this entry to the end of directory
864   //
865   FatGetCurrentFatTime (&DirEnt->Entry.FileCreateTime);
866   CopyMem (&DirEnt->Entry.FileModificationTime, &DirEnt->Entry.FileCreateTime, sizeof (FAT_DATE_TIME));
867   CopyMem (&DirEnt->Entry.FileLastAccess, &DirEnt->Entry.FileCreateTime.Date, sizeof (FAT_DATE));
868   NewEndPos = ODir->CurrentEndPos + DirEnt->EntryCount;
869   if (NewEndPos * sizeof (FAT_DIRECTORY_ENTRY) > OFile->FileSize) {
870     if (NewEndPos >= (OFile->IsFixedRootDir ? OFile->Volume->RootEntries : FAT_MAX_DIRENTRY_COUNT)) {
871       //
872       // We try to use fist fit algorithm to insert this directory entry
873       //
874       return FatFirstFitInsertDirEnt (OFile, DirEnt);
875     }
876     //
877     // We should allocate a new cluster for this directory
878     //
879     Status = FatExpandODir (OFile);
880     if (EFI_ERROR (Status)) {
881       return Status;
882     }
883   }
884   //
885   // We append our directory entry at the end of directory file
886   //
887   ODir->CurrentEndPos = NewEndPos;
888   DirEnt->EntryPos = (UINT16) (ODir->CurrentEndPos - 1);
889   return EFI_SUCCESS;
890 }
891 
892 /**
893 
894   Get the directory entry for the volume.
895 
896   @param  Volume                - FAT file system volume.
897   @param  Name                  - The file name of the volume.
898 
899   @retval EFI_SUCCESS           - Update the volume with the directory entry sucessfully.
900   @return others                - An error occurred when getting volume label.
901 
902 **/
903 EFI_STATUS
FatGetVolumeEntry(IN FAT_VOLUME * Volume,IN CHAR16 * Name)904 FatGetVolumeEntry (
905   IN FAT_VOLUME           *Volume,
906   IN CHAR16               *Name
907   )
908 {
909   EFI_STATUS  Status;
910   FAT_DIRENT  LabelDirEnt;
911 
912   *Name   = 0;
913   Status  = FatSeekVolumeId (Volume->Root, &LabelDirEnt);
914   if (!EFI_ERROR (Status)) {
915     if (!LabelDirEnt.Invalid) {
916       FatNameToStr (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, FALSE, Name);
917     }
918   }
919 
920   return Status;
921 }
922 
923 /**
924 
925   Set the relevant directory entry into disk for the volume.
926 
927   @param  Volume              - FAT file system volume.
928   @param  Name                - The new file name of the volume.
929 
930   @retval EFI_SUCCESS         - Update the Volume sucessfully.
931   @retval EFI_UNSUPPORTED     - The input label is not a valid volume label.
932   @return other               - An error occurred when setting volume label.
933 
934 **/
935 EFI_STATUS
FatSetVolumeEntry(IN FAT_VOLUME * Volume,IN CHAR16 * Name)936 FatSetVolumeEntry (
937   IN FAT_VOLUME           *Volume,
938   IN CHAR16               *Name
939   )
940 {
941   EFI_STATUS  Status;
942   FAT_DIRENT  LabelDirEnt;
943   FAT_OFILE   *Root;
944 
945   Root    = Volume->Root;
946   Status  = FatSeekVolumeId (Volume->Root, &LabelDirEnt);
947   if (EFI_ERROR (Status)) {
948     return Status;
949   }
950 
951   if (LabelDirEnt.Invalid) {
952     //
953     // If there is not the relevant directory entry, create a new one
954     //
955     ZeroMem (&LabelDirEnt, sizeof (FAT_DIRENT));
956     LabelDirEnt.EntryCount = 1;
957     Status                 = FatNewEntryPos (Root, &LabelDirEnt);
958     if (EFI_ERROR (Status)) {
959       return Status;
960     }
961 
962     LabelDirEnt.Entry.Attributes = FAT_ATTRIBUTE_VOLUME_ID;
963   }
964 
965   SetMem (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, ' ');
966   if (FatStrToFat (Name, FAT_NAME_LEN, LabelDirEnt.Entry.FileName)) {
967     return EFI_UNSUPPORTED;
968   }
969 
970   FatGetCurrentFatTime (&LabelDirEnt.Entry.FileModificationTime);
971   return FatStoreDirEnt (Root, &LabelDirEnt);
972 }
973 
974 /**
975 
976   Create "." and ".." directory entries in the newly-created parent OFile.
977 
978   @param  OFile                 - The parent OFile.
979 
980   @retval EFI_SUCCESS           - The dot directory entries are successfully created.
981   @return other                 - An error occurred when creating the directory entry.
982 
983 **/
984 EFI_STATUS
FatCreateDotDirEnts(IN FAT_OFILE * OFile)985 FatCreateDotDirEnts (
986   IN FAT_OFILE          *OFile
987   )
988 {
989   EFI_STATUS  Status;
990   FAT_DIRENT  *DirEnt;
991 
992   Status = FatExpandODir (OFile);
993   if (EFI_ERROR (Status)) {
994     return Status;
995   }
996 
997   FatSetDirEntCluster (OFile);
998   //
999   // Create "."
1000   //
1001   Status = FatCreateDirEnt (OFile, L".", FAT_ATTRIBUTE_DIRECTORY, &DirEnt);
1002   if (EFI_ERROR (Status)) {
1003     return Status;
1004   }
1005   //
1006   // Create ".."
1007   //
1008   Status = FatCreateDirEnt (OFile, L"..", FAT_ATTRIBUTE_DIRECTORY, &DirEnt);
1009   return Status;
1010 }
1011 
1012 /**
1013 
1014   Create a directory entry in the parent OFile.
1015 
1016   @param  OFile                 - The parent OFile.
1017   @param  FileName              - The filename of the newly-created directory entry.
1018   @param  Attributes            - The attribute of the newly-created directory entry.
1019   @param  PtrDirEnt             - The pointer to the newly-created directory entry.
1020 
1021   @retval EFI_SUCCESS           - The directory entry is successfully created.
1022   @retval EFI_OUT_OF_RESOURCES  - Not enough memory to create the directory entry.
1023   @return other                 - An error occurred when creating the directory entry.
1024 
1025 **/
1026 EFI_STATUS
FatCreateDirEnt(IN FAT_OFILE * OFile,IN CHAR16 * FileName,IN UINT8 Attributes,OUT FAT_DIRENT ** PtrDirEnt)1027 FatCreateDirEnt (
1028   IN  FAT_OFILE         *OFile,
1029   IN  CHAR16            *FileName,
1030   IN  UINT8             Attributes,
1031   OUT FAT_DIRENT        **PtrDirEnt
1032   )
1033 {
1034   FAT_DIRENT  *DirEnt;
1035   FAT_ODIR    *ODir;
1036   EFI_STATUS  Status;
1037 
1038   ASSERT (OFile != NULL);
1039   ODir = OFile->ODir;
1040   ASSERT (ODir != NULL);
1041   DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT));
1042   if (DirEnt == NULL) {
1043     return EFI_OUT_OF_RESOURCES;
1044   }
1045 
1046   DirEnt->Signature   = FAT_DIRENT_SIGNATURE;
1047   DirEnt->FileString  = AllocateCopyPool (StrSize (FileName), FileName);
1048   if (DirEnt->FileString == NULL) {
1049     Status = EFI_OUT_OF_RESOURCES;
1050     goto Done;
1051   }
1052   //
1053   // Determine how many directory entries we need
1054   //
1055   FatSetEntryCount (OFile, DirEnt);
1056   //
1057   // Determine the file's directory entry position
1058   //
1059   Status = FatNewEntryPos (OFile, DirEnt);
1060   if (EFI_ERROR (Status)) {
1061     goto Done;
1062   }
1063 
1064   FatAddDirEnt (ODir, DirEnt);
1065   DirEnt->Entry.Attributes = Attributes;
1066   *PtrDirEnt               = DirEnt;
1067   DEBUG ((EFI_D_INFO, "FSOpen: Created new directory entry '%S'\n", DirEnt->FileString));
1068   return FatStoreDirEnt (OFile, DirEnt);
1069 
1070 Done:
1071   FatFreeDirEnt (DirEnt);
1072   return Status;
1073 }
1074 
1075 /**
1076 
1077   Remove this directory entry node from the list of directory entries and hash table.
1078 
1079   @param  OFile                - The parent OFile.
1080   @param  DirEnt               - The directory entry to be removed.
1081 
1082   @retval EFI_SUCCESS          - The directory entry is successfully removed.
1083   @return other                - An error occurred when removing the directory entry.
1084 
1085 **/
1086 EFI_STATUS
FatRemoveDirEnt(IN FAT_OFILE * OFile,IN FAT_DIRENT * DirEnt)1087 FatRemoveDirEnt (
1088   IN FAT_OFILE    *OFile,
1089   IN FAT_DIRENT   *DirEnt
1090   )
1091 {
1092   FAT_ODIR  *ODir;
1093 
1094   ODir = OFile->ODir;
1095   if (ODir->CurrentCursor == &DirEnt->Link) {
1096     //
1097     // Move the directory cursor to its previous directory entry
1098     //
1099     ODir->CurrentCursor = ODir->CurrentCursor->BackLink;
1100   }
1101   //
1102   // Remove from directory entry list
1103   //
1104   RemoveEntryList (&DirEnt->Link);
1105   //
1106   // Remove from hash table
1107   //
1108   FatDeleteFromHashTable (ODir, DirEnt);
1109   DirEnt->Entry.FileName[0] = DELETE_ENTRY_MARK;
1110   DirEnt->Invalid           = TRUE;
1111   return FatStoreDirEnt (OFile, DirEnt);
1112 }
1113 
1114 /**
1115 
1116   Open the directory entry to get the OFile.
1117 
1118   @param  Parent                - The parent OFile.
1119   @param  DirEnt                - The directory entry to be opened.
1120 
1121   @retval EFI_SUCCESS           - The directory entry is successfully opened.
1122   @retval EFI_OUT_OF_RESOURCES  - not enough memory to allocate a new OFile.
1123   @return other                 - An error occurred when opening the directory entry.
1124 
1125 **/
1126 EFI_STATUS
FatOpenDirEnt(IN FAT_OFILE * Parent,IN FAT_DIRENT * DirEnt)1127 FatOpenDirEnt (
1128   IN FAT_OFILE         *Parent,
1129   IN FAT_DIRENT        *DirEnt
1130   )
1131 {
1132   FAT_OFILE   *OFile;
1133   FAT_VOLUME  *Volume;
1134 
1135   if (DirEnt->OFile == NULL) {
1136     //
1137     // Open the directory entry
1138     //
1139     OFile = AllocateZeroPool (sizeof (FAT_OFILE));
1140     if (OFile == NULL) {
1141       return EFI_OUT_OF_RESOURCES;
1142     }
1143 
1144     OFile->Signature = FAT_OFILE_SIGNATURE;
1145     InitializeListHead (&OFile->Opens);
1146     InitializeListHead (&OFile->ChildHead);
1147     OFile->Parent = Parent;
1148     OFile->DirEnt = DirEnt;
1149     if (Parent != NULL) {
1150       //
1151       // The newly created OFile is not root
1152       //
1153       Volume             = Parent->Volume;
1154       OFile->FullPathLen = Parent->FullPathLen + 1 + StrLen (DirEnt->FileString);
1155       OFile->FileCluster = ((DirEnt->Entry.FileClusterHigh) << 16) | (DirEnt->Entry.FileCluster);
1156       InsertTailList (&Parent->ChildHead, &OFile->ChildLink);
1157     } else {
1158       //
1159       // The newly created OFile is root
1160       //
1161       Volume                = VOLUME_FROM_ROOT_DIRENT (DirEnt);
1162       Volume->Root          = OFile;
1163       OFile->FileCluster    = Volume->RootCluster;
1164       if (Volume->FatType  != Fat32) {
1165         OFile->IsFixedRootDir  = TRUE;
1166       }
1167     }
1168 
1169     OFile->FileCurrentCluster  = OFile->FileCluster;
1170     OFile->Volume              = Volume;
1171     InsertHeadList (&Volume->CheckRef, &OFile->CheckLink);
1172 
1173     OFile->FileSize = DirEnt->Entry.FileSize;
1174     if ((DirEnt->Entry.Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) {
1175       if (OFile->IsFixedRootDir) {
1176         OFile->FileSize = Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY);
1177       } else {
1178         OFile->FileSize = FatPhysicalDirSize (Volume, OFile->FileCluster);
1179       }
1180 
1181       FatRequestODir (OFile);
1182       if (OFile->ODir == NULL) {
1183         return EFI_OUT_OF_RESOURCES;
1184       }
1185     }
1186 
1187     DirEnt->OFile = OFile;
1188   }
1189 
1190   return EFI_SUCCESS;
1191 }
1192 
1193 /**
1194 
1195   Close the directory entry and free the OFile.
1196 
1197   @param  DirEnt               - The directory entry to be closed.
1198 
1199 **/
1200 VOID
FatCloseDirEnt(IN FAT_DIRENT * DirEnt)1201 FatCloseDirEnt (
1202   IN FAT_DIRENT        *DirEnt
1203   )
1204 {
1205   FAT_OFILE   *OFile;
1206   FAT_VOLUME  *Volume;
1207 
1208   OFile   = DirEnt->OFile;
1209   ASSERT (OFile != NULL);
1210   Volume  = OFile->Volume;
1211 
1212   if (OFile->ODir != NULL) {
1213     FatDiscardODir (OFile);
1214   }
1215 
1216   if (OFile->Parent == NULL) {
1217     Volume->Root = NULL;
1218   } else {
1219     RemoveEntryList (&OFile->ChildLink);
1220   }
1221 
1222   FreePool (OFile);
1223   DirEnt->OFile = NULL;
1224   if (DirEnt->Invalid == TRUE) {
1225     //
1226     // Free directory entry itself
1227     //
1228     FatFreeDirEnt (DirEnt);
1229   }
1230 }
1231 
1232 /**
1233 
1234   Traverse filename and open all OFiles that can be opened.
1235   Update filename pointer to the component that can't be opened.
1236   If more than one name component remains, returns an error;
1237   otherwise, return the remaining name component so that the caller might choose to create it.
1238 
1239   @param  PtrOFile              - As input, the reference OFile; as output, the located OFile.
1240   @param  FileName              - The file name relevant to the OFile.
1241   @param  Attributes            - The attribute of the destination OFile.
1242   @param  NewFileName           - The remaining file name.
1243 
1244   @retval EFI_NOT_FOUND         - The file name can't be opened and there is more than one
1245                           components within the name left (this means the name can
1246                           not be created either).
1247   @retval EFI_INVALID_PARAMETER - The parameter is not valid.
1248   @retval EFI_SUCCESS           - Open the file successfully.
1249   @return other                 - An error occured when locating the OFile.
1250 
1251 **/
1252 EFI_STATUS
FatLocateOFile(IN OUT FAT_OFILE ** PtrOFile,IN CHAR16 * FileName,IN UINT8 Attributes,OUT CHAR16 * NewFileName)1253 FatLocateOFile (
1254   IN OUT FAT_OFILE        **PtrOFile,
1255   IN     CHAR16           *FileName,
1256   IN     UINT8            Attributes,
1257      OUT CHAR16           *NewFileName
1258   )
1259 {
1260   EFI_STATUS  Status;
1261   FAT_VOLUME  *Volume;
1262   CHAR16      ComponentName[EFI_PATH_STRING_LENGTH];
1263   UINTN       FileNameLen;
1264   BOOLEAN     DirIntended;
1265   CHAR16      *Next;
1266   FAT_OFILE   *OFile;
1267   FAT_DIRENT  *DirEnt;
1268 
1269   DirEnt = NULL;
1270 
1271   FileNameLen = StrLen (FileName);
1272   if (FileNameLen == 0) {
1273     return EFI_INVALID_PARAMETER;
1274   }
1275 
1276   OFile       = *PtrOFile;
1277   Volume      = OFile->Volume;
1278 
1279   DirIntended = FALSE;
1280   if (FileName[FileNameLen - 1] == PATH_NAME_SEPARATOR) {
1281     DirIntended = TRUE;
1282   }
1283   //
1284   // If name starts with path name separator, then move to root OFile
1285   //
1286   if (*FileName == PATH_NAME_SEPARATOR) {
1287     OFile = Volume->Root;
1288     FileName++;
1289     FileNameLen--;
1290   }
1291   //
1292   // Per FAT Spec the file name should meet the following criteria:
1293   //   C1. Length (FileLongName) <= 255
1294   //   C2. Length (X:FileFullPath<NUL>) <= 260
1295   // Here we check C2 first.
1296   //
1297   if (2 + OFile->FullPathLen + 1 + FileNameLen + 1 > EFI_PATH_STRING_LENGTH) {
1298     //
1299     // Full path length can not surpass 256
1300     //
1301     return EFI_INVALID_PARAMETER;
1302   }
1303   //
1304   // Start at current location
1305   //
1306   Next = FileName;
1307   for (;;) {
1308     //
1309     // Get the next component name
1310     //
1311     FileName = Next;
1312     Next     = FatGetNextNameComponent (FileName, ComponentName);
1313 
1314     //
1315     // If end of the file name, we're done
1316     //
1317     if (ComponentName[0] == 0) {
1318       if (DirIntended && OFile->ODir == NULL) {
1319         return EFI_NOT_FOUND;
1320       }
1321 
1322       NewFileName[0] = 0;
1323       break;
1324     }
1325     //
1326     // If "dot", then current
1327     //
1328     if (StrCmp (ComponentName, L".") == 0) {
1329       continue;
1330     }
1331     //
1332     // If "dot dot", then parent
1333     //
1334     if (StrCmp (ComponentName, L"..") == 0) {
1335       if (OFile->Parent == NULL) {
1336         return EFI_INVALID_PARAMETER;
1337       }
1338       OFile = OFile->Parent;
1339       continue;
1340     }
1341 
1342     if (!FatFileNameIsValid (ComponentName, NewFileName)) {
1343       return EFI_INVALID_PARAMETER;
1344     }
1345     //
1346     // We have a component name, try to open it
1347     //
1348     if (OFile->ODir == NULL) {
1349       //
1350       // This file isn't a directory, can't open it
1351       //
1352       return EFI_NOT_FOUND;
1353     }
1354     //
1355     // Search the compName in the directory
1356     //
1357     Status = FatSearchODir (OFile, NewFileName, &DirEnt);
1358     if (EFI_ERROR (Status)) {
1359       return Status;
1360     }
1361 
1362     if (DirEnt == NULL) {
1363       //
1364       // component name is not found in the directory
1365       //
1366       if (*Next != 0) {
1367         return EFI_NOT_FOUND;
1368       }
1369 
1370       if (DirIntended && (Attributes & FAT_ATTRIBUTE_DIRECTORY) == 0) {
1371         return EFI_INVALID_PARAMETER;
1372       }
1373       //
1374       // It's the last component name - return with the open
1375       // path and the remaining name
1376       //
1377       break;
1378     }
1379 
1380     Status = FatOpenDirEnt (OFile, DirEnt);
1381     if (EFI_ERROR (Status)) {
1382       return Status;
1383     }
1384 
1385     OFile = DirEnt->OFile;
1386   }
1387 
1388   *PtrOFile = OFile;
1389   return EFI_SUCCESS;
1390 }
1391 
1392