diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43cbfc79..b9603350 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -373,7 +373,10 @@ jobs: toolchain: ${{ matrix.rust }} - name: Use specific dependency versions for Rust 1.63 compatibility. - run: cargo update --package=once_cell --precise=1.20.3 + run: | + cargo update --package=once_cell --precise=1.20.3 + cargo update --package=async-io --precise=2.5.0 + cargo update --package=polling --precise=3.10.0 # Don't use --all-features because some of the features have dependencies # that don't work on newer Rust versions. diff --git a/cap-primitives/src/rustix/linux/fs/stat_impl.rs b/cap-primitives/src/rustix/linux/fs/stat_impl.rs index b018c16d..df358a01 100644 --- a/cap-primitives/src/rustix/linux/fs/stat_impl.rs +++ b/cap-primitives/src/rustix/linux/fs/stat_impl.rs @@ -18,13 +18,14 @@ pub(crate) fn stat_impl( use crate::fs::{stat_unchecked, OpenOptionsExt}; use std::path::Component; - // Optimization: if path has exactly one component and it's not ".." and - // we're not following symlinks we can go straight to `stat_unchecked`, - // which is faster than doing an open with a separate fstat. + // Optimization: if path has exactly one component and it's not ".." or + // anything non-normal and we're not following symlinks we can go straight + // to `stat_unchecked`, which is faster than doing an open with a separate + // `fstat`. if follow == FollowSymlinks::No { let mut components = path.components(); - if let Some(component) = components.next() { - if components.next().is_none() && component != Component::ParentDir { + if let Some(Component::Normal(component)) = components.next() { + if components.next().is_none() { return stat_unchecked(start, component.as_ref(), FollowSymlinks::No); } } diff --git a/tests/fs_additional.rs b/tests/fs_additional.rs index 4fe7a17c..64716819 100644 --- a/tests/fs_additional.rs +++ b/tests/fs_additional.rs @@ -1380,3 +1380,23 @@ fn dotdot_slashdot_at_end_of_symlink_all_inside_dir() { let _entry = check!(entry); } } + +/// Ensure that a path of "/" is rejected. +#[test] +fn statat_slash() { + let tmpdir = tmpdir(); + + error_contains!(tmpdir.metadata("/"), "a path led outside of the filesystem"); + error_contains!( + tmpdir.metadata("/foo"), + "a path led outside of the filesystem" + ); + error_contains!( + tmpdir.symlink_metadata("/"), + "a path led outside of the filesyste" + ); + error_contains!( + tmpdir.symlink_metadata("/foo"), + "a path led outside of the filesyste" + ); +}