[BOOTLIB]:
[reactos.git] / reactos / boot / environ / lib / io / etfs.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/io/etfs.c
5 * PURPOSE: Boot Library El Torito File System Management Routines
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bl.h"
12 #include "../drivers/filesystems/cdfs_new/cd.h"
13 typedef struct _RAW_ET_VD
14 {
15 UCHAR BootIndicator;
16 UCHAR StandardId[5];
17 UCHAR Version;
18 UCHAR SystemId[32];
19 UCHAR Reserved[32];
20 ULONG BootCatalogOffset;
21 UCHAR Padding[1973];
22 } RAW_ET_VD, *PRAW_ET_VD;
23
24 /* DATA VARIABLES ************************************************************/
25
26 typedef struct _BL_ETFS_DEVICE
27 {
28 ULONG RootDirOffset;
29 ULONG RootDirSize;
30 ULONG BlockSize;
31 ULONG VolumeSize;
32 BOOLEAN IsIso;
33 PUCHAR MemoryBlock;
34 ULONG Offset;
35 } BL_ETFS_DEVICE, *PBL_ETFS_DEVICE;
36
37 typedef struct _BL_ETFS_FILE
38 {
39 ULONG DiskOffset;
40 ULONG DirOffset;
41 ULONG DirEntOffset;
42
43 BL_FILE_INFORMATION;
44
45 ULONG DeviceId;
46 } BL_ETFS_FILE, *PBL_ETFS_FILE;
47
48 ULONG EtfsDeviceTableEntries;
49 PVOID* EtfsDeviceTable;
50
51 NTSTATUS
52 EtfsOpen (
53 _In_ PBL_FILE_ENTRY Directory,
54 _In_ PWCHAR FileName,
55 _In_ ULONG Flags,
56 _Out_ PBL_FILE_ENTRY *FileEntry
57 );
58
59 NTSTATUS
60 EtfsGetInformation (
61 _In_ PBL_FILE_ENTRY FileEntry,
62 _Out_ PBL_FILE_INFORMATION FileInfo
63 );
64
65 NTSTATUS
66 EtfsSetInformation (
67 _In_ PBL_FILE_ENTRY FileEntry,
68 _In_ PBL_FILE_INFORMATION FileInfo
69 );
70
71 NTSTATUS
72 EtfsRead (
73 _In_ PBL_FILE_ENTRY FileEntry,
74 _In_ PVOID Buffer,
75 _In_ ULONG Size,
76 _Out_opt_ PULONG BytesReturned
77 );
78
79 BL_FILE_CALLBACKS EtfsFunctionTable =
80 {
81 EtfsOpen,
82 NULL,
83 EtfsRead,
84 NULL,
85 NULL,
86 EtfsGetInformation,
87 EtfsSetInformation
88 };
89
90 /* FUNCTIONS *****************************************************************/
91
92 VOID
93 EtfspGetDirectoryInfo (
94 _In_ PBL_ETFS_DEVICE EtfsDevice,
95 _In_ PRAW_DIR_REC DirEntry,
96 _Out_ PULONG FileOffset,
97 _Out_ PULONG FileSize,
98 _Out_opt_ PBOOLEAN IsDirectory
99 )
100 {
101 ULONG SectorOffset;
102 BOOLEAN IsDir;
103
104 *FileOffset = *(PULONG)DirEntry->FileLoc * EtfsDevice->BlockSize;
105 *FileOffset += (DirEntry->XarLen * EtfsDevice->BlockSize);
106
107 SectorOffset = ALIGN_DOWN_BY(*FileOffset, CD_SECTOR_SIZE);
108
109 *FileSize = *(PULONG)DirEntry->DataLen;
110
111 IsDir = DE_FILE_FLAGS(EtfsDevice->IsIso, DirEntry) & ISO_ATTR_DIRECTORY;
112 if (IsDir)
113 {
114 *FileSize += ALIGN_UP_BY(SectorOffset, CD_SECTOR_SIZE) - SectorOffset;
115 }
116
117 if (IsDirectory)
118 {
119 *IsDirectory = IsDir;
120 }
121 }
122
123 USHORT
124 EtfspGetDirentNameLength (
125 _In_ PRAW_DIR_REC DirEntry
126 )
127 {
128 USHORT Length, RealLength;
129 PUCHAR Pos;
130
131 RealLength = Length = DirEntry->FileIdLen;
132 for (Pos = &DirEntry->FileIdLen + Length; Length; --Pos)
133 {
134 --Length;
135
136 if (*Pos == ';')
137 {
138 RealLength = Length;
139 break;
140 }
141 }
142
143 Length = RealLength;
144 for (Pos = &DirEntry->FileIdLen + Length; Length; --Pos)
145 {
146 --Length;
147
148 if (*Pos != '.')
149 {
150 break;
151 }
152
153 RealLength = Length;
154 }
155
156 return RealLength;
157 }
158
159 LONG
160 EtfspCompareNames (
161 __in PSTRING Name1,
162 __in PUNICODE_STRING Name2
163 )
164 {
165 ULONG i, l1, l2, l;
166
167 l1 = Name1->Length;
168 l2 = Name2->Length / sizeof(WCHAR);
169 l = min(l1, l2);
170
171 for (i = 0; i < l; i++)
172 {
173 if (toupper(Name1->Buffer[i]) != toupper(Name2->Buffer[i]))
174 {
175 return toupper(Name1->Buffer[i]) - toupper(Name2->Buffer[i]);
176 }
177 }
178
179 if (l2 <= l1)
180 {
181 return l2 < l1;
182 }
183 else
184 {
185 return -1;
186 }
187 }
188
189 BOOLEAN
190 EtfspFileMatch (
191 _In_ PRAW_DIR_REC DirEntry,
192 _In_ PUNICODE_STRING FileName
193 )
194 {
195 BOOLEAN Match;
196 USHORT Length;
197 ANSI_STRING DirName;
198
199 if ((DirEntry->FileIdLen != 1) ||
200 ((DirEntry->FileId[0] != 0) && (DirEntry->FileId[0] != 1)))
201 {
202 Length = EtfspGetDirentNameLength(DirEntry);
203 DirName.Length = Length;
204 DirName.MaximumLength = Length;
205 DirName.Buffer = (PCHAR)DirEntry->FileId;
206
207 Match = EtfspCompareNames(&DirName, FileName);
208 }
209 else
210 {
211 Match = -1;
212 }
213 return Match;
214 }
215
216 NTSTATUS
217 EtfspGetDirent (
218 _In_ PBL_FILE_ENTRY DirectoryEntry,
219 _Out_ PRAW_DIR_REC *DirEntry,
220 _Inout_ PULONG DirentOffset
221 )
222 {
223 PBL_ETFS_FILE EtfsFile;
224 ULONG FileOffset, DirectoryOffset, AlignedOffset, RemainderOffset;
225 ULONG DeviceId, ReadSize, DirLen;
226 PBL_ETFS_DEVICE EtfsDevice;
227 BOOLEAN NeedRead, IsMulti;
228 NTSTATUS result;
229 PRAW_DIR_REC DirEnt;
230 PUCHAR MemoryBlock;
231
232 EtfsFile = DirectoryEntry->FsSpecificData;
233 DeviceId = EtfsFile->DeviceId;
234 FileOffset = EtfsFile->DiskOffset;
235 EtfsDevice = EtfsDeviceTable[DeviceId];
236
237 DirectoryOffset = *DirentOffset;
238 MemoryBlock = EtfsDevice->MemoryBlock;
239
240 IsMulti = 0;
241
242 AlignedOffset = (FileOffset + *DirentOffset) & ~CD_SECTOR_SIZE;
243 RemainderOffset = *DirentOffset + FileOffset - AlignedOffset;
244
245 ReadSize = 2048 - RemainderOffset;
246 NeedRead = AlignedOffset == EtfsDevice->Offset ? 0 : 1;
247
248 ReadAgain:
249 if (DirectoryOffset >= EtfsFile->Size)
250 {
251 return STATUS_NO_SUCH_FILE;
252 }
253
254 while (ReadSize < MIN_DIR_REC_SIZE)
255 {
256 DirectoryOffset += ReadSize;
257 AlignedOffset += 2048;
258 ReadSize = 2048;
259 RemainderOffset = 0;
260 NeedRead = 1;
261 if (DirectoryOffset >= EtfsFile->Size)
262 {
263 return STATUS_NO_SUCH_FILE;
264 }
265 }
266
267 if (NeedRead)
268 {
269 result = BlDeviceReadAtOffset(DirectoryEntry->DeviceId,
270 CD_SECTOR_SIZE,
271 AlignedOffset,
272 MemoryBlock,
273 NULL);
274 if (!NT_SUCCESS(result))
275 {
276 EfiPrintf(L"Device read failed %lx\r\n", result);
277 return result;
278 }
279
280 NeedRead = FALSE;
281 EtfsDevice->Offset = AlignedOffset;
282 }
283
284 if (!*(MemoryBlock + RemainderOffset))
285 {
286 AlignedOffset += 2048;
287 NeedRead = TRUE;
288
289 RemainderOffset = 0;
290 DirectoryOffset += ReadSize;
291 ReadSize = 2048;
292 goto ReadAgain;
293 }
294
295 DirEnt = (PRAW_DIR_REC)(MemoryBlock + RemainderOffset);
296 DirLen = DirEnt->DirLen;
297 if (DirLen > ReadSize)
298 {
299 EfiPrintf(L"Dir won't fit %lx %lx\r\n", DirLen, ReadSize);
300 return STATUS_NO_SUCH_FILE;
301 }
302
303 if (IsMulti)
304 {
305 if (!(DE_FILE_FLAGS(EtfsDevice->IsIso, DirEnt) & ISO_ATTR_MULTI))
306 {
307 IsMulti = TRUE;
308 }
309 }
310 else if (DE_FILE_FLAGS(EtfsDevice->IsIso, DirEnt) & ISO_ATTR_MULTI)
311 {
312 IsMulti = TRUE;
313 }
314 else
315 {
316 if ((DirEnt->FileIdLen != 1) ||
317 ((DirEnt->FileId[0] != 0) && (DirEnt->FileId[0] != 1)))
318 {
319 goto Quickie;
320 }
321 }
322
323 RemainderOffset += DirLen;
324 DirectoryOffset += DirLen;
325 ReadSize -= DirLen;
326 goto ReadAgain;
327
328 Quickie:
329 *DirEntry = DirEnt;
330 *DirentOffset = DirectoryOffset;
331 return STATUS_SUCCESS;
332 }
333
334 NTSTATUS
335 EtfspSearchForDirent (
336 _In_ PBL_FILE_ENTRY DirectoryEntry,
337 _In_ PWCHAR FileName,
338 _Out_ PRAW_DIR_REC *DirEntry,
339 _Out_ PULONG DirentOffset
340 )
341 {
342 UNICODE_STRING Name;
343 ULONG NextOffset;
344 PRAW_DIR_REC DirEnt;
345 NTSTATUS Status;
346
347 RtlInitUnicodeString(&Name, FileName);
348 for (NextOffset = *DirentOffset;
349 ;
350 NextOffset = NextOffset + DirEnt->DirLen)
351 {
352 Status = EtfspGetDirent(DirectoryEntry, &DirEnt, &NextOffset);
353 if (!NT_SUCCESS(Status))
354 {
355 return STATUS_NO_SUCH_FILE;
356 }
357
358 if (!EtfspFileMatch(DirEnt, &Name))
359 {
360 break;
361 }
362 }
363
364 *DirEntry = DirEnt;
365 *DirentOffset = NextOffset;
366 return 0;
367 }
368
369 NTSTATUS
370 EtfspCachedSearchForDirent (
371 _In_ PBL_FILE_ENTRY DirectoryEntry,
372 _In_ PWCHAR FileName,
373 _Out_ PRAW_DIR_REC *DirEntry,
374 _Out_ PULONG DirOffset,
375 _In_ BOOLEAN KeepOffset
376 )
377 {
378 PBL_ETFS_FILE EtfsFile;
379 PBL_ETFS_DEVICE EtfsDevice;
380 NTSTATUS Status;
381 ULONG DirentOffset;
382 PRAW_DIR_REC Dirent;
383 UNICODE_STRING Name;
384
385 EtfsFile = DirectoryEntry->FsSpecificData;
386 EtfsDevice = EtfsDeviceTable[EtfsFile->DeviceId];
387 RtlInitUnicodeString(&Name, FileName);
388 DirentOffset = EtfsFile->DirEntOffset;
389
390 if ((KeepOffset) ||
391 (ALIGN_DOWN_BY((DirentOffset + EtfsFile->DiskOffset), CD_SECTOR_SIZE) ==
392 EtfsDevice->Offset))
393 {
394 Status = EtfspGetDirent(DirectoryEntry, &Dirent, &DirentOffset);
395 if (NT_SUCCESS(Status))
396 {
397 if (!EtfspFileMatch(Dirent, &Name))
398 {
399 *DirEntry = Dirent;
400 *DirOffset = DirentOffset;
401 return STATUS_SUCCESS;
402 }
403 }
404 else
405 {
406 DirentOffset = 0;
407 }
408 }
409 else
410 {
411 DirentOffset = 0;
412 }
413
414 Status = EtfspSearchForDirent(DirectoryEntry,
415 FileName,
416 DirEntry,
417 &DirentOffset);
418 if (!(NT_SUCCESS(Status)) && (DirentOffset))
419 {
420 DirentOffset = 0;
421 Status = EtfspSearchForDirent(DirectoryEntry,
422 FileName,
423 DirEntry,
424 &DirentOffset);
425 }
426
427 if (NT_SUCCESS(Status))
428 {
429 *DirOffset = DirentOffset;
430 }
431
432 return Status;
433 }
434
435 NTSTATUS
436 EtfsRead (
437 _In_ PBL_FILE_ENTRY FileEntry,
438 _In_ PVOID Buffer,
439 _In_ ULONG Size,
440 _Out_opt_ PULONG BytesReturned
441 )
442 {
443 ULONG BytesRead;
444 PBL_ETFS_FILE EtfsFile;
445 NTSTATUS Status;
446
447 /* Assume failure for now */
448 BytesRead = 0;
449
450 /* Make sure that the read is within the file's boundaries */
451 EtfsFile = FileEntry->FsSpecificData;
452 if ((Size + EtfsFile->Offset) > EtfsFile->Size)
453 {
454 /* Bail out otherwise */
455 Status = STATUS_INVALID_PARAMETER;
456 }
457 else
458 {
459 /* Read the offset that matches this file's offset, on the disk */
460 Status = BlDeviceReadAtOffset(FileEntry->DeviceId,
461 Size,
462 EtfsFile->Offset + EtfsFile->DiskOffset,
463 Buffer,
464 &BytesRead);
465 if (NT_SUCCESS(Status))
466 {
467 /* Update the file offset and return the size as having been read */
468 EtfsFile->Offset += Size;
469 BytesRead = Size;
470 }
471 }
472
473 /* Check if caller wanted to know how many bytes were read */
474 if (BytesReturned)
475 {
476 /* Return the value */
477 *BytesReturned = BytesRead;
478 }
479
480 /* All done */
481 return Status;
482 }
483
484 NTSTATUS
485 EtfsSetInformation (
486 _In_ PBL_FILE_ENTRY FileEntry,
487 _In_ PBL_FILE_INFORMATION FileInfo
488 )
489 {
490 PBL_ETFS_FILE EtfsFile;
491 BL_FILE_INFORMATION LocalFileInfo;
492
493 /* Get the underlying ETFS file data structure */
494 EtfsFile = (PBL_ETFS_FILE)FileEntry->FsSpecificData;
495
496 /* Make a copy of the incoming attributes, but ignore the new offset */
497 LocalFileInfo = *FileInfo;
498 LocalFileInfo.Offset = EtfsFile->Offset;
499
500 /* Check if these match exactly the current file */
501 if (!RtlEqualMemory(&LocalFileInfo, &EtfsFile->Size, sizeof(*FileInfo)))
502 {
503 /* Nope -- which means caller is trying to change an immutable */
504 EfiPrintf(L"Incorrect information change\r\n");
505 return STATUS_INVALID_PARAMETER;
506 }
507
508 /* Is the offset past the end of the file? */
509 if (FileInfo->Offset >= EtfsFile->Size)
510 {
511 /* Don't allow EOF */
512 EfiPrintf(L"Offset too large: %lx vs %lx \r\n", FileInfo->Offset, EtfsFile->Size);
513 return STATUS_INVALID_PARAMETER;
514 }
515
516 /* Update the offset */
517 EtfsFile->Offset = FileInfo->Offset;
518 return STATUS_SUCCESS;
519 }
520
521 NTSTATUS
522 EtfsGetInformation (
523 _In_ PBL_FILE_ENTRY FileEntry,
524 _Out_ PBL_FILE_INFORMATION FileInfo
525 )
526 {
527 PBL_ETFS_FILE EtfsFile;
528
529 /* Get the underlying ETFS file data structure */
530 EtfsFile = (PBL_ETFS_FILE)FileEntry->FsSpecificData;
531
532 /* Copy the cached information structure within it */
533 RtlCopyMemory(FileInfo, &EtfsFile->Size, sizeof(*FileInfo));
534 return STATUS_SUCCESS;
535 }
536
537 NTSTATUS
538 EtfsOpen (
539 _In_ PBL_FILE_ENTRY Directory,
540 _In_ PWCHAR FileName,
541 _In_ ULONG Flags,
542 _Out_ PBL_FILE_ENTRY *FileEntry
543 )
544 {
545 PBL_ETFS_DEVICE EtfsDevice;
546 NTSTATUS Status;
547 PBL_FILE_ENTRY NewFile;
548 PWCHAR FilePath, FormatString;
549 PBL_ETFS_FILE EtfsFile;
550 ULONG DeviceId, FileSize, DirOffset, FileOffset, Size;
551 PRAW_DIR_REC DirEntry;
552 BOOLEAN IsDirectory;
553
554 EfiPrintf(L"Attempting to open file %s in directory %s\r\n", FileName, Directory->FilePath);
555
556 EtfsFile = Directory->FsSpecificData;
557 DeviceId = EtfsFile->DeviceId;
558 EtfsDevice = EtfsDeviceTable[DeviceId];
559
560 /* Find the given file (or directory) in the given directory */
561 Status = EtfspCachedSearchForDirent(Directory,
562 FileName,
563 &DirEntry,
564 &DirOffset,
565 FALSE);
566 if (!NT_SUCCESS(Status))
567 {
568 EfiPrintf(L"no dirent found: %lx\r\n", Status);
569 return Status;
570 }
571
572 /* Find out information about the file (or directory) we found */
573 EtfspGetDirectoryInfo(EtfsDevice,
574 DirEntry,
575 &FileOffset,
576 &FileSize,
577 &IsDirectory);
578
579 NewFile = BlMmAllocateHeap(sizeof(*NewFile));
580 if (!NewFile)
581 {
582 return STATUS_NO_MEMORY;
583 }
584 RtlZeroMemory(NewFile, sizeof(*NewFile));
585
586 Size = wcslen(Directory->FilePath) + wcslen(FileName) + 2;
587
588 FilePath = BlMmAllocateHeap(Size * sizeof(WCHAR));
589 if (!FilePath)
590 {
591 Status = STATUS_NO_MEMORY;
592 goto Quickie;
593 }
594
595 EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
596 if (!EtfsFile)
597 {
598 Status = STATUS_NO_MEMORY;
599 goto Quickie;
600 }
601
602 RtlZeroMemory(NewFile, sizeof(*EtfsFile));
603
604 NewFile->DeviceId = Directory->DeviceId;
605 FormatString = L"%ls%ls";
606 if (Directory->FilePath[1])
607 {
608 FormatString = L"%ls\\%ls";
609 }
610
611 _snwprintf(FilePath, Size, FormatString, Directory->FilePath, FileName);
612 NewFile->FilePath = FilePath;
613
614 RtlCopyMemory(&NewFile->Callbacks,
615 &EtfsFunctionTable,
616 sizeof(NewFile->Callbacks));
617 EtfsFile->DiskOffset = FileOffset;
618 EtfsFile->DirOffset = DirOffset;
619 EtfsFile->Size = FileSize;
620 EtfsFile->DeviceId = DeviceId;
621 if (IsDirectory)
622 {
623 EtfsFile->Flags |= 1;
624 NewFile->Flags |= 0x10000;
625 }
626 EtfsFile->FsName = L"cdfs";
627
628 NewFile->FsSpecificData = EtfsFile;
629 *FileEntry = NewFile;
630 return Status;
631
632 Quickie:
633
634 if (NewFile->FilePath)
635 {
636 BlMmFreeHeap(NewFile->FilePath);
637 }
638
639 if (NewFile->FsSpecificData)
640 {
641 BlMmFreeHeap(NewFile->FsSpecificData);
642 }
643
644 BlMmFreeHeap(NewFile);
645 return Status;
646 }
647
648 NTSTATUS
649 EtfspCheckCdfs (
650 _In_ PBL_ETFS_DEVICE EtfsDevice,
651 _In_ ULONG DeviceId,
652 _Out_ PRAW_ISO_VD *VolumeDescriptor,
653 _Out_ PBOOLEAN VolumeIsIso
654 )
655 {
656 EfiPrintf(L"Raw Cdfs not implemented\r\n");
657 return STATUS_NOT_IMPLEMENTED;
658 }
659
660 NTSTATUS
661 EtfspCheckEtfs (
662 _In_ PBL_ETFS_DEVICE EtfsDevice,
663 _In_ ULONG DeviceId,
664 _Out_ PRAW_ISO_VD *VolumeDescriptor,
665 _Out_ PBOOLEAN VolumeIsIso
666 )
667 {
668 PRAW_ISO_VD IsoVd;
669 PRAW_ET_VD EtVd;
670 NTSTATUS Status;
671 BOOLEAN IsIso;
672 BL_DEVICE_INFORMATION DeviceInformation;
673 ULONG Unknown, BytesRead;
674 ANSI_STRING CompareString, String;
675
676 /* Save our static buffer pointer */
677 IsoVd = (PRAW_ISO_VD)EtfsDevice->MemoryBlock;
678 EtVd = (PRAW_ET_VD)IsoVd;
679
680 /* First, read the El Torito Volume Descriptor */
681 BlDeviceGetInformation(DeviceId, &DeviceInformation);
682 Unknown = DeviceInformation.BlockDeviceInfo.Unknown;
683 DeviceInformation.BlockDeviceInfo.Unknown |= 1;
684 BlDeviceSetInformation(DeviceId, &DeviceInformation);
685 Status = BlDeviceReadAtOffset(DeviceId,
686 CD_SECTOR_SIZE,
687 (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE,
688 EtfsDevice->MemoryBlock,
689 &BytesRead);
690 DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
691 BlDeviceSetInformation(DeviceId, &DeviceInformation);
692 if (!NT_SUCCESS(Status))
693 {
694 EfiPrintf(L" read failed\r\n");
695 return Status;
696 }
697
698 /* Remember that's where we last read */
699 EtfsDevice->Offset = (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE;
700
701 /* Check if it's EL TORITO! */
702 RtlInitString(&String, "EL TORITO SPECIFICATION");
703 CompareString.Buffer = (PCHAR)EtVd->SystemId;
704 CompareString.Length = 23;
705 CompareString.MaximumLength = 23;
706 if (!RtlEqualString(&CompareString, &String, TRUE))
707 {
708 return STATUS_UNSUCCESSFUL;
709 }
710
711 /* Check the version and boot indicator */
712 if ((EtVd->Version != 1) || (EtVd->BootIndicator))
713 {
714 return STATUS_UNSUCCESSFUL;
715 }
716
717 /* Check if it has the CD0001 identifier */
718 RtlInitString(&String, ISO_VOL_ID);
719 CompareString.Buffer = (PCHAR)EtVd->StandardId;
720 CompareString.Length = 5;
721 CompareString.MaximumLength = 5;
722 if (!RtlEqualString(&CompareString, &String, TRUE))
723 {
724 return STATUS_UNSUCCESSFUL;
725 }
726
727 /* Step two, we now want to read the ISO Volume Descriptor */
728 DeviceInformation.BlockDeviceInfo.Unknown |= 1u;
729 BlDeviceSetInformation(DeviceId, &DeviceInformation);
730 Status = BlDeviceReadAtOffset(DeviceId,
731 CD_SECTOR_SIZE,
732 FIRST_VD_SECTOR * CD_SECTOR_SIZE,
733 EtfsDevice->MemoryBlock,
734 &BytesRead);
735 DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
736 BlDeviceSetInformation(DeviceId, &DeviceInformation);
737 if (!NT_SUCCESS(Status))
738 {
739 return Status;
740 }
741
742 /* Remember where we left off */
743 EtfsDevice->Offset = FIRST_VD_SECTOR * CD_SECTOR_SIZE;
744
745 /* This should also say CD0001 */
746 CompareString.Buffer = (PCHAR)IsoVd->StandardId;
747 CompareString.Length = 5;
748 CompareString.MaximumLength = 5;
749 IsIso = RtlEqualString(&CompareString, &String, TRUE);
750 if (!IsIso)
751 {
752 return STATUS_UNSUCCESSFUL;
753 }
754
755 /* And should be a version we support */
756 if ((IsoVd->Version != VERSION_1) || (IsoVd->DescType != VD_PRIMARY))
757 {
758 return STATUS_UNSUCCESSFUL;
759 }
760
761 /* Return back to the caller */
762 *VolumeDescriptor = IsoVd;
763 *VolumeIsIso = IsIso;
764 EfiPrintf(L"Recognized!!!\r\n");
765 return STATUS_SUCCESS;
766 }
767
768 NTSTATUS
769 EtfspDeviceContextDestroy (
770 _In_ PBL_ETFS_DEVICE EtfsDevice
771 )
772 {
773 if (EtfsDevice->MemoryBlock)
774 {
775 BlMmFreeHeap(EtfsDevice->MemoryBlock);
776 }
777
778 BlMmFreeHeap(EtfsDevice);
779
780 return STATUS_SUCCESS;
781 }
782
783 NTSTATUS
784 EtfspCreateContext (
785 _In_ ULONG DeviceId,
786 _Out_ PBL_ETFS_DEVICE *EtfsDevice
787 )
788 {
789 PBL_ETFS_DEVICE NewContext;
790 PVOID MemoryBlock;
791 NTSTATUS Status;
792 BOOLEAN IsIso;
793 PRAW_ISO_VD RawVd;
794
795 NewContext = (PBL_ETFS_DEVICE)BlMmAllocateHeap(sizeof(*NewContext));
796 if (!NewContext)
797 {
798 return STATUS_NO_MEMORY;
799 }
800 RtlZeroMemory(NewContext, sizeof(*NewContext));
801
802 MemoryBlock = BlMmAllocateHeap(CD_SECTOR_SIZE);
803 NewContext->MemoryBlock = MemoryBlock;
804 if (!MemoryBlock)
805 {
806 Status = STATUS_NO_MEMORY;
807 goto Quickie;
808 }
809
810 Status = EtfspCheckEtfs(NewContext, DeviceId, &RawVd, &IsIso);
811 if (!NT_SUCCESS(Status))
812 {
813 EfiPrintf(L"Drive not EDFS. Checking for CDFS: %lx\r\n");
814 Status = EtfspCheckCdfs(NewContext, DeviceId, &RawVd, &IsIso);
815 }
816
817 if (!NT_SUCCESS(Status))
818 {
819 EfiPrintf(L"Drive not CDFS. Failing: %lx\r\n");
820 goto Quickie;
821 }
822
823 NewContext->IsIso = IsIso;
824 NewContext->BlockSize = RVD_LB_SIZE(RawVd, IsIso);
825 NewContext->VolumeSize = RVD_VOL_SIZE(RawVd, IsIso);
826
827 EtfspGetDirectoryInfo(NewContext,
828 (PRAW_DIR_REC)RVD_ROOT_DE(RawVd, IsIso),
829 &NewContext->RootDirOffset,
830 &NewContext->RootDirSize,
831 0);
832 Status = STATUS_SUCCESS;
833
834 Quickie:
835 if (!NT_SUCCESS(Status))
836 {
837 EtfspDeviceContextDestroy(NewContext);
838 NewContext = NULL;
839 }
840
841 *EtfsDevice = NewContext;
842 return Status;
843 }
844
845 NTSTATUS
846 EtfspDeviceTableDestroyEntry (
847 _In_ PBL_ETFS_DEVICE EtfsDevice,
848 _In_ ULONG Index
849 )
850 {
851 EtfspDeviceContextDestroy(EtfsDevice);
852 EtfsDeviceTable[Index] = NULL;
853
854 return STATUS_SUCCESS;
855 }
856
857 NTSTATUS
858 EtfsMount (
859 _In_ ULONG DeviceId,
860 _In_ ULONG Unknown,
861 _Out_ PBL_FILE_ENTRY* FileEntry
862 )
863 {
864 PBL_ETFS_DEVICE EtfsDevice = NULL;
865 PBL_FILE_ENTRY RootEntry;
866 NTSTATUS Status;
867 PBL_ETFS_FILE EtfsFile;
868
869 EfiPrintf(L"Trying to mount as ETFS...\r\n");
870
871 Status = EtfspCreateContext(DeviceId, &EtfsDevice);
872 if (!NT_SUCCESS(Status))
873 {
874 EfiPrintf(L"ETFS context failed: %lx\r\n");
875 return Status;
876 }
877
878 Status = BlTblSetEntry(&EtfsDeviceTable,
879 &EtfsDeviceTableEntries,
880 EtfsDevice,
881 &DeviceId,
882 TblDoNotPurgeEntry);
883 if (!NT_SUCCESS(Status))
884 {
885 EtfspDeviceContextDestroy(EtfsDevice);
886 return Status;
887 }
888
889 RootEntry = BlMmAllocateHeap(sizeof(*RootEntry));
890 if (!RootEntry)
891 {
892 Status = STATUS_NO_MEMORY;
893 goto Quickie;
894 }
895
896 RtlZeroMemory(RootEntry, sizeof(*RootEntry));
897
898 RootEntry->FilePath = BlMmAllocateHeap(4);
899 if (!RootEntry->FilePath)
900 {
901 Status = STATUS_NO_MEMORY;
902 goto Quickie;
903 }
904
905 wcsncpy(RootEntry->FilePath, L"\\", 1);
906
907 RootEntry->DeviceId = DeviceId;
908 RtlCopyMemory(&RootEntry->Callbacks,
909 &EtfsFunctionTable,
910 sizeof(RootEntry->Callbacks));
911
912 EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
913 if (!EtfsFile)
914 {
915 Status = STATUS_NO_MEMORY;
916 goto Quickie;
917 }
918
919 RootEntry->Flags |= 0x10000;
920
921 RtlZeroMemory(EtfsFile, sizeof(*EtfsFile));
922 RootEntry->FsSpecificData = EtfsFile;
923 EtfsFile->DeviceId = DeviceId;
924 EtfsFile->Flags |= 1;
925 EtfsFile->DiskOffset = EtfsDevice->RootDirOffset;
926 EtfsFile->DirOffset = 0;
927 EtfsFile->Size = EtfsDevice->RootDirSize;
928 EtfsFile->FsName = L"cdfs";
929 *FileEntry = RootEntry;
930
931 return STATUS_SUCCESS;
932
933 Quickie:
934 if (RootEntry->FilePath)
935 {
936 BlMmFreeHeap(RootEntry->FilePath);
937 }
938 if (RootEntry->FsSpecificData)
939 {
940 BlMmFreeHeap(RootEntry->FsSpecificData);
941 }
942 if (RootEntry)
943 {
944 BlMmFreeHeap(RootEntry);
945 }
946
947 EtfspDeviceTableDestroyEntry(EtfsDevice, DeviceId);
948
949 return Status;
950 }
951
952 NTSTATUS
953 EtfsInitialize (
954 VOID
955 )
956 {
957 NTSTATUS Status;
958
959 /* Allocate the device table with 2 entries*/
960 EtfsDeviceTableEntries = 2;
961 EtfsDeviceTable = BlMmAllocateHeap(sizeof(PBL_FILE_ENTRY) *
962 EtfsDeviceTableEntries);
963 if (EtfsDeviceTable)
964 {
965 /* Zero it out */
966 RtlZeroMemory(EtfsDeviceTable,
967 sizeof(PBL_FILE_ENTRY) * EtfsDeviceTableEntries);
968 Status = STATUS_SUCCESS;
969 }
970 else
971 {
972 /* No memory, fail */
973 Status = STATUS_NO_MEMORY;
974 }
975
976 /* Return back to caller */
977 return Status;
978 }
979