xref: /reactos/boot/environ/lib/io/file.c (revision 48cc7814)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/io/file.c
5  * PURPOSE:         Boot Library File Management Routines
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 
13 /* DATA VARIABLES ************************************************************/
14 
15 PVOID* FileTable;
16 ULONG FileEntries;
17 
18 LIST_ENTRY RegisteredFileSystems;
19 BL_FILE_SYSTEM_REGISTRATION_TABLE FatRegisterFunctionTable =
20 {
21     FatInitialize,
22     NULL,
23     FatMount,
24     NULL
25 };
26 BL_FILE_SYSTEM_REGISTRATION_TABLE EtfsRegisterFunctionTable =
27 {
28     EtfsInitialize,
29     NULL,
30     EtfsMount,
31     NULL
32 };
33 
34 extern ULONG DmTableEntries;
35 extern PVOID* DmDeviceTable;
36 
37 /* FUNCTIONS *****************************************************************/
38 
39 PWCHAR
40 FileIoCopyParentDirectoryPath (
41     _In_ PWCHAR FilePath
42     )
43 {
44     SIZE_T PathSize, PathSizeWithNull;
45     PWCHAR Backslash, ParentCopy;
46 
47     PathSize = wcslen(FilePath) * sizeof(WCHAR);
48 
49     PathSizeWithNull = PathSize + sizeof(UNICODE_NULL);
50     if (PathSizeWithNull < PathSize)
51     {
52         return NULL;
53     }
54 
55     ParentCopy = BlMmAllocateHeap(PathSizeWithNull);
56     if (!ParentCopy)
57     {
58         return NULL;
59     }
60     wcsncpy(ParentCopy, FilePath, PathSizeWithNull / sizeof(WCHAR));
61 
62     Backslash = wcsrchr(ParentCopy, '\\');
63     if (!Backslash)
64     {
65         BlMmFreeHeap(ParentCopy);
66         return NULL;
67     }
68 
69     if (Backslash == ParentCopy)
70     {
71         ++Backslash;
72     }
73 
74     *Backslash = UNICODE_NULL;
75     return ParentCopy;
76 }
77 
78 PWCHAR
79 FileIoCopyFileName (
80     _In_ PWCHAR FilePath
81     )
82 {
83     PWCHAR Separator, FileCopy;
84     SIZE_T PathSize;
85 
86     Separator = wcsrchr(FilePath, '\\');
87     if (!Separator)
88     {
89         return NULL;
90     }
91 
92     PathSize = wcslen(Separator) * sizeof(WCHAR);
93 
94     FileCopy = BlMmAllocateHeap(PathSize);
95     if (!FileCopy)
96     {
97         return NULL;
98     }
99 
100     wcsncpy(FileCopy, Separator + 1, PathSize / sizeof(WCHAR));
101     return FileCopy;
102 }
103 
104 BOOLEAN
105 FileTableCompareWithSubsetAttributes (
106     _In_ PVOID Entry,
107     _In_ PVOID Argument1,
108     _In_ PVOID Argument2,
109     _In_ PVOID Argument3,
110     _In_ PVOID Argument4
111     )
112 {
113     PBL_FILE_ENTRY FileEntry = (PBL_FILE_ENTRY)Entry;
114     ULONG DeviceId = *(PULONG)Argument1;
115     PWCHAR FilePath = (PWCHAR)Argument2;
116     ULONG Flags = *(PULONG)Argument3;
117     ULONG Unknown = *(PULONG)Argument4;
118     BOOLEAN Found;
119 
120     Found = FALSE;
121 
122     if ((FileEntry->DeviceId == DeviceId) &&
123         !(_wcsicmp(FileEntry->FilePath, FilePath)) &&
124         (FileEntry->Unknown == Unknown))
125     {
126         if ((!(Flags & 1) || (FileEntry->Flags & 2)) && (!(Flags & 2) || (FileEntry->Flags & 4)))
127         {
128             if ((!(Flags & 4) || (FileEntry->Flags & 0x10000)) && ((Flags & 4) || !(FileEntry->Flags & 0x10000)))
129             {
130                 Found = TRUE;
131             }
132         }
133     }
134     return Found;
135 }
136 
137 BOOLEAN
138 FileTableCompareWithSameAttributes (
139     _In_ PVOID Entry,
140     _In_ PVOID Argument1,
141     _In_ PVOID Argument2,
142     _In_ PVOID Argument3,
143     _In_ PVOID Argument4
144     )
145 {
146     PBL_FILE_ENTRY FileEntry = (PBL_FILE_ENTRY)Entry;
147     ULONG DeviceId = *(PULONG)Argument1;
148     PWCHAR FilePath = (PWCHAR)Argument2;
149     ULONG Flags = *(PULONG)Argument3;
150     ULONG Unknown = *(PULONG)Argument4;
151     BOOLEAN Found;
152 
153     Found = FALSE;
154 
155     if ((FileEntry->DeviceId == DeviceId) &&
156         !(_wcsicmp(FileEntry->FilePath, FilePath)) &&
157         (FileEntry->Unknown == Unknown))
158     {
159         if ((!(Flags & 1) || (FileEntry->Flags & 2)) && ((Flags & 1) || !(FileEntry->Flags & 2)) && (!(Flags & 2) || (FileEntry->Flags & 4)) && ((Flags & 2) || !(FileEntry->Flags & 4)))
160         {
161             if ((!(Flags & 4) || (FileEntry->Flags & 0x10000)) && ((Flags & 4) || !(FileEntry->Flags & 0x10000)))
162             {
163                 Found = TRUE;
164             }
165         }
166     }
167     return Found;
168 }
169 
170 NTSTATUS
171 FileTableDestroyEntry (
172     _In_ PBL_FILE_ENTRY FileEntry,
173     _In_ ULONG Index
174     )
175 {
176     ULONG DeviceId;
177     PBL_DEVICE_ENTRY DeviceEntry;
178     NTSTATUS Status;
179 
180     DeviceId = FileEntry->DeviceId;
181     if (DmTableEntries > DeviceId)
182     {
183         DeviceEntry = DmDeviceTable[DeviceId];
184         if (DeviceEntry)
185         {
186             --DeviceEntry->ReferenceCount;
187         }
188     }
189 
190     Status = FileEntry->Callbacks.Close(FileEntry);
191 
192     BlMmFreeHeap(FileEntry);
193 
194     FileTable[Index] = NULL;
195     return Status;
196 }
197 
198 #define BL_FILE_PURGE_LIMIT 512
199 
200 NTSTATUS
201 FileTablePurgeEntry (
202     _In_ PVOID Entry
203     )
204 {
205     PBL_FILE_ENTRY FileEntry = (PBL_FILE_ENTRY)Entry;
206 
207     /* Don't purge opened files, or if there's less than 512 files cached */
208     if (((FileEntry->Flags & BL_FILE_ENTRY_OPENED) ||
209         (FileEntry->Flags & 0x10000)) &&
210         (FileEntries < BL_FILE_PURGE_LIMIT))
211     {
212         return STATUS_UNSUCCESSFUL;
213     }
214 
215     /* Purge the entry otherwise */
216     return FileTableDestroyEntry(FileEntry, FileEntry->FileId);
217 }
218 
219 NTSTATUS
220 BlFileClose (
221     _In_ ULONG FileId
222     )
223 {
224     PBL_FILE_ENTRY FileEntry;
225 
226     /* Validate the file ID */
227     if (FileEntries <= FileId)
228     {
229         return STATUS_INVALID_PARAMETER;
230     }
231 
232     /* Make sure a file entry actually exists */
233     FileEntry = FileTable[FileId];
234     if (!FileEntry)
235     {
236         return STATUS_INVALID_PARAMETER;
237     }
238 
239     /* And that it's actually open */
240     if (!(FileEntry->Flags & BL_FILE_ENTRY_OPENED))
241     {
242         return STATUS_INVALID_PARAMETER;
243     }
244 
245     /* Drop a reference, check if this was the last one */
246     --FileEntry->ReferenceCount;
247     if (!FileEntry->ReferenceCount)
248     {
249         /* File is no longer open */
250         FileEntry->Flags &= ~BL_FILE_ENTRY_OPENED;
251     }
252 
253     /* All good */
254     return STATUS_SUCCESS;
255 }
256 
257 NTSTATUS
258 FileIoOpen (
259     _In_ ULONG DeviceId,
260     _In_ PWCHAR FileName,
261     _In_ ULONG Flags,
262     _In_ ULONG Unknown,
263     _In_ PBL_TBL_LOOKUP_ROUTINE CompareRoutine,
264     _Out_opt_ PBL_FILE_ENTRY *NewFileEntry
265     )
266 {
267     PWCHAR FileNameCopy, ParentFileName;
268     NTSTATUS Status;
269     PBL_DEVICE_ENTRY DeviceEntry;
270     PBL_FILE_SYSTEM_ENTRY FileSystem;
271     ULONG FileId, CheckFlags;
272     PBL_FILE_ENTRY DirectoryEntry, FileEntry;
273     PLIST_ENTRY NextEntry, ListHead;
274 
275     /* Preinitialize variables for failure */
276     DirectoryEntry = NULL;
277     FileNameCopy = NULL;
278     ParentFileName = NULL;
279     Status = STATUS_SUCCESS;
280 
281     /* Bail out if the device ID is invalid */
282     if (DmTableEntries <= DeviceId)
283     {
284         return STATUS_ACCESS_DENIED;
285     }
286 
287     /* Bail out if there's no device entry */
288     DeviceEntry = DmDeviceTable[DeviceId];
289     if (!DeviceEntry)
290     {
291         return STATUS_ACCESS_DENIED;
292     }
293 
294     /* Read access is always required for touching the device */
295     CheckFlags = Flags | BL_FILE_READ_ACCESS;
296 
297     /* Check if the device is granting us read access */
298     if ((CheckFlags & BL_FILE_READ_ACCESS) &&
299         (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED) ||
300          !(DeviceEntry->Flags & BL_DEVICE_ENTRY_READ_ACCESS)))
301     {
302         EfiPrintf(L"Access denied\r\n");
303         return STATUS_ACCESS_DENIED;
304     }
305 
306     /* Check if the device is granting us write access */
307     if ((CheckFlags & BL_FILE_WRITE_ACCESS) &&
308         (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED) ||
309          !(DeviceEntry->Flags & BL_DEVICE_ENTRY_WRITE_ACCESS)))
310     {
311         EfiPrintf(L"Access denied2\r\n");
312         return STATUS_ACCESS_DENIED;
313     }
314 
315     /* Check if we already have this file open */
316     FileEntry = (PBL_FILE_ENTRY )BlTblFindEntry(FileTable,
317                                                 FileEntries,
318                                                 &FileId,
319                                                 CompareRoutine,
320                                                 &DeviceId,
321                                                 FileName,
322                                                 &Flags,
323                                                 &Unknown);
324     if (FileEntry)
325     {
326         goto FileOpened;
327     }
328 
329     /* Check if we are opening the root drive or an actual file/directory */
330     if ((*FileName != OBJ_NAME_PATH_SEPARATOR) || (FileName[1]))
331     {
332         /* Get the name of the directory */
333         ParentFileName = FileIoCopyParentDirectoryPath(FileName);
334         if (!ParentFileName)
335         {
336             Status = STATUS_NO_MEMORY;
337             goto Quickie;
338         }
339 
340         /* Open it */
341         Status = FileIoOpen(DeviceId,
342                             ParentFileName,
343                             BL_FILE_READ_ACCESS | BL_DIRECTORY_ACCESS,
344                             Unknown,
345                             FileTableCompareWithSubsetAttributes,
346                             &DirectoryEntry);
347         if (!NT_SUCCESS(Status))
348         {
349             goto Quickie;
350         }
351 
352         /* Now get the the file name itself */
353         FileNameCopy = FileIoCopyFileName(FileName);
354         if (!FileNameCopy)
355         {
356             Status = STATUS_NO_MEMORY;
357             goto Quickie;
358         }
359 
360         /* Open it */
361         Status = DirectoryEntry->Callbacks.Open(DirectoryEntry,
362                                                 FileNameCopy,
363                                                 Flags,
364                                                 &FileEntry);
365     }
366     else
367     {
368         /* We're opening the root, scan through all the file systems */
369         Status = STATUS_UNSUCCESSFUL;
370         ListHead = &RegisteredFileSystems;
371         NextEntry = ListHead->Flink;
372         while (NextEntry != ListHead)
373         {
374             /* Try to mount this one */
375             FileSystem = CONTAINING_RECORD(NextEntry, BL_FILE_SYSTEM_ENTRY, ListEntry);
376             Status = FileSystem->MountCallback(DeviceId, Unknown, &FileEntry);
377             if (NT_SUCCESS(Status))
378             {
379                 /* Mount successful */
380                 break;
381             }
382 
383             /* Try the next file system */
384             NextEntry = NextEntry->Flink;
385         }
386 
387         /* Nothing to free on this path */
388         FileNameCopy = NULL;
389     }
390 
391     /* Handle failure */
392     if (!NT_SUCCESS(Status))
393     {
394         EfiPrintf(L"Could not open file!: %lx\r\n", Status);
395         goto Quickie;
396     }
397 
398     /* Save the unknown */
399     FileEntry->Unknown = Unknown;
400 
401     /* Convert open flags into entry flags */
402     if (Flags & BL_FILE_READ_ACCESS)
403     {
404         FileEntry->Flags |= BL_FILE_ENTRY_READ_ACCESS;
405     }
406     if (Flags & BL_FILE_WRITE_ACCESS)
407     {
408         FileEntry->Flags |= BL_FILE_ENTRY_WRITE_ACCESS;
409     }
410 
411     /* Save the file into the file table */
412     Status = BlTblSetEntry(&FileTable,
413                            &FileEntries,
414                            (PVOID)FileEntry,
415                            &FileId,
416                            FileTablePurgeEntry);
417     if (!NT_SUCCESS(Status))
418     {
419         /* Close it if that failed */
420         FileEntry->Callbacks.Close(FileEntry);
421         goto Quickie;
422     }
423 
424     /* Add a reference on the device, and save our file ID  */
425     ++DeviceEntry->ReferenceCount;
426     Status = STATUS_SUCCESS;
427     FileEntry->FileId = FileId;
428 
429 FileOpened:
430     /* Add a reference to the file entry, and see if this is the first one */
431     if (++FileEntry->ReferenceCount == 1)
432     {
433         /* Reset unknowns */
434         FileEntry->TotalBytesRead = 0;
435         FileEntry->Unknown2 = 0;
436     }
437 
438     /* Set the file as opened */
439     FileEntry->Flags |= BL_FILE_ENTRY_OPENED;
440 
441     /* Not sure what this flag does */
442     if (Flags & BL_UNKNOWN_ACCESS)
443     {
444         FileEntry->Flags |= BL_FILE_ENTRY_UNKNOWN_ACCESS;
445     }
446 
447     /* If the caller wanted the entry back, return it */
448     if (NewFileEntry)
449     {
450         *NewFileEntry = FileEntry;
451     }
452 
453 Quickie:
454     /* Close the parent */
455     if (DirectoryEntry)
456     {
457         BlFileClose(DirectoryEntry->FileId);
458     }
459 
460     /* Free the parent name copy */
461     if (ParentFileName)
462     {
463         BlMmFreeHeap(ParentFileName);
464     }
465 
466     /* Free the file name copy */
467     if (FileNameCopy)
468     {
469         BlMmFreeHeap(FileNameCopy);
470     }
471 
472     /* Return back to caller */
473     return Status;
474 }
475 
476 NTSTATUS
477 BlFileOpen (
478     _In_ ULONG DeviceId,
479     _In_ PWCHAR FileName,
480     _In_ ULONG Flags,
481     _Out_ PULONG FileId
482     )
483 {
484     NTSTATUS Status;
485     PBL_FILE_ENTRY FileEntry;
486     BL_DEVICE_INFORMATION DeviceInformation;
487 
488     /* Make sure we have a valid file name, access flags and parameters */
489     if (!(FileName) ||
490         (*FileName != OBJ_NAME_PATH_SEPARATOR) ||
491         !(FileId) ||
492         !(Flags & (BL_FILE_READ_ACCESS | BL_FILE_WRITE_ACCESS)))
493     {
494         EfiPrintf(L"Invalid file options\r\n");
495         return STATUS_INVALID_PARAMETER;
496     }
497 
498     /* Get information on the underlying device */
499     Status = BlDeviceGetInformation(DeviceId, &DeviceInformation);
500     if (!NT_SUCCESS(Status))
501     {
502         EfiPrintf(L"Get device info failed: %lx\r\n", Status);
503         return Status;
504     }
505 
506     /* Make sure it's a device that can host files */
507     if ((DeviceInformation.DeviceType != DiskDevice) &&
508         (DeviceInformation.DeviceType != LegacyPartitionDevice) &&
509         (DeviceInformation.DeviceType != UdpDevice))
510     {
511         EfiPrintf(L"Invalid device type\r\n");
512         return STATUS_INVALID_PARAMETER;
513     }
514 
515     /* Open a file on this device, creating one if needed */
516     Status = FileIoOpen(DeviceId,
517                         FileName,
518                         Flags,
519                         0,
520                         FileTableCompareWithSameAttributes,
521                         &FileEntry);
522     if (NT_SUCCESS(Status))
523     {
524         /* Return the file ID back to the caller */
525         *FileId = FileEntry->FileId;
526     }
527 
528     /* All good */
529     return Status;
530 }
531 
532 NTSTATUS
533 BlFileSetInformation (
534     _In_ ULONG FileId,
535     _Out_ PBL_FILE_INFORMATION FileInfo
536     )
537 {
538     PBL_FILE_ENTRY FileEntry;
539 
540     /* Make sure caller passed this in */
541     if (!FileInfo)
542     {
543         return STATUS_INVALID_PARAMETER;
544     }
545 
546     /* Validate file ID */
547     if (FileId > FileEntries)
548     {
549         return STATUS_INVALID_PARAMETER;
550     }
551 
552     /* Make sure an opened file exits with this ID */
553     FileEntry = FileTable[FileId];
554     if (!(FileEntry) || !(FileEntry->Flags & BL_FILE_ENTRY_OPENED))
555     {
556         return STATUS_INVALID_PARAMETER;
557     }
558 
559     /* Do the I/O operation */
560     return FileEntry->Callbacks.SetInfo(FileEntry, FileInfo);
561 }
562 
563 NTSTATUS
564 BlFileGetInformation (
565     _In_ ULONG FileId,
566     _In_ PBL_FILE_INFORMATION FileInfo
567     )
568 {
569     PBL_FILE_ENTRY FileEntry;
570 
571     /* Make sure caller passed this in */
572     if (!FileInfo)
573     {
574         return STATUS_INVALID_PARAMETER;
575     }
576 
577     /* Validate file ID */
578     if (FileId > FileEntries)
579     {
580         return STATUS_INVALID_PARAMETER;
581     }
582 
583     /* Make sure an opened file exits with this ID */
584     FileEntry = FileTable[FileId];
585     if (!(FileEntry) || !(FileEntry->Flags & BL_FILE_ENTRY_OPENED))
586     {
587         return STATUS_INVALID_PARAMETER;
588     }
589 
590     /* Do the I/O operation */
591     return FileEntry->Callbacks.GetInfo(FileEntry, FileInfo);
592 }
593 
594 NTSTATUS
595 FileInformationCheck (
596     _In_ PBL_FILE_INFORMATION FileInformation,
597     _In_ BOOLEAN Write,
598     _In_opt_ PULONG InputSize,
599     _In_opt_ PULONG BytesReturned,
600     _Out_opt_ PULONG RequiredSize
601     )
602 {
603     NTSTATUS Status;
604     ULONG Size;
605 
606     /* Initialize variables */
607     Status = STATUS_SUCCESS;
608     Size = 0;
609 
610     /* Make sure we didn't overshoot */
611     if (FileInformation->Offset > FileInformation->Size)
612     {
613         /* Bail out */
614         Status = STATUS_INVALID_PARAMETER;
615         goto Quickie;
616     }
617 
618     /* Compute the appropriate 32-bit size of this read, based on file size */
619     Size = ULONG_MAX;
620     if ((FileInformation->Size - FileInformation->Offset) <= ULONG_MAX)
621     {
622         Size = (ULONG)(FileInformation->Size) - (ULONG)(FileInformation->Offset);
623     }
624 
625     /* Check if the caller has an input buffer */
626     if (InputSize)
627     {
628         /* Is the size bigger than what the caller can handle? */
629         if (Size >= *InputSize)
630         {
631             /* Yes, so cap it at the size of the caller's buffer */
632             Size = *InputSize;
633         }
634         else if (!(BytesReturned) || (Write))
635         {
636             /* Caller's input buffer is too smaller is fatal for writes */
637             Status = STATUS_INVALID_PARAMETER;
638             goto Quickie;
639         }
640     }
641 
642 Quickie:
643     /* Does the caller want to know how big to make their buffer? */
644     if (RequiredSize)
645     {
646         /* Let them know*/
647         *RequiredSize = Size;
648     }
649 
650     /* Return final status */
651     return Status;
652 }
653 
654 NTSTATUS
655 BlFileReadEx (
656     _In_ ULONG FileId,
657     _Out_ PVOID Buffer,
658     _In_ ULONG Size,
659     _Out_ PULONG BytesReturned,
660     _In_ ULONG Flags
661     )
662 {
663     PBL_FILE_ENTRY FileEntry;
664     NTSTATUS Status;
665     ULONG OldUnknown, RequiredSize;
666     BOOLEAN ChangedUnknown;
667     BL_DEVICE_INFORMATION DeviceInfo;
668     BL_FILE_INFORMATION fileInfo;
669 
670     /* Initialize variables */
671     RtlZeroMemory(&DeviceInfo, sizeof(DeviceInfo));
672     OldUnknown = 0;
673     ChangedUnknown = FALSE;
674 
675     /* Bail out if there's no buffer */
676     if (!Buffer)
677     {
678         return STATUS_INVALID_PARAMETER;
679     }
680 
681     /* Bail out of the file ID is invalid */
682     if (FileId > FileEntries)
683     {
684         return STATUS_INVALID_PARAMETER;
685     }
686 
687     /* Bail out if there's no file opened for read access */
688     FileEntry = FileTable[FileId];
689     if (!(FileEntry) ||
690         !(FileEntry->Flags & (BL_FILE_ENTRY_OPENED | BL_FILE_ENTRY_READ_ACCESS)))
691     {
692         return STATUS_INVALID_PARAMETER;
693     }
694 
695     /* Bail out if we can't read the file's information */
696     Status = BlFileGetInformation(FileId, &fileInfo);
697     if (!NT_SUCCESS(Status))
698     {
699         return Status;
700     }
701 
702     /* Ensure the read attempt is valid, and fix up the size if needed */
703     RequiredSize = Size;
704     Status = FileInformationCheck(&fileInfo,
705                                   FALSE,
706                                   &RequiredSize,
707                                   BytesReturned,
708                                   &RequiredSize);
709     if (!NT_SUCCESS(Status))
710     {
711         /* Invalid or illegal read attempt */
712         return Status;
713     }
714 
715     /* Is there anything left to read after all? */
716     if (RequiredSize)
717     {
718         /* Check if flags 2 or 4 are set */
719         if ((Flags & 2) || (Flags & 4))
720         {
721             /* Check if this is a disk or partition device */
722             BlDeviceGetInformation(FileEntry->DeviceId, &DeviceInfo);
723             if ((DeviceInfo.DeviceType == DiskDevice) ||
724                 (DeviceInfo.DeviceType == LegacyPartitionDevice))
725             {
726                 /* Check if request flags are incompatible with device flags */
727                 if ((!(DeviceInfo.BlockDeviceInfo.Unknown & 1) && (Flags & 2)) ||
728                     (!(DeviceInfo.BlockDeviceInfo.Unknown & 2) && (Flags & 4)))
729                 {
730                     /* We're going to change the device flags */
731                     ChangedUnknown = TRUE;
732 
733                     /* Set unknown flag 1 for request flag 2 */
734                     if (Flags & 2)
735                     {
736                         DeviceInfo.BlockDeviceInfo.Unknown |= 1;
737                     }
738 
739                     /* Set unknown flag 2 for request flag 4 */
740                     if (Flags & 4)
741                     {
742                         DeviceInfo.BlockDeviceInfo.Unknown |= 2;
743                     }
744 
745                     /* Save the new device flags */
746                     BlDeviceSetInformation(FileEntry->DeviceId, &DeviceInfo);
747                 }
748             }
749         }
750 
751         /* Issue the read to the underlying file system */
752         Status = FileEntry->Callbacks.Read(FileEntry,
753                                            Buffer,
754                                            RequiredSize,
755                                            BytesReturned);
756         if (!NT_SUCCESS(Status))
757         {
758             /* Don't update the bytes read on failure */
759             RequiredSize = 0;
760         }
761     }
762     else
763     {
764         /* There's nothing to do, return success and 0 bytes */
765         Status = STATUS_SUCCESS;
766         if (BytesReturned)
767         {
768             *BytesReturned = 0;
769         }
770     }
771 
772     /* Increment the number of bytes read */
773     FileEntry->TotalBytesRead += RequiredSize;
774 
775     /* Check if the unknown flag on the device was changed during this routine */
776     if (ChangedUnknown)
777     {
778         /* Reset it back to its original value */
779         DeviceInfo.BlockDeviceInfo.Unknown = OldUnknown;
780         BlDeviceSetInformation(FileEntry->DeviceId, &DeviceInfo);
781     }
782 
783     /* Return the final status */
784     return Status;
785 }
786 
787 NTSTATUS
788 BlFileReadAtOffsetEx (
789     _In_ ULONG FileId,
790     _In_ ULONG Size,
791     _In_ ULONGLONG ByteOffset,
792     _In_ PVOID Buffer,
793     _Out_ PULONG BytesReturned,
794     _In_ ULONG Flags
795     )
796 {
797     NTSTATUS Status;
798     BL_FILE_INFORMATION FileInfo;
799     ULONG RequiredSize;
800     ULONGLONG FileOffset;
801 
802     /* Get information on the specified file */
803     Status = BlFileGetInformation(FileId, &FileInfo);
804     if (!NT_SUCCESS(Status))
805     {
806         return Status;
807     }
808 
809     /* Save the current offset, and overwrite it with the one we want */
810     FileOffset = FileInfo.Offset;
811     FileInfo.Offset = ByteOffset;
812 
813     /* Check the validity of the read and the actual size to read */
814     RequiredSize = Size;
815     Status = FileInformationCheck(&FileInfo,
816                                   FALSE,
817                                   &RequiredSize,
818                                   BytesReturned,
819                                   &RequiredSize);
820     if (!NT_SUCCESS(Status))
821     {
822         /* Bail out if the read is invalid */
823         EfiPrintf(L"File info check failure: %lx\r\n", Status);
824         return Status;
825     }
826 
827     /* Check if the offset we're requesting is not the current offset */
828     if (FileInfo.Offset != FileOffset)
829     {
830         /* Set the new offset to use */
831         Status = BlFileSetInformation(FileId, &FileInfo);
832         if (!NT_SUCCESS(Status))
833         {
834             /* Can't do much if that failed */
835             return Status;
836         }
837     }
838 
839     /* Do the read at the required offset now */
840     Status = BlFileReadEx(FileId,
841                           Buffer,
842                           RequiredSize,
843                           BytesReturned,
844                           Flags);
845     if (!NT_SUCCESS(Status))
846     {
847         /* The read failed -- had we modified the offset? */
848         if (FileInfo.Offset != FileOffset)
849         {
850             /* Restore the offset back to its original value */
851             FileInfo.Offset = FileOffset;
852             BlFileSetInformation(FileId, &FileInfo);
853         }
854     }
855 
856     /* Return the status of the read */
857     return Status;
858 }
859 
860 NTSTATUS
861 BlpFileRegisterFileSystem (
862     _In_ PBL_FS_INIT_CALLBACK InitCallback,
863     _In_ PBL_FS_DESTROY_CALLBACK DestroyCallback,
864     _In_ PBL_FS_MOUNT_CALLBACK MountCallback,
865     _In_ PBL_FS_PURGE_CALLBACK PurgeCallback,
866     _In_ ULONG Flags
867     )
868 {
869     PBL_FILE_SYSTEM_ENTRY FsEntry;
870     NTSTATUS Status;
871 
872     /* Allocate an entry */
873     FsEntry = BlMmAllocateHeap(sizeof(*FsEntry));
874     if (!FsEntry)
875     {
876         return STATUS_NO_MEMORY;
877     }
878 
879     /* Initialize the file system */
880     Status = InitCallback();
881     if (!NT_SUCCESS(Status))
882     {
883         BlMmFreeHeap(FsEntry);
884         return Status;
885     }
886 
887     /* Register the callbacks */
888     FsEntry->MountCallback = MountCallback;
889     FsEntry->DestroyCallback = DestroyCallback;
890     FsEntry->InitCallback = InitCallback;
891     FsEntry->PurgeCallback = PurgeCallback;
892 
893     /* Insert in the right location in the list */
894     if (Flags & BL_FS_REGISTER_AT_HEAD_FLAG)
895     {
896         InsertHeadList(&RegisteredFileSystems, &FsEntry->ListEntry);
897     }
898     else
899     {
900         InsertTailList(&RegisteredFileSystems, &FsEntry->ListEntry);
901     }
902 
903     /* Return */
904     return STATUS_SUCCESS;
905 }
906 
907 NTSTATUS
908 BlpFileInitialize (
909     VOID
910     )
911 {
912     NTSTATUS Status;
913 
914     /* Allocate the file table */
915     FileEntries = 16;
916     FileTable = BlMmAllocateHeap(sizeof(PBL_FILE_ENTRY) * FileEntries);
917     if (!FileTable)
918     {
919         return STATUS_INVALID_PARAMETER;
920     }
921 
922     /* Initialize it */
923     RtlZeroMemory(FileTable, sizeof(PBL_FILE_ENTRY) * FileEntries);
924     InitializeListHead(&RegisteredFileSystems);
925 
926 #if 0
927     /* Initialize the network file system */
928     Status = BlpFileRegisterFileSystem(NetRegisterFunctionTable.Init,
929                                        NetRegisterFunctionTable.Destroy,
930                                        NetRegisterFunctionTable.Mount,
931                                        NetRegisterFunctionTable.Purge,
932                                        1);
933     if (NT_SUCCESS(Status))
934     {
935         /* Initialize NTFS */
936         Status = BlpFileRegisterFileSystem(NtfsRegisterFunctionTable.Init,
937                                            NtfsRegisterFunctionTable.Destroy,
938                                            NtfsRegisterFunctionTable.Mount,
939                                            NtfsRegisterFunctionTable.Purge,
940                                            0);
941     }
942 
943     if (NT_SUCCESS(Status))
944 #endif
945     {
946         /* Initialize FAT */
947         Status = BlpFileRegisterFileSystem(FatRegisterFunctionTable.Init,
948                                            FatRegisterFunctionTable.Destroy,
949                                            FatRegisterFunctionTable.Mount,
950                                            FatRegisterFunctionTable.Purge,
951                                            0);
952     }
953 
954 #if 0
955     if (NT_SUCCESS(Status))
956     {
957         /* Initialize EXFAT (FatPlus) */
958         Status = BlpFileRegisterFileSystem(FppRegisterFunctionTable.Init,
959                                            FppRegisterFunctionTable.Destroy,
960                                            FppRegisterFunctionTable.Mount,
961                                            FppRegisterFunctionTable.Purge,
962                                            0);
963     }
964 
965     if (NT_SUCCESS(Status))
966     {
967         /* Initialize WIM */
968         Status = BlpFileRegisterFileSystem(WimRegisterFunctionTable.Init,
969                                            WimRegisterFunctionTable.Destroy,
970                                            WimRegisterFunctionTable.Mount,
971                                            WimRegisterFunctionTable.Purge,
972                                            0);
973     }
974 
975     if (NT_SUCCESS(Status))
976     {
977         /* Initialize UDFS */
978         Status = BlpFileRegisterFileSystem(UdfsRegisterFunctionTable.Init,
979                                            UdfsRegisterFunctionTable.Destroy,
980                                            UdfsRegisterFunctionTable.Mount,
981                                            UdfsRegisterFunctionTable.Purge,
982                                            0);
983     }
984 #endif
985     if (NT_SUCCESS(Status))
986     {
987         /* Initialize El-Torito CDFS */
988         Status = BlpFileRegisterFileSystem(EtfsRegisterFunctionTable.Init,
989                                            EtfsRegisterFunctionTable.Destroy,
990                                            EtfsRegisterFunctionTable.Mount,
991                                            EtfsRegisterFunctionTable.Purge,
992                                            0);
993     }
994 
995     /* Destroy the file manager if any of the file systems didn't initialize */
996     if (!NT_SUCCESS(Status))
997     {
998         if (FileTable)
999         {
1000             //BlpFileDestroy();
1001         }
1002     }
1003     return Status;
1004 }
1005