[BOOTMGFW]

- Additional El Torito support. We now parse the EFI, BOOT directories, and find/open the BCD file, and get a handle to it!

svn path=/trunk/; revision=69189
This commit is contained in:
Alex Ionescu 2015-09-12 08:57:42 +00:00
parent b1114c9e93
commit feab49d6ab

View file

@ -23,25 +23,26 @@ typedef struct _RAW_ET_VD
/* DATA VARIABLES ************************************************************/
typedef struct _BL_ETFS_CONTEXT
typedef struct _BL_ETFS_DEVICE
{
ULONG RootDirOffset;
ULONG RootDirSize;
ULONG BlockSize;
ULONG VolumeSize;
BOOLEAN IsIso;
PRAW_ISO_VD MemoryBlock;
PUCHAR MemoryBlock;
ULONG Offset;
} BL_ETFS_CONTEXT, *PBL_ETFS_CONTEXT;
} BL_ETFS_DEVICE, *PBL_ETFS_DEVICE;
typedef struct _BL_ETFS_FILE
{
ULONG DirOffset;
ULONG DirEntOffset;
ULONGLONG Size;
ULONGLONG Offset;
PWCHAR FsName;
ULONG Flags;
ULONG DeviceId;
ULONG Offset;
ULONG Unknown;
ULONGLONG Size;
PWCHAR FsName;
} BL_ETFS_FILE, *PBL_ETFS_FILE;
ULONG EtfsDeviceTableEntries;
@ -62,6 +63,349 @@ BL_FILE_CALLBACKS EtfsFunctionTable =
/* FUNCTIONS *****************************************************************/
VOID
EtfspGetDirectoryInfo (
_In_ PBL_ETFS_DEVICE EtfsDevice,
_In_ PRAW_DIR_REC DirEntry,
_Out_ PULONG FileOffset,
_Out_ PULONG FileSize,
_Out_opt_ PBOOLEAN IsDirectory
)
{
ULONG SectorOffset;
BOOLEAN IsDir;
*FileOffset = *(PULONG)DirEntry->FileLoc * EtfsDevice->BlockSize;
*FileOffset += (DirEntry->XarLen * EtfsDevice->BlockSize);
SectorOffset = ALIGN_DOWN_BY(*FileOffset, CD_SECTOR_SIZE);
*FileSize = *(PULONG)DirEntry->DataLen;
IsDir = DE_FILE_FLAGS(EtfsDevice->IsIso, DirEntry) & ISO_ATTR_DIRECTORY;
if (IsDir)
{
*FileSize += ALIGN_UP_BY(SectorOffset, CD_SECTOR_SIZE) - SectorOffset;
}
if (IsDirectory)
{
*IsDirectory = IsDir;
}
}
USHORT
EtfspGetDirentNameLength (
_In_ PRAW_DIR_REC DirEntry
)
{
USHORT Length, RealLength;
PUCHAR Pos;
RealLength = Length = DirEntry->FileIdLen;
for (Pos = &DirEntry->FileIdLen + Length; Length; --Pos)
{
--Length;
if (*Pos == ';')
{
RealLength = Length;
break;
}
}
Length = RealLength;
for (Pos = &DirEntry->FileIdLen + Length; Length; --Pos)
{
--Length;
if (*Pos != '.')
{
break;
}
RealLength = Length;
}
return RealLength;
}
LONG
EtfspCompareNames (
__in PSTRING Name1,
__in PUNICODE_STRING Name2
)
{
ULONG i, l1, l2, l;
l1 = Name1->Length;
l2 = Name2->Length / sizeof(WCHAR);
l = min(l1, l2);
for (i = 0; i < l; i++)
{
if (toupper(Name1->Buffer[i]) != toupper(Name2->Buffer[i]))
{
return toupper(Name1->Buffer[i]) - toupper(Name2->Buffer[i]);
}
}
if (l2 <= l1)
{
return l2 < l1;
}
else
{
return -1;
}
}
BOOLEAN
EtfspFileMatch (
_In_ PRAW_DIR_REC DirEntry,
_In_ PUNICODE_STRING FileName
)
{
BOOLEAN Match;
USHORT Length;
ANSI_STRING DirName;
if ((DirEntry->FileIdLen != 1) ||
((DirEntry->FileId[0] != 0) && (DirEntry->FileId[0] != 1)))
{
Length = EtfspGetDirentNameLength(DirEntry);
DirName.Length = Length;
DirName.MaximumLength = Length;
DirName.Buffer = (PCHAR)DirEntry->FileId;
Match = EtfspCompareNames(&DirName, FileName);
}
else
{
Match = -1;
}
return Match;
}
NTSTATUS
EtfspGetDirent (
_In_ PBL_FILE_ENTRY DirectoryEntry,
_Out_ PRAW_DIR_REC *DirEntry,
_Inout_ PULONG DirentOffset
)
{
PBL_ETFS_FILE EtfsFile;
ULONG FileOffset, DirectoryOffset, AlignedOffset, RemainderOffset;
ULONG DeviceId, ReadSize, DirLen;
PBL_ETFS_DEVICE EtfsDevice;
BOOLEAN NeedRead, IsMulti;
NTSTATUS result;
PRAW_DIR_REC DirEnt;
PUCHAR MemoryBlock;
EtfsFile = DirectoryEntry->FsSpecificData;
DeviceId = EtfsFile->DeviceId;
FileOffset = EtfsFile->Offset;
EtfsDevice = EtfsDeviceTable[DeviceId];
DirectoryOffset = *DirentOffset;
MemoryBlock = EtfsDevice->MemoryBlock;
IsMulti = 0;
AlignedOffset = (FileOffset + *DirentOffset) & ~CD_SECTOR_SIZE;
RemainderOffset = *DirentOffset + FileOffset - AlignedOffset;
ReadSize = 2048 - RemainderOffset;
NeedRead = AlignedOffset == EtfsDevice->Offset ? 0 : 1;
ReadAgain:
if (DirectoryOffset >= EtfsFile->Size)
{
return STATUS_NO_SUCH_FILE;
}
while (ReadSize < MIN_DIR_REC_SIZE)
{
DirectoryOffset += ReadSize;
AlignedOffset += 2048;
ReadSize = 2048;
RemainderOffset = 0;
NeedRead = 1;
if (DirectoryOffset >= EtfsFile->Size)
{
return STATUS_NO_SUCH_FILE;
}
}
if (NeedRead)
{
result = BlDeviceReadAtOffset(DirectoryEntry->DeviceId,
CD_SECTOR_SIZE,
AlignedOffset,
MemoryBlock,
NULL);
if (!NT_SUCCESS(result))
{
EfiPrintf(L"Device read failed %lx\r\n", result);
return result;
}
NeedRead = FALSE;
EtfsDevice->Offset = AlignedOffset;
}
if (!*(MemoryBlock + RemainderOffset))
{
AlignedOffset += 2048;
NeedRead = TRUE;
RemainderOffset = 0;
DirectoryOffset += ReadSize;
ReadSize = 2048;
goto ReadAgain;
}
DirEnt = (PRAW_DIR_REC)(MemoryBlock + RemainderOffset);
DirLen = DirEnt->DirLen;
if (DirLen > ReadSize)
{
EfiPrintf(L"Dir won't fit %lx %lx\r\n", DirLen, ReadSize);
return STATUS_NO_SUCH_FILE;
}
if (IsMulti)
{
if (!(DE_FILE_FLAGS(EtfsDevice->IsIso, DirEnt) & ISO_ATTR_MULTI))
{
IsMulti = TRUE;
}
}
else if (DE_FILE_FLAGS(EtfsDevice->IsIso, DirEnt) & ISO_ATTR_MULTI)
{
IsMulti = TRUE;
}
else
{
if ((DirEnt->FileIdLen != 1) ||
((DirEnt->FileId[0] != 0) && (DirEnt->FileId[0] != 1)))
{
goto Quickie;
}
}
RemainderOffset += DirLen;
DirectoryOffset += DirLen;
ReadSize -= DirLen;
goto ReadAgain;
Quickie:
*DirEntry = DirEnt;
*DirentOffset = DirectoryOffset;
return STATUS_SUCCESS;
}
NTSTATUS
EtfspSearchForDirent (
_In_ PBL_FILE_ENTRY DirectoryEntry,
_In_ PWCHAR FileName,
_Out_ PRAW_DIR_REC *DirEntry,
_Out_ PULONG DirentOffset
)
{
UNICODE_STRING Name;
ULONG NextOffset;
PRAW_DIR_REC DirEnt;
NTSTATUS Status;
RtlInitUnicodeString(&Name, FileName);
for (NextOffset = *DirentOffset;
;
NextOffset = NextOffset + DirEnt->DirLen)
{
Status = EtfspGetDirent(DirectoryEntry, &DirEnt, &NextOffset);
if (!NT_SUCCESS(Status))
{
return STATUS_NO_SUCH_FILE;
}
if (!EtfspFileMatch(DirEnt, &Name))
{
break;
}
}
*DirEntry = DirEnt;
*DirentOffset = NextOffset;
return 0;
}
NTSTATUS
EtfspCachedSearchForDirent (
_In_ PBL_FILE_ENTRY DirectoryEntry,
_In_ PWCHAR FileName,
_Out_ PRAW_DIR_REC *DirEntry,
_Out_ PULONG DirOffset,
_In_ BOOLEAN KeepOffset
)
{
PBL_ETFS_FILE EtfsFile;
PBL_ETFS_DEVICE EtfsDevice;
NTSTATUS Status;
ULONG DirentOffset;
PRAW_DIR_REC Dirent;
UNICODE_STRING Name;
EtfsFile = DirectoryEntry->FsSpecificData;
EtfsDevice = EtfsDeviceTable[EtfsFile->DeviceId];
RtlInitUnicodeString(&Name, FileName);
DirentOffset = EtfsFile->DirEntOffset;
if ((KeepOffset) ||
(ALIGN_DOWN_BY((DirentOffset + EtfsFile->Offset), CD_SECTOR_SIZE) ==
EtfsDevice->Offset))
{
Status = EtfspGetDirent(DirectoryEntry, &Dirent, &DirentOffset);
if (NT_SUCCESS(Status))
{
if (!EtfspFileMatch(Dirent, &Name))
{
*DirEntry = Dirent;
*DirOffset = DirentOffset;
return STATUS_SUCCESS;
}
}
else
{
DirentOffset = 0;
}
}
else
{
DirentOffset = 0;
}
Status = EtfspSearchForDirent(DirectoryEntry,
FileName,
DirEntry,
&DirentOffset);
if (!(NT_SUCCESS(Status)) && (DirentOffset))
{
DirentOffset = 0;
Status = EtfspSearchForDirent(DirectoryEntry,
FileName,
DirEntry,
&DirentOffset);
}
if (NT_SUCCESS(Status))
{
*DirOffset = DirentOffset;
}
return Status;
}
NTSTATUS
EtfsOpen (
_In_ PBL_FILE_ENTRY Directory,
@ -70,13 +414,112 @@ EtfsOpen (
_Out_ PBL_FILE_ENTRY *FileEntry
)
{
EfiPrintf(L"Attempting to open file %s in directory %s. Not yet supported\r\n", FileName, Directory->FilePath);
return STATUS_NOT_IMPLEMENTED;
PBL_ETFS_DEVICE EtfsDevice;
NTSTATUS Status;
PBL_FILE_ENTRY NewFile;
PWCHAR FilePath, FormatString;
PBL_ETFS_FILE EtfsFile;
ULONG DeviceId, FileSize, DirOffset, FileOffset, Size;
PRAW_DIR_REC DirEntry;
BOOLEAN IsDirectory;
EfiPrintf(L"Attempting to open file %s in directory %s\r\n", FileName, Directory->FilePath);
EtfsFile = Directory->FsSpecificData;
DeviceId = EtfsFile->DeviceId;
EtfsDevice = EtfsDeviceTable[DeviceId];
/* Find the given file (or directory) in the given directory */
Status = EtfspCachedSearchForDirent(Directory,
FileName,
&DirEntry,
&DirOffset,
FALSE);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"no dirent found: %lx\r\n", Status);
return Status;
}
/* Find out information about the file (or directory) we found */
EtfspGetDirectoryInfo(EtfsDevice,
DirEntry,
&FileOffset,
&FileSize,
&IsDirectory);
NewFile = BlMmAllocateHeap(sizeof(*NewFile));
if (!NewFile)
{
return STATUS_NO_MEMORY;
}
RtlZeroMemory(NewFile, sizeof(*NewFile));
Size = wcslen(Directory->FilePath) + wcslen(FileName) + 2;
FilePath = BlMmAllocateHeap(Size * sizeof(WCHAR));
if (!FilePath)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
EtfsFile = (PBL_ETFS_FILE)BlMmAllocateHeap(sizeof(*EtfsFile));
if (!EtfsFile)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
RtlZeroMemory(NewFile, sizeof(*EtfsFile));
NewFile->DeviceId = Directory->DeviceId;
FormatString = L"%ls%ls";
if (Directory->FilePath[1])
{
FormatString = L"%ls\\%ls";
}
_snwprintf(FilePath, Size, FormatString, Directory->FilePath, FileName);
NewFile->FilePath = FilePath;
RtlCopyMemory(&NewFile->Callbacks,
&EtfsFunctionTable,
sizeof(NewFile->Callbacks));
EtfsFile->Offset = FileOffset;
EtfsFile->DirOffset = DirOffset;
EtfsFile->Size = FileSize;
EtfsFile->DeviceId = DeviceId;
if (IsDirectory)
{
EtfsFile->Flags |= 1;
NewFile->Flags |= 0x10000;
}
EtfsFile->FsName = L"cdfs";
NewFile->FsSpecificData = EtfsFile;
*FileEntry = NewFile;
return Status;
Quickie:
if (NewFile->FilePath)
{
BlMmFreeHeap(NewFile->FilePath);
}
if (NewFile->FsSpecificData)
{
BlMmFreeHeap(NewFile->FsSpecificData);
}
BlMmFreeHeap(NewFile);
return Status;
}
NTSTATUS
EtfspCheckCdfs (
_In_ PBL_ETFS_CONTEXT EtfsContext,
_In_ PBL_ETFS_DEVICE EtfsDevice,
_In_ ULONG DeviceId,
_Out_ PRAW_ISO_VD *VolumeDescriptor,
_Out_ PBOOLEAN VolumeIsIso
@ -88,7 +531,7 @@ EtfspCheckCdfs (
NTSTATUS
EtfspCheckEtfs (
_In_ PBL_ETFS_CONTEXT EtfsContext,
_In_ PBL_ETFS_DEVICE EtfsDevice,
_In_ ULONG DeviceId,
_Out_ PRAW_ISO_VD *VolumeDescriptor,
_Out_ PBOOLEAN VolumeIsIso
@ -103,7 +546,7 @@ EtfspCheckEtfs (
ANSI_STRING CompareString, String;
/* Save our static buffer pointer */
IsoVd = EtfsContext->MemoryBlock;
IsoVd = (PRAW_ISO_VD)EtfsDevice->MemoryBlock;
EtVd = (PRAW_ET_VD)IsoVd;
/* First, read the El Torito Volume Descriptor */
@ -114,7 +557,7 @@ EtfspCheckEtfs (
Status = BlDeviceReadAtOffset(DeviceId,
CD_SECTOR_SIZE,
(FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE,
EtfsContext->MemoryBlock,
EtfsDevice->MemoryBlock,
&BytesRead);
DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
BlDeviceSetInformation(DeviceId, &DeviceInformation);
@ -125,7 +568,7 @@ EtfspCheckEtfs (
}
/* Remember that's where we last read */
EtfsContext->Offset = (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE;
EtfsDevice->Offset = (FIRST_VD_SECTOR + 1) * CD_SECTOR_SIZE;
/* Check if it's EL TORITO! */
RtlInitString(&String, "EL TORITO SPECIFICATION");
@ -159,7 +602,7 @@ EtfspCheckEtfs (
Status = BlDeviceReadAtOffset(DeviceId,
CD_SECTOR_SIZE,
FIRST_VD_SECTOR * CD_SECTOR_SIZE,
EtfsContext->MemoryBlock,
EtfsDevice->MemoryBlock,
&BytesRead);
DeviceInformation.BlockDeviceInfo.Unknown = Unknown;
BlDeviceSetInformation(DeviceId, &DeviceInformation);
@ -169,7 +612,7 @@ EtfspCheckEtfs (
}
/* Remember where we left off */
EtfsContext->Offset = FIRST_VD_SECTOR * CD_SECTOR_SIZE;
EtfsDevice->Offset = FIRST_VD_SECTOR * CD_SECTOR_SIZE;
/* This should also say CD0001 */
CompareString.Buffer = (PCHAR)IsoVd->StandardId;
@ -194,63 +637,34 @@ EtfspCheckEtfs (
return STATUS_SUCCESS;
}
VOID
EtfspGetDirectoryInfo (
_In_ PBL_ETFS_CONTEXT EtfsContext,
_In_ PRAW_DIR_REC DirEntry,
_Out_ PULONG FileOffset,
_Out_ PULONG FileSize,
_Out_opt_ PBOOLEAN IsDirectory
)
{
ULONG SectorOffset;
BOOLEAN IsDir;
*FileOffset = *(PULONG)DirEntry->FileLoc * EtfsContext->BlockSize;
*FileOffset += (DirEntry->XarLen * EtfsContext->BlockSize);
SectorOffset = ALIGN_DOWN_BY(*FileOffset, CD_SECTOR_SIZE);
*FileSize = *(PULONG)DirEntry->DataLen;
IsDir = DE_FILE_FLAGS(EtfsContext->IsIso, DirEntry) & ISO_ATTR_DIRECTORY;
if (IsDir)
{
*FileSize += ALIGN_UP_BY(SectorOffset, CD_SECTOR_SIZE) - SectorOffset;
}
if (IsDirectory)
{
*IsDirectory = IsDir;
}
}
NTSTATUS
EtfspDeviceContextDestroy (
_In_ PBL_ETFS_CONTEXT EtfsContext
_In_ PBL_ETFS_DEVICE EtfsDevice
)
{
if (EtfsContext->MemoryBlock)
if (EtfsDevice->MemoryBlock)
{
BlMmFreeHeap(EtfsContext->MemoryBlock);
BlMmFreeHeap(EtfsDevice->MemoryBlock);
}
BlMmFreeHeap(EtfsContext);
return 0;
BlMmFreeHeap(EtfsDevice);
return STATUS_SUCCESS;
}
NTSTATUS
EtfspCreateContext (
_In_ ULONG DeviceId,
_Out_ PBL_ETFS_CONTEXT *EtfsContext
_Out_ PBL_ETFS_DEVICE *EtfsDevice
)
{
PBL_ETFS_CONTEXT NewContext;
PBL_ETFS_DEVICE NewContext;
PVOID MemoryBlock;
NTSTATUS Status;
BOOLEAN IsIso;
PRAW_ISO_VD RawVd;
NewContext = (PBL_ETFS_CONTEXT)BlMmAllocateHeap(sizeof(*NewContext));
NewContext = (PBL_ETFS_DEVICE)BlMmAllocateHeap(sizeof(*NewContext));
if (!NewContext)
{
return STATUS_NO_MEMORY;
@ -287,22 +701,26 @@ EtfspCreateContext (
&NewContext->RootDirOffset,
&NewContext->RootDirSize,
0);
Status = STATUS_SUCCESS;
Quickie:
EtfspDeviceContextDestroy(NewContext);
NewContext = NULL;
if (!NT_SUCCESS(Status))
{
EtfspDeviceContextDestroy(NewContext);
NewContext = NULL;
}
*EtfsContext = NewContext;
*EtfsDevice = NewContext;
return Status;
}
NTSTATUS
EtfspDeviceTableDestroyEntry (
_In_ PBL_ETFS_CONTEXT EtfsContext,
_In_ PBL_ETFS_DEVICE EtfsDevice,
_In_ ULONG Index
)
{
EtfspDeviceContextDestroy(EtfsContext);
EtfspDeviceContextDestroy(EtfsDevice);
EtfsDeviceTable[Index] = NULL;
return STATUS_SUCCESS;
@ -315,14 +733,14 @@ EtfsMount (
_Out_ PBL_FILE_ENTRY* FileEntry
)
{
PBL_ETFS_CONTEXT EtfsContext = NULL;
PBL_ETFS_DEVICE EtfsDevice = NULL;
PBL_FILE_ENTRY RootEntry;
NTSTATUS Status;
PBL_ETFS_FILE EtfsFile;
EfiPrintf(L"Trying to mount as ETFS...\r\n");
Status = EtfspCreateContext(DeviceId, &EtfsContext);
Status = EtfspCreateContext(DeviceId, &EtfsDevice);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"ETFS context failed: %lx\r\n");
@ -331,12 +749,12 @@ EtfsMount (
Status = BlTblSetEntry(&EtfsDeviceTable,
&EtfsDeviceTableEntries,
EtfsContext,
EtfsDevice,
&DeviceId,
TblDoNotPurgeEntry);
if (!NT_SUCCESS(Status))
{
EtfspDeviceContextDestroy(EtfsContext);
EtfspDeviceContextDestroy(EtfsDevice);
return Status;
}
@ -376,9 +794,10 @@ EtfsMount (
RootEntry->FsSpecificData = EtfsFile;
EtfsFile->DeviceId = DeviceId;
EtfsFile->Flags |= 1;
EtfsFile->Offset = EtfsContext->RootDirOffset;
EtfsFile->Unknown = 0;
EtfsFile->Size = EtfsContext->RootDirSize;
EtfsFile->Offset = EtfsDevice->RootDirOffset;
EtfsFile->DirOffset = 0;
EtfsFile->Size = EtfsDevice->RootDirSize;
EfiPrintf(L"Root offset: %I64x Size: %I64x\r\n", EtfsFile->Offset, EtfsFile->Size);
EtfsFile->FsName = L"cdfs";
*FileEntry = RootEntry;
@ -398,7 +817,7 @@ Quickie:
BlMmFreeHeap(RootEntry);
}
EtfspDeviceTableDestroyEntry(EtfsContext, DeviceId);
EtfspDeviceTableDestroyEntry(EtfsDevice, DeviceId);
return Status;
}