mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
416 lines
9.3 KiB
C
416 lines
9.3 KiB
C
/** @file
|
|
Copyright (c) 2023, Savva Mitrofanov. All rights reserved.
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Ext4Dxe.h>
|
|
|
|
#include <string.h>
|
|
#include <UserFile.h>
|
|
#include <UserGlobalVar.h>
|
|
#include <UserMemory.h>
|
|
#include <UserUnicodeCollation.h>
|
|
|
|
STATIC FILE *mImageFp;
|
|
STATIC UINT64 mImageSize;
|
|
STATIC EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *mEfiSfsInterface;
|
|
|
|
void
|
|
PrintHex (
|
|
const void *data,
|
|
size_t size
|
|
)
|
|
{
|
|
char ascii[17];
|
|
size_t i, j;
|
|
|
|
ascii[16] = '\0';
|
|
for (i = 0; i < size; ++i) {
|
|
printf ("%02X ", ((unsigned char *)data)[i]);
|
|
if ((((unsigned char *)data)[i] >= ' ') && (((unsigned char *)data)[i] <= '~')) {
|
|
ascii[i % 16] = ((unsigned char *)data)[i];
|
|
} else {
|
|
ascii[i % 16] = '.';
|
|
}
|
|
|
|
if (((i + 1) % 8 == 0) || (i + 1 == size)) {
|
|
printf (" ");
|
|
if ((i + 1) % 16 == 0) {
|
|
printf ("| %s \n", ascii);
|
|
} else if (i + 1 == size) {
|
|
ascii[(i + 1) % 16] = '\0';
|
|
if ((i + 1) % 16 <= 8) {
|
|
printf (" ");
|
|
}
|
|
|
|
for (j = (i + 1) % 16; j < 16; ++j) {
|
|
printf (" ");
|
|
}
|
|
|
|
printf ("| %s \n", ascii);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Initialises Unicode collation, which is needed for case-insensitive string comparisons
|
|
within the driver (a good example of an application of this is filename comparison).
|
|
|
|
@param[in] DriverHandle Handle to the driver image.
|
|
|
|
@retval EFI_SUCCESS Unicode collation was successfully initialised.
|
|
@retval !EFI_SUCCESS Failure.
|
|
**/
|
|
EFI_STATUS
|
|
Ext4InitialiseUnicodeCollation (
|
|
EFI_HANDLE DriverHandle
|
|
)
|
|
{
|
|
OcUnicodeCollationInitializeMappingTables ();
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Does a case-insensitive string comparison. Refer to EFI_UNICODE_COLLATION_PROTOCOL's StriColl
|
|
for more details.
|
|
|
|
@param[in] Str1 Pointer to a null terminated string.
|
|
@param[in] Str2 Pointer to a null terminated string.
|
|
|
|
@retval 0 Str1 is equivalent to Str2.
|
|
@retval >0 Str1 is lexically greater than Str2.
|
|
@retval <0 Str1 is lexically less than Str2.
|
|
**/
|
|
INTN
|
|
Ext4StrCmpInsensitive (
|
|
IN CHAR16 *Str1,
|
|
IN CHAR16 *Str2
|
|
)
|
|
{
|
|
return EngStriColl (NULL, Str1, Str2);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapInstallMultipleProtocolInterfaces (
|
|
IN OUT EFI_HANDLE *Handle,
|
|
...
|
|
)
|
|
{
|
|
VA_LIST Args;
|
|
EFI_STATUS Status;
|
|
EFI_GUID *Protocol;
|
|
VOID *Interface;
|
|
|
|
if (Handle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
VA_START (Args, Handle);
|
|
for (Status = EFI_SUCCESS; !EFI_ERROR (Status);) {
|
|
//
|
|
// If protocol is NULL, then it's the end of the list
|
|
//
|
|
Protocol = VA_ARG (Args, EFI_GUID *);
|
|
if (Protocol == NULL) {
|
|
break;
|
|
}
|
|
|
|
Interface = VA_ARG (Args, VOID *);
|
|
|
|
//
|
|
// If this is Sfs protocol then save interface into global state
|
|
//
|
|
if (CompareGuid (Protocol, &gEfiSimpleFileSystemProtocolGuid)) {
|
|
mEfiSfsInterface = Interface;
|
|
}
|
|
}
|
|
|
|
VA_END (Args);
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
FreeAll (
|
|
IN CHAR16 *FileName,
|
|
IN EXT4_PARTITION *Part
|
|
)
|
|
{
|
|
FreePool (FileName);
|
|
|
|
if (Part != NULL) {
|
|
if (Part->DiskIo != NULL) {
|
|
FreePool (Part->DiskIo);
|
|
}
|
|
|
|
if (Part->BlockIo != NULL) {
|
|
if (Part->BlockIo->Media != NULL) {
|
|
FreePool (Part->BlockIo->Media);
|
|
}
|
|
|
|
FreePool (Part->BlockIo);
|
|
}
|
|
|
|
if (Part->Root != NULL) {
|
|
Ext4UnmountAndFreePartition (Part);
|
|
} else if (Part != NULL) {
|
|
FreePool (Part);
|
|
}
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UserReadDisk (
|
|
IN EFI_DISK_IO_PROTOCOL *This,
|
|
IN UINT32 MediaId,
|
|
IN UINT64 Offset,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Offset > mImageSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((mImageSize - Offset) < BufferSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fseek (mImageFp, Offset, SEEK_SET) != 0) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (fread (Buffer, BufferSize, 1, mImageFp) != 1) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
int
|
|
ENTRY_POINT (
|
|
int argc,
|
|
char **argv
|
|
)
|
|
{
|
|
FILE *ImageFp;
|
|
UINT64 ImageSize;
|
|
EFI_STATUS Status;
|
|
EFI_FILE_INFO *Info;
|
|
UINTN Len;
|
|
CHAR16 *FileName;
|
|
EFI_FILE_PROTOCOL *This;
|
|
EFI_DISK_IO_PROTOCOL *DiskIo;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
EFI_HANDLE DeviceHandle;
|
|
EXT4_PARTITION *Part;
|
|
UINTN BufferSize;
|
|
VOID *Buffer;
|
|
VOID *TmpBuffer;
|
|
EFI_FILE_PROTOCOL *NewHandle;
|
|
|
|
SetPoolAllocationSizeLimit (BASE_1GB | BASE_2GB);
|
|
|
|
DeviceHandle = (EFI_HANDLE)0xDEADBEAFULL;
|
|
|
|
gBS->InstallMultipleProtocolInterfaces = WrapInstallMultipleProtocolInterfaces;
|
|
|
|
if ((argc < 2) || (argc != 3)) {
|
|
DEBUG ((DEBUG_ERROR, "Usage: ./ext4read ([image path] [file path])*\n"));
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Open Ext4 user image
|
|
//
|
|
ImageFp = fopen (argv[1], "rb");
|
|
|
|
if (ImageFp == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Can't open image file\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (fseek (ImageFp, 0, SEEK_END) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "Can't set file position\n"));
|
|
fclose (ImageFp);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ImageSize = ftell (ImageFp);
|
|
if (ImageSize <= 0) {
|
|
DEBUG ((DEBUG_ERROR, "Incorrect file size\n"));
|
|
fclose (ImageFp);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (fseek (ImageFp, 0, SEEK_SET) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "Can't rewind file position to the start\n"));
|
|
fclose (ImageFp);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
mImageFp = ImageFp;
|
|
mImageSize = ImageSize;
|
|
|
|
//
|
|
// Prepare Filename to read from partition
|
|
//
|
|
FileName = AllocateZeroPool (strlen (argv[2]) * sizeof (CHAR16) + 1);
|
|
|
|
if (FileName == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Can't allocate space for unicode filename\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Status = AsciiStrToUnicodeStrS (argv[2], FileName, strlen (argv[2]) + 1);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Can't convert given filename into UnicodeStr\n"));
|
|
fclose (ImageFp);
|
|
FreePool (FileName);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Construct BlockIo and DiskIo interfaces
|
|
//
|
|
DiskIo = AllocateZeroPool (sizeof (EFI_DISK_IO_PROTOCOL));
|
|
if (DiskIo == NULL) {
|
|
fclose (ImageFp);
|
|
FreePool (FileName);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
DiskIo->ReadDisk = UserReadDisk;
|
|
|
|
BlockIo = AllocateZeroPool (sizeof (EFI_BLOCK_IO_PROTOCOL));
|
|
if (BlockIo == NULL) {
|
|
fclose (ImageFp);
|
|
FreePool (FileName);
|
|
FreePool (DiskIo);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
BlockIo->Media = AllocateZeroPool (sizeof (EFI_BLOCK_IO_MEDIA));
|
|
if (BlockIo->Media == NULL) {
|
|
fclose (ImageFp);
|
|
FreePool (FileName);
|
|
FreePool (DiskIo);
|
|
FreePool (BlockIo);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Check Ext4 SuperBlock magic like it done
|
|
// in Ext4IsBindingSupported routine
|
|
//
|
|
if (!Ext4SuperblockCheckMagic (DiskIo, BlockIo)) {
|
|
DEBUG ((DEBUG_WARN, "[ext4] Superblock contains bad magic \n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Open partition
|
|
//
|
|
Status = Ext4OpenPartition (DeviceHandle, DiskIo, NULL, BlockIo);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "[ext4] Error mounting: %r\n", Status));
|
|
fclose (ImageFp);
|
|
FreePool (FileName);
|
|
FreePool (BlockIo->Media);
|
|
FreePool (BlockIo);
|
|
FreePool (DiskIo);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Part = (EXT4_PARTITION *)mEfiSfsInterface;
|
|
|
|
This = (EFI_FILE_PROTOCOL *)Part->Root;
|
|
|
|
Status = Ext4Open (This, &NewHandle, FileName, EFI_FILE_MODE_READ, 0);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "[ext4] Couldn't open file %s \n", FileName));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Get FileInfo
|
|
//
|
|
Len = 0;
|
|
Info = NULL;
|
|
Status = Ext4GetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Info = AllocateZeroPool (Len);
|
|
if (Info == NULL) {
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Status = Ext4GetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Couldn't get file information \n"));
|
|
FreePool (Info);
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
Buffer = AllocateZeroPool (Info->FileSize);
|
|
BufferSize = Info->FileSize;
|
|
if (Buffer == NULL) {
|
|
FreePool (Info);
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Read file from image
|
|
//
|
|
Status = Ext4ReadFile (NewHandle, &BufferSize, Buffer);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
TmpBuffer = ReallocatePool (Info->FileSize, BufferSize, Buffer);
|
|
if (TmpBuffer == NULL) {
|
|
FreePool (Buffer);
|
|
FreePool (Info);
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Buffer = TmpBuffer;
|
|
|
|
Status = Ext4ReadFile (NewHandle, &BufferSize, Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "Couldn't read file with Status: %r \n", Status));
|
|
FreePool (Info);
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
FreePool (Info);
|
|
|
|
//
|
|
// Print file contents
|
|
//
|
|
if (Buffer != NULL) {
|
|
PrintHex (Buffer, BufferSize);
|
|
}
|
|
|
|
FreePool (Buffer);
|
|
fclose (ImageFp);
|
|
FreeAll (FileName, Part);
|
|
return EXIT_SUCCESS;
|
|
}
|