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