+
+ *Symlink = Symlink16Tmp;
+
+ FreePool (SymlinkTmp);
+ return Status;
+}
+
/**
Opens a new file relative to the source file's location.
- @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
+ @param[out] FoundFile A pointer to the location to return the opened handle for the new
+ file.
+ @param[in] Source A pointer to the EXT4_FILE instance that is the file
handle to the source location. This would typically be an open
handle to a directory.
- @param[out] NewHandle A pointer to the location to return the opened handle for the new
- file.
@param[in] FileName The Null-terminated string of the name of the file to be opened.
The file name may contain the following path modifiers: "\", ".",
and "..".
@@ -165,13 +381,12 @@ Ext4DirCanLookup (
**/
EFI_STATUS
-EFIAPI
-Ext4Open (
- IN EFI_FILE_PROTOCOL *This,
- OUT EFI_FILE_PROTOCOL **NewHandle,
- IN CHAR16 *FileName,
- IN UINT64 OpenMode,
- IN UINT64 Attributes
+Ext4OpenInternal (
+ OUT EXT4_FILE **FoundFile,
+ IN EXT4_FILE *Source,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
)
{
EXT4_FILE *Current;
@@ -180,13 +395,14 @@ Ext4Open (
CHAR16 PathSegment[EXT4_NAME_MAX + 1];
UINTN Length;
EXT4_FILE *File;
+ CHAR16 *Symlink;
EFI_STATUS Status;
- Current = (EXT4_FILE *)This;
+ Current = Source;
Partition = Current->Partition;
Level = 0;
- DEBUG ((DEBUG_FS, "[ext4] Ext4Open %s\n", FileName));
+ DEBUG ((DEBUG_FS, "[ext4] Ext4OpenInternal %s\n", FileName));
// If the path starts with a backslash, we treat the root directory as the base directory
if (FileName[0] == L'\\') {
FileName++;
@@ -194,6 +410,11 @@ Ext4Open (
}
while (FileName[0] != L'\0') {
+ if (Partition->Root->Symloops > SYMLOOP_MAX) {
+ DEBUG ((DEBUG_FS, "[ext4] Symloop limit is hit !\n"));
+ return EFI_ACCESS_DENIED;
+ }
+
// Discard leading path separators
while (FileName[0] == L'\\') {
FileName++;
@@ -238,18 +459,45 @@ Ext4Open (
}
// Check if this is a valid file to open in EFI
-
- // What to do with symlinks? They're nonsense when absolute but may
- // be useful when they're relative. Right now, they're ignored, since they
- // bring a lot of trouble for something that's not as useful in our case.
- // If you want to link, use hard links.
-
if (!Ext4FileIsOpenable (File)) {
Ext4CloseInternal (File);
// This looks like an /okay/ status to return.
return EFI_ACCESS_DENIED;
}
+ //
+ // Reading symlink and then trying to follow it
+ //
+ if (Ext4FileIsSymlink (File)) {
+ Partition->Root->Symloops++;
+ DEBUG ((DEBUG_FS, "[ext4] File %s is symlink, trying to read it\n", PathSegment));
+ Status = Ext4ReadSymlink (Partition, File, &Symlink);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_FS, "[ext4] Error reading %s symlink!\n", PathSegment));
+ return Status;
+ }
+
+ DEBUG ((DEBUG_FS, "[ext4] File %s is linked to %s\n", PathSegment, Symlink));
+ //
+ // Close symlink file
+ //
+ Ext4CloseInternal (File);
+ //
+ // Open linked file by recursive call of Ext4OpenFile
+ //
+ Status = Ext4OpenInternal (FoundFile, Current, Symlink, OpenMode, Attributes);
+ FreePool (Symlink);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_FS, "[ext4] Error opening linked file %s\n", Symlink));
+ return Status;
+ }
+
+ //
+ // Set File to newly opened
+ //
+ File = *FoundFile;
+ }
+
if (Level != 0) {
// Careful not to close the base directory
Ext4CloseInternal (Current);
@@ -273,12 +521,75 @@ Ext4Open (
return EFI_ACCESS_DENIED;
}
- *NewHandle = &Current->Protocol;
+ *FoundFile = Current;
DEBUG ((DEBUG_FS, "[ext4] Opened filename %s\n", Current->Dentry->Name));
return EFI_SUCCESS;
}
+/**
+ Opens a new file relative to the source file's location.
+ @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
+ handle to the source location. This would typically be an open
+ handle to a directory.
+ @param[out] NewHandle A pointer to the location to return the opened handle for the new
+ file.
+ @param[in] FileName The Null-terminated string of the name of the file to be opened.
+ The file name may contain the following path modifiers: "\", ".",
+ and "..".
+ @param[in] OpenMode The mode to open the file. The only valid combinations that the
+ file may be opened with are: Read, Read/Write, or Create/Read/Write.
+ @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the
+ attribute bits for the newly created file.
+ @retval EFI_SUCCESS The file was opened.
+ @retval EFI_NOT_FOUND The specified file could not be found on the device.
+ @retval EFI_NO_MEDIA The device has no medium.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected.
+ @retval EFI_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+Ext4Open (
+ IN EFI_FILE_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+ EXT4_FILE *FoundFile;
+ EXT4_FILE *Source;
+
+ Source = (EXT4_FILE *)This;
+
+ //
+ // Reset Symloops counter
+ //
+ Source->Partition->Root->Symloops = 0;
+
+ Status = Ext4OpenInternal (
+ &FoundFile,
+ Source,
+ FileName,
+ OpenMode,
+ Attributes
+ );
+
+ if (!EFI_ERROR (Status)) {
+ *NewHandle = &FoundFile->Protocol;
+ }
+
+ return Status;
+}
+
/**
Closes a specified file handle.
@@ -588,7 +899,7 @@ Ext4GetVolumeName (
// s_volume_name is only valid on dynamic revision; old filesystems don't support this
if (Partition->SuperBlock.s_rev_level == EXT4_DYNAMIC_REV) {
- CopyMem (TempVolName, (CONST CHAR8 *)Partition->SuperBlock.s_volume_name, 16);
+ CopyMem (TempVolName, Partition->SuperBlock.s_volume_name, 16);
TempVolName[16] = '\0';
Status = UTF8StrToUCS2 (TempVolName, &VolumeName);
@@ -754,12 +1065,14 @@ Ext4GetInfo (
OUT VOID *Buffer
)
{
+ EXT4_FILE *File;
EXT4_PARTITION *Partition;
- Partition = ((EXT4_FILE *)This)->Partition;
+ File = (EXT4_FILE *)This;
+ Partition = File->Partition;
if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
- return Ext4GetFileInfo ((EXT4_FILE *)This, Buffer, BufferSize);
+ return Ext4GetFileInfo (File, Buffer, BufferSize);
}
if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
@@ -870,12 +1183,12 @@ Ext4SetInfo (
)
{
EXT4_FILE *File;
- EXT4_PARTITION *Part;
+ EXT4_PARTITION *Partition;
- File = (EXT4_FILE *)This;
- Part = File->Partition;
+ File = (EXT4_FILE *)This;
+ Partition = File->Partition;
- if (Part->ReadOnly) {
+ if (Partition->ReadOnly) {
return EFI_WRITE_PROTECTED;
}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dxe/Inode.c
index 831f5946e870..e95299c3aee0 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Inode.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c
@@ -255,6 +255,44 @@ Ext4FileIsDir (
return (File->Inode->i_mode & EXT4_INO_TYPE_DIR) == EXT4_INO_TYPE_DIR;
}
+/**
+ Checks if a file is a symlink.
+ @param[in] File Pointer to the opened file.
+
+ @return TRUE if file is a symlink.
+**/
+BOOLEAN
+Ext4FileIsSymlink (
+ IN CONST EXT4_FILE *File
+ )
+{
+ return (File->Inode->i_mode & EXT4_INO_TYPE_SYMLINK) == EXT4_INO_TYPE_SYMLINK;
+}
+
+/**
+ Detects if a symlink is a fast symlink.
+ @param[in] File Pointer to the opened file.
+
+ @return TRUE if file is a fast symlink.
+**/
+BOOLEAN
+Ext4SymlinkIsFastSymlink (
+ IN CONST EXT4_FILE *File
+ )
+{
+ //
+ // REF:
https://github.com/heatd/Onyx/blob/master/kernel/kernel/fs/ext2/symlink.cpp#L26