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