From 5e32356f74809eac58370ce5206ec9786e083f92 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Thu, 28 Dec 2023 14:50:22 -0300 Subject: [PATCH] FreeBSD RESOLVE_BENEATH support (#296) * Update FreeBSD versions in Cirrus CI * Enable O_PATH usage on FreeBSD (Available since 13.0) * tests/fs_additional: fix incorrect FreeBSD specifics These do not apply (anymore?) * Use AT_/O_RESOLVE_BENEATH on FreeBSD >= 13.0, fixes #180 --- .cirrus.yml | 4 +- README.md | 6 ++ cap-primitives/src/fs/manually/mod.rs | 4 +- cap-primitives/src/fs/manually/open.rs | 6 +- cap-primitives/src/fs/maybe_owned_file.rs | 2 +- cap-primitives/src/rustix/freebsd/fs/check.rs | 26 ++++++ cap-primitives/src/rustix/freebsd/fs/mod.rs | 18 ++++ .../src/rustix/freebsd/fs/open_entry_impl.rs | 12 +++ .../src/rustix/freebsd/fs/open_impl.rs | 30 +++++++ .../src/rustix/freebsd/fs/remove_dir_impl.rs | 16 ++++ .../src/rustix/freebsd/fs/remove_file_impl.rs | 12 +++ .../rustix/freebsd/fs/set_permissions_impl.rs | 22 +++++ .../src/rustix/freebsd/fs/set_times_impl.rs | 45 ++++++++++ .../src/rustix/freebsd/fs/stat_impl.rs | 22 +++++ cap-primitives/src/rustix/freebsd/mod.rs | 1 + cap-primitives/src/rustix/fs/dir_utils.rs | 2 +- cap-primitives/src/rustix/fs/mod.rs | 38 +++++--- cap-primitives/src/rustix/fs/times.rs | 3 +- cap-primitives/src/rustix/mod.rs | 2 + tests/fs_additional.rs | 86 +++++-------------- 20 files changed, 270 insertions(+), 87 deletions(-) create mode 100644 cap-primitives/src/rustix/freebsd/fs/check.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/mod.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/open_entry_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/open_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/remove_dir_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/remove_file_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/set_permissions_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/set_times_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/fs/stat_impl.rs create mode 100644 cap-primitives/src/rustix/freebsd/mod.rs diff --git a/.cirrus.yml b/.cirrus.yml index abe8fce4..d6821155 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,7 +4,7 @@ task: name: stable x86_64-unknown-freebsd-13 freebsd_instance: - image_family: freebsd-13-0-snap + image_family: freebsd-13-2 setup_script: - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh @@ -18,7 +18,7 @@ task: task: name: stable x86_64-unknown-freebsd-12 freebsd_instance: - image_family: freebsd-12-1 + image_family: freebsd-12-4 setup_script: - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh diff --git a/README.md b/README.md index dcecbf7f..941694a4 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,11 @@ utilize [`openat2`], [`O_PATH`], and [`/proc/self/fd`] (though only when /proc is mounted, it's really `procfs`, and there are no mounts on top of it) for fast path resolution as well. +On FreeBSD 13.0 and newer, `cap-std` uses [`openat(O_RESOLVE_BENEATH)`] to +implement `Dir::open` with a single system call in common cases. +Several other operations internally utilize `AT_RESOLVE_BENEATH` and `O_PATH` for +fast path resolution as well. + Otherwise, `cap-std` opens each component of a path individually, in order to specially handle `..` and symlinks. The algorithm is carefully designed to minimize system calls, so opening `red/green/blue` performs just 5 system @@ -177,6 +182,7 @@ and `green`. [`openat2`]: https://lwn.net/Articles/796868/ [`O_PATH`]: https://man7.org/linux/man-pages/man2/open.2.html [`/proc/self/fd`]: https://man7.org/linux/man-pages/man5/proc.5.html +[`openat(O_RESOLVE_BENEATH)`]: https://man.freebsd.org/cgi/man.cgi?openat ## What about networking? diff --git a/cap-primitives/src/fs/manually/mod.rs b/cap-primitives/src/fs/manually/mod.rs index c2c3bd32..34da44f9 100644 --- a/cap-primitives/src/fs/manually/mod.rs +++ b/cap-primitives/src/fs/manually/mod.rs @@ -5,7 +5,7 @@ mod canonical_path; mod canonicalize; mod cow_component; mod open; -#[cfg(not(windows))] +#[cfg(not(any(windows, target_os = "freebsd")))] mod open_entry; mod read_link_one; @@ -19,5 +19,5 @@ pub(super) use canonicalize::canonicalize_with; pub(crate) use canonicalize::canonicalize; pub(crate) use open::{open, stat}; -#[cfg(not(windows))] +#[cfg(not(any(windows, target_os = "freebsd")))] pub(crate) use open_entry::open_entry; diff --git a/cap-primitives/src/fs/manually/open.rs b/cap-primitives/src/fs/manually/open.rs index 9bebe14d..6c630fe2 100644 --- a/cap-primitives/src/fs/manually/open.rs +++ b/cap-primitives/src/fs/manually/open.rs @@ -6,7 +6,7 @@ use crate::fs::{ dir_options, errors, open_unchecked, path_has_trailing_dot, path_has_trailing_slash, stat_unchecked, FollowSymlinks, MaybeOwnedFile, Metadata, OpenOptions, OpenUncheckedError, }; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] use rustix::fs::OFlags; use std::borrow::Cow; use std::ffi::OsStr; @@ -247,7 +247,7 @@ impl<'start> Context<'start> { Ok(file) => { // Emulate `O_PATH` + `FollowSymlinks::Yes` on Linux. If `file` // is a symlink, follow it. - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] if should_emulate_o_path(&use_options) { match read_link_one( &file, @@ -527,7 +527,7 @@ pub(crate) fn stat(start: &fs::File, path: &Path, follow: FollowSymlinks) -> io: /// Test whether the given options imply that we should treat an open file as /// potentially being a symlink we need to follow, due to use of `O_PATH`. -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] fn should_emulate_o_path(use_options: &OpenOptions) -> bool { (use_options.ext.custom_flags & (OFlags::PATH.bits() as i32)) == (OFlags::PATH.bits() as i32) && use_options.follow == FollowSymlinks::Yes diff --git a/cap-primitives/src/fs/maybe_owned_file.rs b/cap-primitives/src/fs/maybe_owned_file.rs index 2e1a888c..443cba24 100644 --- a/cap-primitives/src/fs/maybe_owned_file.rs +++ b/cap-primitives/src/fs/maybe_owned_file.rs @@ -120,7 +120,7 @@ impl<'borrow> MaybeOwnedFile<'borrow> { } /// Assuming `self` holds an owned `File`, return it. - #[cfg_attr(windows, allow(dead_code))] + #[cfg_attr(any(windows, target_os = "freebsd"), allow(dead_code))] pub(super) fn unwrap_owned(self) -> fs::File { match self.inner { MaybeOwned::Owned(file) => file, diff --git a/cap-primitives/src/rustix/freebsd/fs/check.rs b/cap-primitives/src/rustix/freebsd/fs/check.rs new file mode 100644 index 00000000..2a823e69 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/check.rs @@ -0,0 +1,26 @@ +use rustix::fs::{statat, AtFlags}; +use std::fs; +use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; + +static WORKING: AtomicBool = AtomicBool::new(false); +static CHECKED: AtomicBool = AtomicBool::new(false); + +#[inline] +pub(crate) fn beneath_supported(start: &fs::File) -> bool { + if WORKING.load(Relaxed) { + return true; + } + if CHECKED.load(Relaxed) { + return false; + } + // Unknown O_ flags get ignored but AT_ flags have strict checks, so we use that. + if let Err(rustix::io::Errno::INVAL) = + statat(start, "", AtFlags::EMPTY_PATH | AtFlags::RESOLVE_BENEATH) + { + CHECKED.store(true, Relaxed); + false + } else { + WORKING.store(true, Relaxed); + true + } +} diff --git a/cap-primitives/src/rustix/freebsd/fs/mod.rs b/cap-primitives/src/rustix/freebsd/fs/mod.rs new file mode 100644 index 00000000..2f1ef261 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/mod.rs @@ -0,0 +1,18 @@ +mod check; +mod open_entry_impl; +mod open_impl; +mod remove_dir_impl; +mod remove_file_impl; +mod set_permissions_impl; +mod set_times_impl; +mod stat_impl; + +pub(crate) use crate::fs::manually::canonicalize as canonicalize_impl; +pub(crate) use check::beneath_supported; +pub(crate) use open_entry_impl::open_entry_impl; +pub(crate) use open_impl::open_impl; +pub(crate) use remove_dir_impl::remove_dir_impl; +pub(crate) use remove_file_impl::remove_file_impl; +pub(crate) use set_permissions_impl::set_permissions_impl; +pub(crate) use set_times_impl::{set_times_impl, set_times_nofollow_impl}; +pub(crate) use stat_impl::stat_impl; diff --git a/cap-primitives/src/rustix/freebsd/fs/open_entry_impl.rs b/cap-primitives/src/rustix/freebsd/fs/open_entry_impl.rs new file mode 100644 index 00000000..2bf5645b --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/open_entry_impl.rs @@ -0,0 +1,12 @@ +use crate::fs::{open_impl, OpenOptions}; +use std::ffi::OsStr; +use std::{fs, io}; + +#[inline(always)] +pub(crate) fn open_entry_impl( + start: &fs::File, + path: &OsStr, + options: &OpenOptions, +) -> io::Result { + open_impl(start, path.as_ref(), options) +} diff --git a/cap-primitives/src/rustix/freebsd/fs/open_impl.rs b/cap-primitives/src/rustix/freebsd/fs/open_impl.rs new file mode 100644 index 00000000..7924f4d5 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/open_impl.rs @@ -0,0 +1,30 @@ +use super::super::super::fs::compute_oflags; +use crate::fs::{errors, manually, OpenOptions}; +use io_lifetimes::FromFd; +use rustix::fs::{openat, Mode, OFlags, RawMode}; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn open_impl( + start: &fs::File, + path: &Path, + options: &OpenOptions, +) -> io::Result { + if !super::beneath_supported(start) { + return manually::open(start, path, options); + } + + let oflags = compute_oflags(options)? | OFlags::RESOLVE_BENEATH; + + let mode = if oflags.contains(OFlags::CREATE) { + Mode::from_bits((options.ext.mode & 0o7777) as RawMode).unwrap() + } else { + Mode::empty() + }; + + match openat(start, path, oflags, mode) { + Ok(file) => Ok(fs::File::from_into_fd(file)), + Err(rustix::io::Errno::NOTCAPABLE) => Err(errors::escape_attempt()), + Err(err) => Err(err.into()), + } +} diff --git a/cap-primitives/src/rustix/freebsd/fs/remove_dir_impl.rs b/cap-primitives/src/rustix/freebsd/fs/remove_dir_impl.rs new file mode 100644 index 00000000..0c2fa4b2 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/remove_dir_impl.rs @@ -0,0 +1,16 @@ +use crate::fs::via_parent; +use rustix::fs::{unlinkat, AtFlags}; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn remove_dir_impl(start: &fs::File, path: &Path) -> io::Result<()> { + if !super::beneath_supported(start) { + return via_parent::remove_dir(start, path); + } + + Ok(unlinkat( + start, + path, + AtFlags::RESOLVE_BENEATH | AtFlags::REMOVEDIR, + )?) +} diff --git a/cap-primitives/src/rustix/freebsd/fs/remove_file_impl.rs b/cap-primitives/src/rustix/freebsd/fs/remove_file_impl.rs new file mode 100644 index 00000000..79e72287 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/remove_file_impl.rs @@ -0,0 +1,12 @@ +use crate::fs::via_parent; +use rustix::fs::{unlinkat, AtFlags}; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn remove_file_impl(start: &fs::File, path: &Path) -> io::Result<()> { + if !super::beneath_supported(start) { + return via_parent::remove_file(start, path); + } + + Ok(unlinkat(start, path, AtFlags::RESOLVE_BENEATH)?) +} diff --git a/cap-primitives/src/rustix/freebsd/fs/set_permissions_impl.rs b/cap-primitives/src/rustix/freebsd/fs/set_permissions_impl.rs new file mode 100644 index 00000000..47c36b7d --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/set_permissions_impl.rs @@ -0,0 +1,22 @@ +use crate::fs::Permissions; +use rustix::fs::{chmodat, AtFlags, Mode}; +use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn set_permissions_impl( + start: &fs::File, + path: &Path, + perm: Permissions, +) -> io::Result<()> { + if !super::beneath_supported(start) { + return super::super::super::fs::set_permissions_manually(start, path, perm); + } + + Ok(chmodat( + start, + path, + Mode::from_raw_mode(perm.mode() as _), + AtFlags::RESOLVE_BENEATH, + )?) +} diff --git a/cap-primitives/src/rustix/freebsd/fs/set_times_impl.rs b/cap-primitives/src/rustix/freebsd/fs/set_times_impl.rs new file mode 100644 index 00000000..85dc8e60 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/set_times_impl.rs @@ -0,0 +1,45 @@ +use crate::fs::{to_timespec, via_parent, SystemTimeSpec}; +use rustix::fs::{utimensat, AtFlags, Timestamps}; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn set_times_impl( + start: &fs::File, + path: &Path, + atime: Option, + mtime: Option, +) -> io::Result<()> { + if !super::beneath_supported(start) { + return super::super::super::fs::set_times_manually(start, path, atime, mtime); + } + + let times = Timestamps { + last_access: to_timespec(atime)?, + last_modification: to_timespec(mtime)?, + }; + + Ok(utimensat(start, path, ×, AtFlags::RESOLVE_BENEATH)?) +} + +pub(crate) fn set_times_nofollow_impl( + start: &fs::File, + path: &Path, + atime: Option, + mtime: Option, +) -> io::Result<()> { + if !super::beneath_supported(start) { + return via_parent::set_times_nofollow(start, path, atime, mtime); + } + + let times = Timestamps { + last_access: to_timespec(atime)?, + last_modification: to_timespec(mtime)?, + }; + + Ok(utimensat( + start, + path, + ×, + AtFlags::RESOLVE_BENEATH | AtFlags::SYMLINK_NOFOLLOW, + )?) +} diff --git a/cap-primitives/src/rustix/freebsd/fs/stat_impl.rs b/cap-primitives/src/rustix/freebsd/fs/stat_impl.rs new file mode 100644 index 00000000..3dfe62b4 --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/fs/stat_impl.rs @@ -0,0 +1,22 @@ +use crate::fs::{manually, FollowSymlinks, Metadata, MetadataExt}; +use rustix::fs::{statat, AtFlags}; +use std::path::Path; +use std::{fs, io}; + +pub(crate) fn stat_impl( + start: &fs::File, + path: &Path, + follow: FollowSymlinks, +) -> io::Result { + if !super::beneath_supported(start) { + return manually::stat(start, path, follow); + } + + let flags = AtFlags::RESOLVE_BENEATH + | if follow == FollowSymlinks::Yes { + AtFlags::empty() + } else { + AtFlags::SYMLINK_NOFOLLOW + }; + Ok(MetadataExt::from_rustix(statat(start, path, flags)?)) +} diff --git a/cap-primitives/src/rustix/freebsd/mod.rs b/cap-primitives/src/rustix/freebsd/mod.rs new file mode 100644 index 00000000..aabc2afc --- /dev/null +++ b/cap-primitives/src/rustix/freebsd/mod.rs @@ -0,0 +1 @@ +pub(crate) mod fs; diff --git a/cap-primitives/src/rustix/fs/dir_utils.rs b/cap-primitives/src/rustix/fs/dir_utils.rs index e58a7d9c..f5db324b 100644 --- a/cap-primitives/src/rustix/fs/dir_utils.rs +++ b/cap-primitives/src/rustix/fs/dir_utils.rs @@ -127,6 +127,7 @@ pub(crate) const fn target_o_path() -> OFlags { #[cfg(any( target_os = "android", target_os = "emscripten", + target_os = "freebsd", target_os = "fuchsia", target_os = "linux", target_os = "redox", @@ -137,7 +138,6 @@ pub(crate) const fn target_o_path() -> OFlags { #[cfg(any( target_os = "dragonfly", - target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "netbsd", diff --git a/cap-primitives/src/rustix/fs/mod.rs b/cap-primitives/src/rustix/fs/mod.rs index ccfa41c0..ac45f84a 100644 --- a/cap-primitives/src/rustix/fs/mod.rs +++ b/cap-primitives/src/rustix/fs/mod.rs @@ -37,18 +37,18 @@ mod times; pub(crate) mod errors; -// On Linux, use optimized implementations of `open` and `stat` using `openat2` -// and `O_PATH` when available. +// On Linux, use optimized implementations based on +// `openat2` and `O_PATH` when available. // -// FreeBSD has a similar mechanism in `O_BENEATH`, however it appears to have -// different behavior on absolute and `..` paths in ways that make it -// unsuitable for `cap-std`'s style of sandboxing. For more information, see -// the bug filed upstream: +// On FreeBSD, use optimized implementations based on +// `O_RESOLVE_BENEATH`/`AT_RESOLVE_BENEATH` and `O_PATH` when available. #[cfg(any(target_os = "macos", target_os = "ios"))] pub(crate) use crate::rustix::darwin::fs::*; +#[cfg(target_os = "freebsd")] +pub(crate) use crate::rustix::freebsd::fs::*; #[cfg(any(target_os = "android", target_os = "linux"))] pub(crate) use crate::rustix::linux::fs::*; -#[cfg(not(any(target_os = "android", target_os = "linux")))] +#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "freebsd")))] #[rustfmt::skip] pub(crate) use crate::fs::{ manually::open_entry as open_entry_impl, @@ -66,13 +66,21 @@ pub(super) use file_path::file_path_by_ttyname_or_seaching; target_os = "ios" )))] pub(crate) use file_path::file_path_by_ttyname_or_seaching as file_path; -#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "wasi")))] +#[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd", + target_os = "wasi" +)))] pub(crate) use set_permissions_impl::set_permissions_impl; +#[cfg(target_os = "freebsd")] +pub(crate) use set_permissions_impl::set_permissions_impl as set_permissions_manually; #[cfg(not(target_os = "wasi"))] pub(crate) use set_symlink_permissions_unchecked::set_symlink_permissions_unchecked; -#[cfg(not(any(target_os = "android", target_os = "linux")))] +#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "freebsd")))] pub(crate) use set_times_impl::set_times_impl; - +#[cfg(target_os = "freebsd")] +pub(crate) use set_times_impl::set_times_impl as set_times_manually; #[rustfmt::skip] pub(crate) use crate::fs::{ via_parent::access as access_impl, @@ -80,13 +88,17 @@ pub(crate) use crate::fs::{ via_parent::create_dir as create_dir_impl, via_parent::read_link as read_link_impl, via_parent::rename as rename_impl, - via_parent::remove_dir as remove_dir_impl, via_parent::symlink as symlink_impl, - via_parent::remove_file as remove_file_impl, remove_open_dir_by_searching as remove_open_dir_impl, }; #[cfg(not(target_os = "wasi"))] pub(crate) use crate::fs::via_parent::set_symlink_permissions as set_symlink_permissions_impl; +#[cfg(not(target_os = "freebsd"))] +#[rustfmt::skip] +pub(crate) use crate::fs::{ + via_parent::remove_dir as remove_dir_impl, + via_parent::remove_file as remove_file_impl, +}; pub(crate) use access_unchecked::access_unchecked; pub(crate) use copy_impl::copy_impl; @@ -116,7 +128,7 @@ pub(crate) use reopen_impl::reopen_impl; pub(crate) use stat_unchecked::stat_unchecked; pub(crate) use symlink_unchecked::symlink_unchecked; #[allow(unused_imports)] -pub(crate) use times::{set_times_follow_unchecked, set_times_nofollow_unchecked}; +pub(crate) use times::{set_times_follow_unchecked, set_times_nofollow_unchecked, to_timespec}; // On Linux, there is a limit of 40 symlink expansions. // Source: diff --git a/cap-primitives/src/rustix/fs/times.rs b/cap-primitives/src/rustix/fs/times.rs index d4641c9d..555ebe63 100644 --- a/cap-primitives/src/rustix/fs/times.rs +++ b/cap-primitives/src/rustix/fs/times.rs @@ -7,7 +7,7 @@ use std::path::Path; use std::{fs, io}; #[allow(clippy::useless_conversion)] -fn to_timespec(ft: Option) -> io::Result { +pub(crate) fn to_timespec(ft: Option) -> io::Result { Ok(match ft { None => Timespec { tv_sec: 0, @@ -33,6 +33,7 @@ fn to_timespec(ft: Option) -> io::Result { }) } +#[allow(dead_code)] pub(crate) fn set_times_nofollow_unchecked( start: &fs::File, path: &Path, diff --git a/cap-primitives/src/rustix/mod.rs b/cap-primitives/src/rustix/mod.rs index 6a19568a..2a7a2f10 100644 --- a/cap-primitives/src/rustix/mod.rs +++ b/cap-primitives/src/rustix/mod.rs @@ -5,5 +5,7 @@ pub(crate) mod fs; #[cfg(any(target_os = "macos", target_os = "ios"))] mod darwin; +#[cfg(target_os = "freebsd")] +mod freebsd; #[cfg(any(target_os = "android", target_os = "linux"))] mod linux; diff --git a/tests/fs_additional.rs b/tests/fs_additional.rs index 63fd6f0d..279ba215 100644 --- a/tests/fs_additional.rs +++ b/tests/fs_additional.rs @@ -454,33 +454,16 @@ fn check_dot_access() { check!(tmpdir.metadata("dir/")); check!(tmpdir.metadata("dir//")); - #[cfg(not(target_os = "freebsd"))] - { - assert!(tmpdir.metadata("dir/.").is_err()); - assert!(tmpdir.metadata("dir/./").is_err()); - assert!(tmpdir.metadata("dir/.//").is_err()); - assert!(tmpdir.metadata("dir/./.").is_err()); - assert!(tmpdir.metadata("dir/.//.").is_err()); - assert!(tmpdir.metadata("dir/..").is_err()); - assert!(tmpdir.metadata("dir/../").is_err()); - assert!(tmpdir.metadata("dir/..//").is_err()); - assert!(tmpdir.metadata("dir/../.").is_err()); - assert!(tmpdir.metadata("dir/..//.").is_err()); - } - - #[cfg(target_os = "freebsd")] - { - assert!(tmpdir.metadata("dir/.").is_ok()); - assert!(tmpdir.metadata("dir/./").is_ok()); - assert!(tmpdir.metadata("dir/.//").is_ok()); - assert!(tmpdir.metadata("dir/./.").is_ok()); - assert!(tmpdir.metadata("dir/.//.").is_ok()); - assert!(tmpdir.metadata("dir/..").is_ok()); - assert!(tmpdir.metadata("dir/../").is_ok()); - assert!(tmpdir.metadata("dir/..//").is_ok()); - assert!(tmpdir.metadata("dir/../.").is_ok()); - assert!(tmpdir.metadata("dir/..//.").is_ok()); - } + assert!(tmpdir.metadata("dir/.").is_err()); + assert!(tmpdir.metadata("dir/./").is_err()); + assert!(tmpdir.metadata("dir/.//").is_err()); + assert!(tmpdir.metadata("dir/./.").is_err()); + assert!(tmpdir.metadata("dir/.//.").is_err()); + assert!(tmpdir.metadata("dir/..").is_err()); + assert!(tmpdir.metadata("dir/../").is_err()); + assert!(tmpdir.metadata("dir/..//").is_err()); + assert!(tmpdir.metadata("dir/../.").is_err()); + assert!(tmpdir.metadata("dir/..//.").is_err()); } /// This test is the same as `check_dot_access` but uses `std::fs`' @@ -503,33 +486,16 @@ fn check_dot_access_ambient() { check!(fs::metadata(dir.path().join("dir/"))); check!(fs::metadata(dir.path().join("dir//"))); - #[cfg(not(target_os = "freebsd"))] - { - assert!(fs::metadata(dir.path().join("dir/.")).is_err()); - assert!(fs::metadata(dir.path().join("dir/./")).is_err()); - assert!(fs::metadata(dir.path().join("dir/.//")).is_err()); - assert!(fs::metadata(dir.path().join("dir/./.")).is_err()); - assert!(fs::metadata(dir.path().join("dir/.//.")).is_err()); - assert!(fs::metadata(dir.path().join("dir/..")).is_err()); - assert!(fs::metadata(dir.path().join("dir/../")).is_err()); - assert!(fs::metadata(dir.path().join("dir/..//")).is_err()); - assert!(fs::metadata(dir.path().join("dir/../.")).is_err()); - assert!(fs::metadata(dir.path().join("dir/..//.")).is_err()); - } - - #[cfg(target_os = "freebsd")] - { - assert!(fs::metadata(dir.path().join("dir/.")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/./")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/.//")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/./.")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/.//.")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/..")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/../")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/..//")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/../.")).is_ok()); - assert!(fs::metadata(dir.path().join("dir/..//.")).is_ok()); - } + assert!(fs::metadata(dir.path().join("dir/.")).is_err()); + assert!(fs::metadata(dir.path().join("dir/./")).is_err()); + assert!(fs::metadata(dir.path().join("dir/.//")).is_err()); + assert!(fs::metadata(dir.path().join("dir/./.")).is_err()); + assert!(fs::metadata(dir.path().join("dir/.//.")).is_err()); + assert!(fs::metadata(dir.path().join("dir/..")).is_err()); + assert!(fs::metadata(dir.path().join("dir/../")).is_err()); + assert!(fs::metadata(dir.path().join("dir/..//")).is_err()); + assert!(fs::metadata(dir.path().join("dir/../.")).is_err()); + assert!(fs::metadata(dir.path().join("dir/..//.")).is_err()); } // Windows allows one to open "file/." and "file/.." and similar, however it @@ -689,17 +655,13 @@ fn dir_unsearchable_unreadable() { // below confirming this. if cfg!(any( target_os = "android", + target_os = "freebsd", target_os = "linux", target_os = "redox", )) { let dir = check!(tmpdir.open_dir("dir")); assert!(dir.entries().is_err()); assert!(dir.open_dir(".").is_err()); - } else if cfg!(target_os = "freebsd") { - let dir = check!(tmpdir.open_dir("dir")); - check!(dir.metadata(".")); - check!(dir.entries()); - check!(dir.open_dir(".")); } else { assert!(tmpdir.open_dir("dir").is_err()); } @@ -719,19 +681,15 @@ fn dir_unsearchable_unreadable_ambient() { options.mode(0o000); check!(options.create(dir.path().join("dir"))); - // FreeBSD seems able to open directories with 0o000 permissions. if cfg!(any( target_os = "android", target_os = "linux", + target_os = "freebsd", target_os = "redox", )) { assert!(std::fs::File::open(dir.path().join("dir")).is_err()); assert!(std::fs::read_dir(dir.path().join("dir")).is_err()); assert!(std::fs::File::open(dir.path().join("dir/.")).is_err()); - } else if cfg!(target_os = "freebsd") { - check!(std::fs::File::open(dir.path().join("dir"))); - check!(std::fs::read_dir(dir.path().join("dir"))); - check!(std::fs::File::open(dir.path().join("dir/."))); } }