]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/EnhancedFatDxe/DirectoryManage.c
BaseTools: Library hashing fix and optimization for --hash feature
[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 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
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 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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