a3f4b6e581e104af5c497c1fd4a4dbce01f63e35
[reactos.git] / boot / environ / lib / io / file.c
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 ULONG 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 ULONG 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 }