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