Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
fix(ext2): symlink stored path resolution for path with length <= 60
Browse files Browse the repository at this point in the history
  • Loading branch information
RatCornu committed Jan 8, 2024
1 parent 8f4ae17 commit 86ef6a8
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 8 deletions.
50 changes: 47 additions & 3 deletions src/fs/ext2/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use alloc::vec::Vec;
use core::fmt::Debug;
use core::mem::size_of;
use core::ops::{AddAssign, SubAssign};
use core::ptr::addr_of;
use core::slice::from_raw_parts;

use itertools::Itertools;
Expand All @@ -24,6 +25,9 @@ use crate::fs::PATH_MAX;
use crate::io::{Read, Seek, SeekFrom, Write};
use crate::types::{Blkcnt, Blksize, Dev, Gid, Ino, Mode, Nlink, Off, Time, Timespec, Uid};

/// Limit in bytes for the length of a pointed path of a symbolic link to be store in an inode and not in a separate data block.
pub const SYMBOLIC_LINK_INODE_STORE_LIMIT: usize = 60;

/// General file implementation.
pub struct File<D: Device<u8, Ext2Error>> {
/// Ext2 object associated with the device containing this file.
Expand Down Expand Up @@ -869,13 +873,27 @@ impl<D: Device<u8, Ext2Error>> SymbolicLink<D> {
///
/// Returns a [`BadString`](Ext2Error::BadString) if the content of the given inode does not look like a valid path.
///
/// Returns a [`NameTooLong`](crate::fs::error::FsError::NameTooLong) if the size of the inode's content is greater than
/// [`PATH_MAX`].
///
/// Otherwise, returns the same errors as [`Ext2::inode`].
#[inline]
pub fn new(filesystem: &Celled<Ext2<D>>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
let fs = filesystem.borrow();
let file = File::new(&filesystem.clone(), inode_number)?;
let mut buffer = [0_u8; PATH_MAX];
let _: usize = file.inode.read_data(&fs.device, fs.superblock(), &mut buffer, 0)?;

let data_size = usize::try_from(file.inode.data_size()).unwrap_or(PATH_MAX);

let mut buffer = vec![0_u8; data_size];

if data_size < SYMBOLIC_LINK_INODE_STORE_LIMIT {
// SAFETY: it is always possible to read a slice of u8
buffer.clone_from_slice(unsafe {
core::slice::from_raw_parts(addr_of!(file.inode.direct_block_pointers).cast(), data_size)
});
} else {
let _: usize = file.inode.read_data(&fs.device, fs.superblock(), &mut buffer, 0)?;
}
let pointed_file = buffer.split(|char| *char == b'\0').next().ok_or(Ext2Error::BadString)?.to_vec();
Ok(Self {
file,
Expand Down Expand Up @@ -928,7 +946,7 @@ mod test {
.into_iter()
.map(|entry| entry.name.to_string_lossy().to_string())
.collect::<Vec<String>>(),
vec![".", "..", "lost+found", "big_file"]
vec![".", "..", "lost+found", "big_file", "symlink"]
);
}

Expand Down Expand Up @@ -970,11 +988,22 @@ mod test {
)
}
.unwrap();
let symlink = unsafe {
Entry::parse(
&celled_file,
Address::new(
(root_inode.direct_block_pointers[0] * superblock.block_size()) as usize
+ (dot.rec_len + two_dots.rec_len + lost_and_found.rec_len + big_file.rec_len) as usize,
),
)
}
.unwrap();

assert_eq!(dot.name.as_c_str().to_string_lossy(), ".");
assert_eq!(two_dots.name.as_c_str().to_string_lossy(), "..");
assert_eq!(lost_and_found.name.as_c_str().to_string_lossy(), "lost+found");
assert_eq!(big_file.name.as_c_str().to_string_lossy(), "big_file");
assert_eq!(symlink.name.as_c_str().to_string_lossy(), "symlink");
}

#[test]
Expand Down Expand Up @@ -1030,6 +1059,21 @@ mod test {
assert_eq!(foo.read_all().unwrap(), b"Hello world!\n");
}

#[test]
fn read_symlink() {
let file = RefCell::new(File::options().read(true).write(true).open("./tests/fs/ext2/extended.ext2").unwrap());
let ext2 = Celled::new(Ext2::new(file, 0).unwrap());
let root = Directory::new(&ext2, ROOT_DIRECTORY_INODE).unwrap();

let TypeWithFile::SymbolicLink(symlink) =
crate::file::Directory::entry(&root, UnixStr::new("symlink").unwrap()).unwrap().unwrap()
else {
panic!("`symlink` has been created as a symbolic link")
};

assert_eq!(symlink.pointed_file, "big_file");
}

#[test]
fn set_inode() {
fs::copy("./tests/fs/ext2/io_operations.ext2", "./tests/fs/ext2/io_operations_copy_set_inode.ext2").unwrap();
Expand Down
6 changes: 3 additions & 3 deletions src/fs/ext2/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,12 +399,12 @@ impl Inode {
#[inline]
pub const fn file_type(&self) -> Result<Type, Ext2Error> {
let types_permissions = self.type_permissions();
if types_permissions.contains(TypePermissions::REGULAR_FILE) {
if types_permissions.contains(TypePermissions::SYMBOLIC_LINK) {
Ok(Type::SymbolicLink)
} else if types_permissions.contains(TypePermissions::REGULAR_FILE) {
Ok(Type::Regular)
} else if types_permissions.contains(TypePermissions::DIRECTORY) {
Ok(Type::Directory)
} else if types_permissions.contains(TypePermissions::SYMBOLIC_LINK) {
Ok(Type::SymbolicLink)
} else if types_permissions.contains(TypePermissions::FIFO) {
Ok(Type::Fifo)
} else if types_permissions.contains(TypePermissions::CHARACTER_DEVICE) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

extern crate alloc;
extern crate core;
#[cfg(any(not(no_std), test, feature = "std"))]
#[cfg(feature = "std")]
extern crate std;

pub mod dev;
Expand Down
2 changes: 1 addition & 1 deletion tests/fs/ext2/extended.ext2
Git LFS file not shown

0 comments on commit 86ef6a8

Please sign in to comment.