diff --git a/src/filesystem/canonicalization.rs b/src/filesystem/canonicalization.rs index 90843b7..6b4aa7d 100644 --- a/src/filesystem/canonicalization.rs +++ b/src/filesystem/canonicalization.rs @@ -5,6 +5,8 @@ use crate::filesystem::binding::Side; use crate::filesystem::substitution::Substitutor; use crate::filesystem::FileSystem; +use super::ext::{PathBufExt, PathExt}; + pub trait Canonicalizer { fn canonicalize>(&self, path: P, deref_final: bool) -> Result; } @@ -59,6 +61,8 @@ impl Canonicalizer for FileSystem { )); } + let trailing_slash = guest_path.with_trailing_slash(); + // build guest_path_new from user_path let mut guest_path_new = PathBuf::new(); @@ -133,13 +137,22 @@ impl Canonicalizer for FileSystem { if let Some(comp) = next_comp { new_user_path.push(comp); } - new_user_path.push(it); + it.for_each(|comp| new_user_path.push(comp)); + if trailing_slash { + // recover the trailing slash + new_user_path.try_add_trailing_slash(); + } // use new_user_path to call this function again and return // TODO: Can be optimized by replacing `it` return self.canonicalize(&new_user_path, deref_final); } // we cannot go through a path which is neither a directory nor a symlink - if !is_last_component { + if !is_last_component + || (is_last_component + && !file_type.is_dir() + && !file_type.is_symlink() + && trailing_slash) + { return Err(Error::errno_with_msg( Errno::ENOTDIR, "when canonicalizing an intermediate path", diff --git a/tests/applets.bats b/tests/applets.bats index ff3db04..0e82303 100644 --- a/tests/applets.bats +++ b/tests/applets.bats @@ -394,3 +394,30 @@ function script_test_should_not_follow { [ "$status" -eq 0 ] } + + + +function script_test_trailing_slash_in_symlink { + PATH=/bin + + cd /tmp/test_trailing_slash_in_symlink + + echo "hello world" > file + ln -s file link1 + ln -s file/ link2 + ln -s file/. link3 + + [[ "$(stat -L -c "%F" link1 2>&1)" == *"regular file"* ]] + [[ "$(stat -L -c "%F" link2 2>&1)" == *"Not a directory"* ]] + [[ "$(stat -L -c "%F" link3 2>&1)" == *"Not a directory"* ]] + +} + +@test "test trailing slash in symlink" { + local test_dir="$ROOTFS/tmp/test_trailing_slash_in_symlink" + mkdir "$test_dir" + runp proot-rs --rootfs "$ROOTFS" -- /bin/sh -e -x -c "$(declare -f script_test_trailing_slash_in_symlink); script_test_trailing_slash_in_symlink" + rm -rf "$test_dir" + [ "$status" -eq 0 ] + +}