audk/Ext4Pkg/Ext4Dxe/Symlink.c
Mike Beaton 4f8ce12f1e
Ext4Dxe: Convert DEBUG_WARN to DEBUG_INFO (#81)
The issues which are warned about can happen as a
normal part of life when using Linux, especially
`Needs journal recovery, mounting read-only`.
This means that when using Ext4Dxe with OpenCore
it becomes necessary not to include WARN in HaltLevel,
whereas in general, to keep a clean system, one
might want to include it.

More generally this PR tries to reflect the idea that
WARNs should be to let users know of config (and
other) errors which they should really fix. Whereas
here we have a state which occurs quite commonly
and which the user can do nothing about.

I am pretty certain that (from the point of view
described above) the changes in Superblock.c are
correct. I am less certain about the changes in
Symlink.c - but all the same, if they are not errors
and the system can continue, and the user can do
nothing about it, then probably the same logic applies.
2025-10-12 13:42:27 +03:00

262 lines
7.5 KiB
C

/** @file
Symbolic links routines
Copyright (c) 2022-2023 Savva Mitrofanov All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
/**
Detects if a symlink is a fast symlink.
@param[in] File Pointer to the opened file.
@return BOOLEAN Whether symlink is a fast symlink
**/
STATIC
BOOLEAN
Ext4SymlinkIsFastSymlink (
IN CONST EXT4_FILE *File
)
{
//
// Detection logic of the fast-symlink splits into two behaviors - old and new.
// The old behavior is based on comparing the extended attribute blocks
// with the inode's i_blocks, and if it's zero we know the inode isn't storing
// the link in filesystem blocks, so we look to the inode->i_data.
// The new behavior is apparently needed only with the large EA inode feature.
// In this case we check that inode size less than maximum fast symlink size.
// So, we revert to the old behavior if the large EA inode feature is not set.
//
UINT32 FileAcl;
UINT32 ExtAttrBlocks;
if ((File->Inode->i_flags & EXT4_EA_INODE_FL) == 0) {
FileAcl = File->Inode->i_file_acl;
if (EXT4_IS_64_BIT (File->Partition)) {
//
// We don't care about final value, we are just checking for any bit is set
// so, thats why we neglect LShiftU64(.., 32)
//
FileAcl |= File->Inode->i_osd2.data_linux.l_i_file_acl_high;
}
ExtAttrBlocks = FileAcl != 0 ? (File->Partition->BlockSize >> 9) : 0;
return File->Inode->i_blocks == ExtAttrBlocks;
}
return EXT4_INODE_SIZE (File->Inode) <= EXT4_FAST_SYMLINK_MAX_SIZE;
}
/**
Reads a fast symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] AsciiSymlink Pointer to the output ascii symlink string.
@param[out] AsciiSymlinkSize Pointer to the output ascii symlink string length.
@retval EFI_SUCCESS Fast symlink was read.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
**/
STATIC
EFI_STATUS
Ext4ReadFastSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR8 **AsciiSymlink,
OUT UINT32 *AsciiSymlinkSize
)
{
UINT32 SymlinkSize;
CHAR8 *AsciiSymlinkTmp;
//
// Fast-symlink's EXT4_INODE_SIZE is not necessarily validated when we checked it in
// Ext4SymlinkIsFastSymlink(), so truncate if necessary.
//
SymlinkSize = (UINT32)MIN (EXT4_INODE_SIZE (File->Inode), EXT4_FAST_SYMLINK_MAX_SIZE);
AsciiSymlinkTmp = AllocatePool (SymlinkSize + 1);
if (AsciiSymlinkTmp == NULL) {
DEBUG ((DEBUG_ERROR, "[ext4] Failed to allocate symlink ascii string buffer\n"));
return EFI_OUT_OF_RESOURCES;
}
CopyMem (AsciiSymlinkTmp, File->Inode->i_data, SymlinkSize);
//
// Add null-terminator
//
AsciiSymlinkTmp[SymlinkSize] = '\0';
*AsciiSymlink = AsciiSymlinkTmp;
*AsciiSymlinkSize = SymlinkSize + 1;
return EFI_SUCCESS;
}
/**
Reads a slow symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] AsciiSymlink Pointer to the output ascii symlink string.
@param[out] AsciiSymlinkSize Pointer to the output ascii symlink string length.
@retval EFI_SUCCESS Slow symlink was read.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
@retval EFI_INVALID_PARAMETER Slow symlink path has incorrect length
@retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode value
**/
STATIC
EFI_STATUS
Ext4ReadSlowSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR8 **AsciiSymlink,
OUT UINT32 *AsciiSymlinkSize
)
{
EFI_STATUS Status;
CHAR8 *SymlinkTmp;
UINT64 SymlinkSizeTmp;
UINT32 SymlinkAllocateSize;
UINTN ReadSize;
SymlinkSizeTmp = EXT4_INODE_SIZE (File->Inode);
//
// Allocate EXT4_INODE_SIZE + 1
//
if (SymlinkSizeTmp >= EXT4_EFI_PATH_MAX) {
DEBUG ((
DEBUG_INFO,
"[ext4] Warn: symlink path maximum length was hit!\n"
));
return EFI_INVALID_PARAMETER;
}
SymlinkAllocateSize = (UINT32)SymlinkSizeTmp + 1;
SymlinkTmp = AllocatePool (SymlinkAllocateSize);
if (SymlinkTmp == NULL) {
DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink ascii string buffer\n"));
return EFI_OUT_OF_RESOURCES;
}
ReadSize = (UINTN)SymlinkSizeTmp;
Status = Ext4Read (Partition, File, SymlinkTmp, File->Position, &ReadSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Failed to read symlink from blocks with status %r\n", Status));
FreePool (SymlinkTmp);
return Status;
}
if (SymlinkSizeTmp != ReadSize) {
DEBUG ((
DEBUG_FS,
"[ext4] Error! The size of the read block doesn't match the value from the inode!\n"
));
FreePool (SymlinkTmp);
return EFI_VOLUME_CORRUPTED;
}
//
// Add null-terminator
//
SymlinkTmp[ReadSize] = '\0';
*AsciiSymlinkSize = SymlinkAllocateSize;
*AsciiSymlink = SymlinkTmp;
return EFI_SUCCESS;
}
/**
Reads a symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] Symlink Pointer to the output unicode symlink string.
@retval EFI_SUCCESS Symlink was read.
@retval EFI_ACCESS_DENIED Symlink is encrypted.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
@retval EFI_INVALID_PARAMETER Symlink path has incorrect length
@retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode value
**/
EFI_STATUS
Ext4ReadSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR16 **Symlink
)
{
EFI_STATUS Status;
CHAR8 *SymlinkTmp;
UINT32 SymlinkSize;
CHAR16 *Symlink16Tmp;
CHAR16 *Needle;
//
// Assume that we already read Inode via Ext4ReadInode
// Skip reading, just check encryption flag
//
if ((File->Inode->i_flags & EXT4_ENCRYPT_FL) != 0) {
DEBUG ((DEBUG_INFO, "[ext4] Warn: symlink is encrypted\n"));
return EFI_ACCESS_DENIED;
}
if (Ext4SymlinkIsFastSymlink (File)) {
Status = Ext4ReadFastSymlink (Partition, File, &SymlinkTmp, &SymlinkSize);
} else {
Status = Ext4ReadSlowSymlink (Partition, File, &SymlinkTmp, &SymlinkSize);
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Symlink read error with Status %r\n", Status));
return Status;
}
Symlink16Tmp = AllocatePool (SymlinkSize * sizeof (CHAR16));
if (Symlink16Tmp == NULL) {
DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink unicode string buffer\n"));
FreePool (SymlinkTmp);
return EFI_OUT_OF_RESOURCES;
}
Status = AsciiStrToUnicodeStrS (
SymlinkTmp,
Symlink16Tmp,
SymlinkSize
);
FreePool (SymlinkTmp);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_FS,
"[ext4] Failed to convert ascii symlink to unicode with Status %r\n",
Status
));
FreePool (Symlink16Tmp);
return Status;
}
//
// Convert to UEFI slashes
//
for (Needle = Symlink16Tmp; *Needle != L'\0'; Needle++) {
if (*Needle == L'/') {
*Needle = L'\\';
}
}
*Symlink = Symlink16Tmp;
return Status;
}