xref: /reactos/boot/environ/lib/io/etfs.c (revision 9d33a205)
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 <cdfs/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->FileId + Length - 1; 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->FileId + Length - 1; 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;
551     SIZE_T Size;
552     PRAW_DIR_REC DirEntry;
553     BOOLEAN IsDirectory;
554 
555     EtfsFile = Directory->FsSpecificData;
556     DeviceId = EtfsFile->DeviceId;
557     EtfsDevice = EtfsDeviceTable[DeviceId];
558 
559     /* Find the given file (or directory) in the given directory */
560     Status = EtfspCachedSearchForDirent(Directory,
561                                         FileName,
562                                         &DirEntry,
563                                         &DirOffset,
564                                         FALSE);
565     if (!NT_SUCCESS(Status))
566     {
567         return Status;
568     }
569 
570     /* Find out information about the file (or directory) we found */
571     EtfspGetDirectoryInfo(EtfsDevice,
572                           DirEntry,
573                           &FileOffset,
574                           &FileSize,
575                           &IsDirectory);
576 
577     /* Allocate a file entry */
578     NewFile = BlMmAllocateHeap(sizeof(*NewFile));
579     if (!NewFile)
580     {
581         return STATUS_NO_MEMORY;
582     }
583 
584     /* Zero it out */
585     RtlZeroMemory(NewFile, sizeof(*NewFile));
586 
587     /* Figure out the size of the path and filename plus a slash and NUL */
588     Size = wcslen(Directory->FilePath) + wcslen(FileName) + 2;
589     FilePath = BlMmAllocateHeap(Size * sizeof(WCHAR));
590     if (!FilePath)
591     {
592         Status = STATUS_NO_MEMORY;
593         goto Quickie;
594     }
595 
596     /* Allocate an ETFS file entry */
597     EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
598     if (!EtfsFile)
599     {
600         Status = STATUS_NO_MEMORY;
601         goto Quickie;
602     }
603 
604     /* Zero it out */
605     RtlZeroMemory(EtfsFile, sizeof(*EtfsFile));
606 
607     /* Capture the device ID of the directory */
608     NewFile->DeviceId = Directory->DeviceId;
609 
610     /* Check if this is the root or a filename\directory under */
611     FormatString = L"%ls%ls";
612     if (Directory->FilePath[1])
613     {
614         FormatString = L"%ls\\%ls";
615     }
616 
617     /* Combine the paths, and save the final path in the file entry */
618     _snwprintf(FilePath, Size, FormatString, Directory->FilePath, FileName);
619     NewFile->FilePath = FilePath;
620 
621     /* Copy the ETFS function callbacks into the file netry */
622     RtlCopyMemory(&NewFile->Callbacks,
623                   &EtfsFunctionTable,
624                   sizeof(NewFile->Callbacks));
625 
626     /* Fill out the rest of the details */
627     EtfsFile->DiskOffset = FileOffset;
628     EtfsFile->DirOffset = DirOffset;
629     EtfsFile->Size = FileSize;
630     EtfsFile->DeviceId = DeviceId;
631 
632     /* Check if this is a directory */
633     if (IsDirectory)
634     {
635         EtfsFile->Flags |= BL_ETFS_FILE_ENTRY_DIRECTORY;
636         NewFile->Flags |= BL_FILE_ENTRY_DIRECTORY;
637     }
638 
639     /* Write down the name of the filesystem */
640     EtfsFile->FsName = L"cdfs";
641 
642     /* All done, return the file entry, and save the ETFS side */
643     NewFile->FsSpecificData = EtfsFile;
644     *FileEntry = NewFile;
645     return Status;
646 
647 Quickie:
648     /* Failure path -- free the file path if we had one */
649     if (NewFile->FilePath)
650     {
651         BlMmFreeHeap(NewFile->FilePath);
652     }
653 
654     /* Free the ETFS file entry if we had one */
655     if (NewFile->FsSpecificData)
656     {
657         BlMmFreeHeap(NewFile->FsSpecificData);
658     }
659 
660     /* Free the file entry itself, and return the error code */
661     BlMmFreeHeap(NewFile);
662     return Status;
663 }
664 
665 NTSTATUS
666 EtfspCheckCdfs (
667     _In_ PBL_ETFS_DEVICE EtfsDevice,
668     _In_ ULONG DeviceId,
669     _Out_ PRAW_ISO_VD *VolumeDescriptor,
670     _Out_ PBOOLEAN VolumeIsIso
671     )
672 {
673     EfiPrintf(L"Raw Cdfs not implemented\r\n");
674     return STATUS_NOT_IMPLEMENTED;
675 }
676 
677 NTSTATUS
678 EtfspCheckEtfs (
679     _In_ PBL_ETFS_DEVICE EtfsDevice,
680     _In_ ULONG DeviceId,
681     _Out_ PRAW_ISO_VD *VolumeDescriptor,
682     _Out_ PBOOLEAN VolumeIsIso
683     )
684 {
685     PRAW_ISO_VD IsoVd;
686     PRAW_ET_VD EtVd;
687     NTSTATUS Status;
688     BOOLEAN IsIso;
689     BL_DEVICE_INFORMATION DeviceInformation;
690     ULONG Unknown, BytesRead;
691     ANSI_STRING CompareString, String;
692 
693     /* Save our static buffer pointer */
694     IsoVd = (PRAW_ISO_VD)EtfsDevice->MemoryBlock;
695     EtVd = (PRAW_ET_VD)IsoVd;
696 
697     /* First, read the El Torito Volume Descriptor */
698     BlDeviceGetInformation(DeviceId, &DeviceInformation);
699     Unknown = DeviceInformation.BlockDeviceInfo.Unknown;
700     DeviceInformation.BlockDeviceInfo.Unknown |= 1;
701     BlDeviceSetInformation(DeviceId, &DeviceInformation);
702     Status = BlDeviceReadAtOffset(DeviceId,
703                                   CD_SECTOR_SIZE,
704                                   (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE,
705                                   EtfsDevice->MemoryBlock,
706                                   &BytesRead);
707     DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
708     BlDeviceSetInformation(DeviceId, &DeviceInformation);
709     if (!NT_SUCCESS(Status))
710     {
711         EfiPrintf(L" read failed\r\n");
712         return Status;
713     }
714 
715     /* Remember that's where we last read */
716     EtfsDevice->Offset = (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE;
717 
718     /* Check if it's EL TORITO! */
719     RtlInitString(&String, "EL TORITO SPECIFICATION");
720     CompareString.Buffer = (PCHAR)EtVd->SystemId;
721     CompareString.Length = 23;
722     CompareString.MaximumLength = 23;
723     if (!RtlEqualString(&CompareString, &String, TRUE))
724     {
725         return STATUS_UNSUCCESSFUL;
726     }
727 
728     /* Check the version and boot indicator */
729     if ((EtVd->Version != 1) || (EtVd->BootIndicator))
730     {
731         return STATUS_UNSUCCESSFUL;
732     }
733 
734     /* Check if it has the CD0001 identifier */
735     RtlInitString(&String, ISO_VOL_ID);
736     CompareString.Buffer = (PCHAR)EtVd->StandardId;
737     CompareString.Length = 5;
738     CompareString.MaximumLength = 5;
739     if (!RtlEqualString(&CompareString, &String, TRUE))
740     {
741         return STATUS_UNSUCCESSFUL;
742     }
743 
744     /* Step two, we now want to read the ISO Volume Descriptor */
745     DeviceInformation.BlockDeviceInfo.Unknown |= 1u;
746     BlDeviceSetInformation(DeviceId, &DeviceInformation);
747     Status = BlDeviceReadAtOffset(DeviceId,
748                                   CD_SECTOR_SIZE,
749                                   FIRST_VD_SECTOR * CD_SECTOR_SIZE,
750                                   EtfsDevice->MemoryBlock,
751                                   &BytesRead);
752     DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
753     BlDeviceSetInformation(DeviceId, &DeviceInformation);
754     if (!NT_SUCCESS(Status))
755     {
756         return Status;
757     }
758 
759     /* Remember where we left off */
760     EtfsDevice->Offset = FIRST_VD_SECTOR  * CD_SECTOR_SIZE;
761 
762     /* This should also say CD0001 */
763     CompareString.Buffer = (PCHAR)IsoVd->StandardId;
764     CompareString.Length = 5;
765     CompareString.MaximumLength = 5;
766     IsIso = RtlEqualString(&CompareString, &String, TRUE);
767     if (!IsIso)
768     {
769         return STATUS_UNSUCCESSFUL;
770     }
771 
772     /* And should be a version we support */
773     if ((IsoVd->Version != VERSION_1) || (IsoVd->DescType != VD_PRIMARY))
774     {
775         return STATUS_UNSUCCESSFUL;
776     }
777 
778     /* Return back to the caller */
779     *VolumeDescriptor = IsoVd;
780     *VolumeIsIso = IsIso;
781     return STATUS_SUCCESS;
782 }
783 
784 NTSTATUS
785 EtfspDeviceContextDestroy (
786     _In_ PBL_ETFS_DEVICE EtfsDevice
787     )
788 {
789     if (EtfsDevice->MemoryBlock)
790     {
791         BlMmFreeHeap(EtfsDevice->MemoryBlock);
792     }
793 
794     BlMmFreeHeap(EtfsDevice);
795 
796     return STATUS_SUCCESS;
797 }
798 
799 NTSTATUS
800 EtfspCreateContext (
801     _In_ ULONG DeviceId,
802     _Out_ PBL_ETFS_DEVICE *EtfsDevice
803     )
804 {
805     PBL_ETFS_DEVICE NewContext;
806     PVOID MemoryBlock;
807     NTSTATUS Status;
808     BOOLEAN IsIso;
809     PRAW_ISO_VD RawVd;
810 
811     NewContext = (PBL_ETFS_DEVICE)BlMmAllocateHeap(sizeof(*NewContext));
812     if (!NewContext)
813     {
814         return STATUS_NO_MEMORY;
815     }
816     RtlZeroMemory(NewContext, sizeof(*NewContext));
817 
818     MemoryBlock = BlMmAllocateHeap(CD_SECTOR_SIZE);
819     NewContext->MemoryBlock = MemoryBlock;
820     if (!MemoryBlock)
821     {
822         Status = STATUS_NO_MEMORY;
823         goto Quickie;
824     }
825 
826     Status = EtfspCheckEtfs(NewContext, DeviceId, &RawVd, &IsIso);
827     if (!NT_SUCCESS(Status))
828     {
829         EfiPrintf(L"Drive not EDFS. Checking for CDFS: %lx\r\n");
830         Status = EtfspCheckCdfs(NewContext, DeviceId, &RawVd, &IsIso);
831     }
832 
833     if (!NT_SUCCESS(Status))
834     {
835         EfiPrintf(L"Drive not CDFS. Failing: %lx\r\n");
836         goto Quickie;
837     }
838 
839     NewContext->IsIso = IsIso;
840     NewContext->BlockSize = RVD_LB_SIZE(RawVd, IsIso);
841     NewContext->VolumeSize = RVD_VOL_SIZE(RawVd, IsIso);
842 
843     EtfspGetDirectoryInfo(NewContext,
844                           (PRAW_DIR_REC)RVD_ROOT_DE(RawVd, IsIso),
845                           &NewContext->RootDirOffset,
846                           &NewContext->RootDirSize,
847                           0);
848     Status = STATUS_SUCCESS;
849 
850 Quickie:
851     if (!NT_SUCCESS(Status))
852     {
853         EtfspDeviceContextDestroy(NewContext);
854         NewContext = NULL;
855     }
856 
857     *EtfsDevice = NewContext;
858     return Status;
859 }
860 
861 NTSTATUS
862 EtfspDeviceTableDestroyEntry (
863     _In_ PBL_ETFS_DEVICE EtfsDevice,
864     _In_ ULONG Index
865     )
866 {
867     EtfspDeviceContextDestroy(EtfsDevice);
868     EtfsDeviceTable[Index] = NULL;
869 
870     return STATUS_SUCCESS;
871 }
872 
873 NTSTATUS
874 EtfsMount (
875     _In_ ULONG DeviceId,
876     _In_ ULONG Unknown,
877     _Out_ PBL_FILE_ENTRY* FileEntry
878     )
879 {
880     PBL_ETFS_DEVICE EtfsDevice = NULL;
881     PBL_FILE_ENTRY RootEntry;
882     NTSTATUS Status;
883     PBL_ETFS_FILE EtfsFile;
884 
885     EfiPrintf(L"Trying to mount as ETFS...\r\n");
886 
887     Status = EtfspCreateContext(DeviceId, &EtfsDevice);
888     if (!NT_SUCCESS(Status))
889     {
890         EfiPrintf(L"ETFS context failed: %lx\r\n");
891         return Status;
892     }
893 
894     Status = BlTblSetEntry(&EtfsDeviceTable,
895                            &EtfsDeviceTableEntries,
896                            EtfsDevice,
897                            &DeviceId,
898                            TblDoNotPurgeEntry);
899     if (!NT_SUCCESS(Status))
900     {
901         EtfspDeviceContextDestroy(EtfsDevice);
902         return Status;
903     }
904 
905     RootEntry = BlMmAllocateHeap(sizeof(*RootEntry));
906     if (!RootEntry)
907     {
908         Status = STATUS_NO_MEMORY;
909         goto Quickie;
910     }
911 
912     RtlZeroMemory(RootEntry, sizeof(*RootEntry));
913 
914     RootEntry->FilePath = BlMmAllocateHeap(4);
915     if (!RootEntry->FilePath)
916     {
917         Status = STATUS_NO_MEMORY;
918         goto Quickie;
919     }
920 
921     wcsncpy(RootEntry->FilePath, L"\\", 1);
922 
923     RootEntry->DeviceId = DeviceId;
924     RtlCopyMemory(&RootEntry->Callbacks,
925                   &EtfsFunctionTable,
926                   sizeof(RootEntry->Callbacks));
927 
928     EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
929     if (!EtfsFile)
930     {
931         Status = STATUS_NO_MEMORY;
932         goto Quickie;
933     }
934 
935     RootEntry->Flags |= 0x10000;
936 
937     RtlZeroMemory(EtfsFile, sizeof(*EtfsFile));
938     RootEntry->FsSpecificData = EtfsFile;
939     EtfsFile->DeviceId = DeviceId;
940     EtfsFile->Flags |= 1;
941     EtfsFile->DiskOffset = EtfsDevice->RootDirOffset;
942     EtfsFile->DirOffset = 0;
943     EtfsFile->Size = EtfsDevice->RootDirSize;
944     EtfsFile->FsName = L"cdfs";
945     *FileEntry = RootEntry;
946 
947     return STATUS_SUCCESS;
948 
949 Quickie:
950     if (RootEntry->FilePath)
951     {
952         BlMmFreeHeap(RootEntry->FilePath);
953     }
954     if (RootEntry->FsSpecificData)
955     {
956         BlMmFreeHeap(RootEntry->FsSpecificData);
957     }
958     if (RootEntry)
959     {
960         BlMmFreeHeap(RootEntry);
961     }
962 
963     EtfspDeviceTableDestroyEntry(EtfsDevice, DeviceId);
964 
965     return Status;
966 }
967 
968 NTSTATUS
969 EtfsInitialize (
970     VOID
971     )
972 {
973     NTSTATUS Status;
974 
975     /* Allocate the device table with 2 entries*/
976     EtfsDeviceTableEntries = 2;
977     EtfsDeviceTable = BlMmAllocateHeap(sizeof(PBL_FILE_ENTRY) *
978                                        EtfsDeviceTableEntries);
979     if (EtfsDeviceTable)
980     {
981         /* Zero it out */
982         RtlZeroMemory(EtfsDeviceTable,
983                       sizeof(PBL_FILE_ENTRY) * EtfsDeviceTableEntries);
984         Status = STATUS_SUCCESS;
985     }
986     else
987     {
988         /* No memory, fail */
989         Status = STATUS_NO_MEMORY;
990     }
991 
992     /* Return back to caller */
993     return Status;
994 }
995 
996