]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/EnhancedFatDxe/DirectoryManage.c
FatPkg/EnhancedFatDxe: Make the comments align with EDKIIcoding style
[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 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
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
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
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
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
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
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
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
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
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
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 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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