diff --git a/src/error.rs b/src/error.rs index 5abf068d..f577afc4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -143,6 +143,11 @@ pub enum Error { pub(crate) trait ErrorExt { /// Wrap a `Result<..., Error>` with an additional context string. fn wrap>(self, context: S) -> Self; + + /// Wrap a `Result<..., Error>` with an additional context string created by a closure. + fn with_wrap(self, context_fn: F) -> Self + where + F: FnOnce() -> String; } impl ErrorExt for Result { @@ -151,6 +156,13 @@ impl ErrorExt for Result { context: context.into(), }) } + + fn with_wrap(self, context_fn: F) -> Self + where + F: FnOnce() -> String, + { + self.wrap(context_fn()) + } } /// A backport of the nightly-only [`Chain`]. This method diff --git a/src/resolvers/opath.rs b/src/resolvers/opath.rs index 68abb699..1302fe14 100644 --- a/src/resolvers/opath.rs +++ b/src/resolvers/opath.rs @@ -247,23 +247,16 @@ pub(crate) fn resolve>( // Don't continue walking if user asked for no symlinks. if flags.contains(ResolverFlags::NO_SYMLINKS) { - return error::SafetyViolationSnafu { - description: "next is a symlink and symlink resolution disabled", - } - .fail(); - } - - // Check if it's safe for us to touch. In principle this should - // never be an actual security issue (since we readlink(2) the - // symlink) but it's much better to be safe than sorry here. - if next - .is_dangerous() - .wrap("check if next is on a dangerous filesystem")? - { - return error::SafetyViolationSnafu { - description: "next is a symlink on a dangerous filesystem", - } - .fail(); + return Err(IOError::from_raw_os_error(libc::ELOOP)) + .context(error::OsSnafu { + operation: "emulated symlink resolution", + }) + .with_wrap(|| { + format!( + "component {:?} is a symlink but symlink resolution is disabled", + part + ) + })?; } // We need a limit on the number of symlinks we traverse to @@ -280,6 +273,34 @@ pub(crate) fn resolve>( operation: "readlink next symlink component", })?; + // Check if it's a good idea to walk this symlink. If we are on a + // filesystem that supports magic-links and we've hit an absolute + // symlink, it is incredibly likely that this component is a magic-link + // and it makes no sense to try to resolve it in userspace. + // + // NOTE: There are some pseudo-magic-links like /proc/self (which + // dynamically generates the symlink contents but doesn't use + // nd_jump_link). In the case of procfs, these are always relative, and + // they are reasonable for us to walk. + // + // In procfs, all magic-links use d_path() to generate readlink() and + // thus are all absolute paths. (Unfortunately, apparmorfs uses + // nd_jump_link to make /sys/kernel/security/apparmor/policy dynamic + // using actual nd_jump_link() and their readlink give us a dummy + // relative path like "apparmorfs:[123]". But in that case we will just + // get an error.) + if link_target.is_absolute() + && next + .is_dangerous() + .wrap("check if next is on a dangerous filesystem")? + { + return Err(IOError::from_raw_os_error(libc::ELOOP)) + .context(error::OsSnafu { + operation: "emulated RESOLVE_NO_MAGICLINKS", + }) + .wrap("walked into a potential magic-link")?; + } + // Remove the link component from our expectex path. expected_path.pop(); diff --git a/src/syscalls.rs b/src/syscalls.rs index 030a8961..a3b09a36 100644 --- a/src/syscalls.rs +++ b/src/syscalls.rs @@ -752,3 +752,9 @@ pub fn openat2>(dirfd: RawFd, path: P, how: &OpenHow) -> Result libc::pid_t { + // SAFETY: Obviously safe libc function. + unsafe { libc::getpid() } +} diff --git a/src/tests/test_resolve.rs b/src/tests/test_resolve.rs index 35db4cb3..60260603 100644 --- a/src/tests/test_resolve.rs +++ b/src/tests/test_resolve.rs @@ -16,10 +16,11 @@ * with this program. If not, see . */ -use std::fs::File; +use std::{fs::File, path::Path}; use crate::{ resolvers::{opath, openat2}, + syscalls, tests::common as tests_common, Handle, ResolverBackend, ResolverFlags, Root, }; @@ -32,74 +33,99 @@ macro_rules! resolve_tests { // abc("foo") => ExpectedResult::Err(..); // xyz("baz") => ExpectedResult::Ok{..}; // } - ($($test_name:ident ( $unsafe_path:expr ) => $expected:expr);+ $(;)?) => { + ($(let $root_dir_var:ident = $root_dir:expr => @reopen_tests:$reopen_tests:ident { $($test_name:ident ( $unsafe_path:expr, $rflags:expr ) => $expected:expr);+ $(;)? });* $(;)?) => { paste::paste! { - $( + $( $( #[test] fn []() -> Result<(), Error> { - let root_dir = tests_common::create_basic_tree()?; - let root = Root::open(&root_dir)?; + let $root_dir_var = $root_dir; + let mut root = Root::open(&$root_dir_var)?; + root.resolver.flags = $rflags; let expected = $expected; - utils::check_resolve_in_root(&root, $unsafe_path, &expected)?; + let reopen_tests: bool = $reopen_tests; + utils::check_resolve_in_root( + &root, + $unsafe_path, + &expected, + reopen_tests, + )?; - // Make sure root_dir is not dropped earlier. - let _root_dir = root_dir; + // Make sure $root_dir_var is not dropped earlier. + let _root_dir_var = $root_dir_var; Ok(()) } #[test] fn []() -> Result<(), Error> { - let root_dir = tests_common::create_basic_tree()?; - let mut root = Root::open(&root_dir)?; + let $root_dir_var = $root_dir; + let mut root = Root::open(&$root_dir_var)?; + root.resolver.flags = $rflags; root.resolver.backend = ResolverBackend::EmulatedOpath; let expected = $expected; - utils::check_resolve_in_root(&root, $unsafe_path, &expected)?; + let reopen_tests: bool = $reopen_tests; + utils::check_resolve_in_root( + &root, + $unsafe_path, + &expected, + reopen_tests, + )?; - // Make sure root_dir is not dropped earlier. - let _root_dir = root_dir; + // Make sure $root_dir_var is not dropped earlier. + let _root_dir_var = $root_dir_var; Ok(()) } #[test] fn []() -> Result<(), Error> { - let root_dir = tests_common::create_basic_tree()?; - let mut root = Root::open(&root_dir)?; + let $root_dir_var = $root_dir; + let mut root = Root::open(&$root_dir_var)?; + root.resolver.flags = $rflags; root.resolver.backend = ResolverBackend::KernelOpenat2; + if !root.resolver.backend.supported() { // skip test return Ok(()); } let expected = $expected; - utils::check_resolve_in_root(&root, $unsafe_path, &expected)?; + let reopen_tests: bool = $reopen_tests; + utils::check_resolve_in_root( + &root, + $unsafe_path, + &expected, + reopen_tests, + )?; - // Make sure root_dir is not dropped earlier. - let _root_dir = root_dir; + // Make sure $root_dir_var is not dropped earlier. + let _root_dir_var = $root_dir_var; Ok(()) } #[test] fn []() -> Result<(), Error> { - let root_dir = tests_common::create_basic_tree()?; - let root = File::open(&root_dir)?; + let $root_dir_var = $root_dir; + let root = File::open(&$root_dir_var)?; + let rflags = $rflags; let expected = $expected; + let reopen_tests: bool = $reopen_tests; utils::check_resolve_fn( |file, subpath| { - opath::resolve(file, subpath, ResolverFlags::default()) + opath::resolve(file, subpath, rflags) .map(Handle::into_file) }, &root, $unsafe_path, &expected, + reopen_tests, )?; - // Make sure root_dir is not dropped earlier. - let _root_dir = root_dir; + // Make sure $root_dir_var is not dropped earlier. + let _root_dir_var = $root_dir_var; Ok(()) } @@ -111,166 +137,194 @@ macro_rules! resolve_tests { return Ok(()); } - let root_dir = tests_common::create_basic_tree()?; - let root = File::open(&root_dir)?; + let $root_dir_var = $root_dir; + let root = File::open(&$root_dir_var)?; + let rflags = $rflags; let expected = $expected; + let reopen_tests: bool = $reopen_tests; utils::check_resolve_fn( |file, subpath| { - openat2::resolve(file, subpath, ResolverFlags::default()) + openat2::resolve(file, subpath, rflags) .map(Handle::into_file) }, &root, $unsafe_path, &expected, + reopen_tests, )?; - // Make sure root_dir is not dropped earlier. - let _root_dir = root_dir; + // Make sure $root_dir_var is not dropped earlier. + let _root_dir_var = $root_dir_var; Ok(()) } - )* + )* )* } } } resolve_tests! { + let proc_root_dir = Path::new("/proc") => @reopen_tests:false { + proc_pseudo_magiclink("self/status", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: &*format!("{}/status", syscalls::getpid()), file_type: libc::S_IFREG }; + proc_pseudo_magiclink_nosym1("self", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + proc_pseudo_magiclink_nosym2("self/status", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + proc_pseudo_magiclink_nofollow1("self", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: "self", file_type: libc::S_IFLNK }; + proc_pseudo_magiclink_nofollow2("self/status", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: &*format!("{}/status", syscalls::getpid()), file_type: libc::S_IFREG }; + + proc_magiclink("self/exe", ResolverFlags::empty()) => ExpectedResult::Err(libc::ELOOP); + proc_magiclink_nofollow("self/exe", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: &*format!("{}/exe", syscalls::getpid()), file_type: libc::S_IFLNK }; + proc_magiclink_component_nofollow("self/root/etc/passwd", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Err(libc::ELOOP); + }; + // Complete lookups. - complete_root1("/") => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; - complete_root2("/../../../../../..") => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; - complete_root_link1("root-link1") => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; - complete_root_link2("root-link2") => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; - complete_root_link3("root-link3") => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; - complete_dir1("a") => ExpectedResult::Ok { real_path: "/a", file_type: libc::S_IFDIR }; - complete_dir2("b/c/d/e/f") => ExpectedResult::Ok { real_path: "/b/c/d/e/f", file_type: libc::S_IFDIR }; - complete_dir3("b///././c////.//d/./././///e////.//./f//././././") => ExpectedResult::Ok { real_path: "/b/c/d/e/f", file_type: libc::S_IFDIR }; - complete_file("b/c/file") => ExpectedResult::Ok { real_path: "/b/c/file", file_type: libc::S_IFREG }; - complete_file_link("b-file") => ExpectedResult::Ok { real_path: "/b/c/file", file_type: libc::S_IFREG }; - complete_fifo("b/fifo") => ExpectedResult::Ok { real_path: "/b/fifo", file_type: libc::S_IFIFO }; - complete_sock("b/sock") => ExpectedResult::Ok { real_path: "/b/sock", file_type: libc::S_IFSOCK }; - // Partial lookups. - partial_dir_basic("a/b/c/d/e/f/g/h") => ExpectedResult::Err(libc::ENOENT); - partial_dir_dotdot("a/foo/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - // Complete lookups of non_lexical symlinks. - nonlexical_basic_complete("target") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_basic_complete1("target/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_basic_complete2("target//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_basic_partial("target/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_basic_partial_dotdot("target/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level1_abs_complete1("link1/target_abs") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_abs_complete2("link1/target_abs/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_abs_complete3("link1/target_abs//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_abs_partial("link1/target_abs/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level1_abs_partial_dotdot("link1/target_abs/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level1_rel_complete1("link1/target_rel") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_rel_complete2("link1/target_rel/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_rel_complete3("link1/target_rel//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level1_rel_partial("link1/target_rel/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level1_rel_partial_dotdot("link1/target_rel/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_abs_complete1("link2/link1_abs/target_abs") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_abs_complete2("link2/link1_abs/target_abs/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_abs_complete3("link2/link1_abs/target_abs//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_abs_partial("link2/link1_abs/target_abs/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_abs_partial_dotdot("link2/link1_abs/target_abs/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_rel_complete1("link2/link1_abs/target_rel") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_rel_complete2("link2/link1_abs/target_rel/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_rel_complete3("link2/link1_abs/target_rel//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_rel_partial("link2/link1_abs/target_rel/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_rel_partial_dotdot("link2/link1_abs/target_rel/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_open_complete1("link2/link1_abs/../target") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_open_complete2("link2/link1_abs/../target/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_open_complete3("link2/link1_abs/../target//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_abs_open_partial("link2/link1_abs/../target/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_abs_open_partial_dotdot("link2/link1_abs/../target/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_abs_complete1("link2/link1_rel/target_abs") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_abs_complete2("link2/link1_rel/target_abs/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_abs_complete3("link2/link1_rel/target_abs//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_abs_partial("link2/link1_rel/target_abs/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_abs_partial_dotdot("link2/link1_rel/target_abs/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_rel_complete1("link2/link1_rel/target_rel") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_rel_complete2("link2/link1_rel/target_rel/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_rel_complete3("link2/link1_rel/target_rel//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_rel_partial("link2/link1_rel/target_rel/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_rel_partial_dotdot("link2/link1_rel/target_rel/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_open_complete1("link2/link1_rel/../target") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_open_complete2("link2/link1_rel/../target/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_open_complete3("link2/link1_rel/../target//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level2_rel_open_partial("link2/link1_rel/../target/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level2_rel_open_partial_dotdot("link2/link1_rel/../target/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level3_abs_complete1("link3/target_abs") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level3_abs_complete2("link3/target_abs/") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level3_abs_complete3("link3/target_abs//") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level3_abs_partial("link3/target_abs/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level3_abs_partial_dotdot("link3/target_abs/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level3_rel_complete("link3/target_rel") => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; - nonlexical_level3_rel_partial("link3/target_rel/foo") => ExpectedResult::Err(libc::ENOENT); - nonlexical_level3_rel_partial_dotdot("link3/target_rel/../target/foo/bar/../baz") => ExpectedResult::Err(libc::ENOENT); - // Partial lookups due to hitting a non_directory. - partial_nondir_slash1("b/c/file/") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_slash2("b/c/file//") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_dot("b/c/file/.") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_dotdot1("b/c/file/..") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_dotdot2("b/c/file/../foo/bar") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_symlink_slash1("b-file/") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_symlink_slash2("b-file//") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_symlink_dot("b-file/.") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_symlink_dotdot1("b-file/..") => ExpectedResult::Err(libc::ENOTDIR); - partial_nondir_symlink_dotdot2("b-file/../foo/bar") => ExpectedResult::Err(libc::ENOTDIR); - partial_fifo_slash1("b/fifo/") => ExpectedResult::Err(libc::ENOTDIR); - partial_fifo_slash2("b/fifo//") => ExpectedResult::Err(libc::ENOTDIR); - partial_fifo_dot("b/fifo/.") => ExpectedResult::Err(libc::ENOTDIR); - partial_fifo_dotdot1("b/fifo/..") => ExpectedResult::Err(libc::ENOTDIR); - partial_fifo_dotdot2("b/fifo/../foo/bar") => ExpectedResult::Err(libc::ENOTDIR); - partial_sock_slash1("b/sock/") => ExpectedResult::Err(libc::ENOTDIR); - partial_sock_slash2("b/sock//") => ExpectedResult::Err(libc::ENOTDIR); - partial_sock_dot("b/sock/.") => ExpectedResult::Err(libc::ENOTDIR); - partial_sock_dotdot1("b/sock/..") => ExpectedResult::Err(libc::ENOTDIR); - partial_sock_dotdot2("b/sock/../foo/bar") => ExpectedResult::Err(libc::ENOTDIR); - // Dangling symlinks are treated as though they are non_existent. - dangling1_inroot_trailing("a-fake1") => ExpectedResult::Err(libc::ENOENT); - dangling1_inroot_partial("a-fake1/foo") => ExpectedResult::Err(libc::ENOENT); - dangling1_inroot_partial_dotdot("a-fake1/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - dangling1_sub_trailing("c/a-fake1") => ExpectedResult::Err(libc::ENOENT); - dangling1_sub_partial("c/a-fake1/foo") => ExpectedResult::Err(libc::ENOENT); - dangling1_sub_partial_dotdot("c/a-fake1/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - dangling2_inroot_trailing("a-fake2") => ExpectedResult::Err(libc::ENOENT); - dangling2_inroot_partial("a-fake2/foo") => ExpectedResult::Err(libc::ENOENT); - dangling2_inroot_partial_dotdot("a-fake2/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - dangling2_sub_trailing("c/a-fake2") => ExpectedResult::Err(libc::ENOENT); - dangling2_sub_partial("c/a-fake2/foo") => ExpectedResult::Err(libc::ENOENT); - dangling2_sub_partial_dotdot("c/a-fake2/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - dangling3_inroot_trailing("a-fake3") => ExpectedResult::Err(libc::ENOENT); - dangling3_inroot_partial("a-fake3/foo") => ExpectedResult::Err(libc::ENOENT); - dangling3_inroot_partial_dotdot("a-fake3/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - dangling3_sub_trailing("c/a-fake3") => ExpectedResult::Err(libc::ENOENT); - dangling3_sub_partial("c/a-fake3/foo") => ExpectedResult::Err(libc::ENOENT); - dangling3_sub_partial_dotdot("c/a-fake3/../bar/baz") => ExpectedResult::Err(libc::ENOENT); - // Tricky dangling symlinks. - dangling_tricky1_trailing("link3/deep_dangling1") => ExpectedResult::Err(libc::ENOENT); - dangling_tricky1_partial("link3/deep_dangling1/foo") => ExpectedResult::Err(libc::ENOENT); - dangling_tricky1_partial_dotdot("link3/deep_dangling1/..") => ExpectedResult::Err(libc::ENOENT); - dangling_tricky2_trailing("link3/deep_dangling2") => ExpectedResult::Err(libc::ENOENT); - dangling_tricky2_partial("link3/deep_dangling2/foo") => ExpectedResult::Err(libc::ENOENT); - dangling_tricky2_partial_dotdot("link3/deep_dangling2/..") => ExpectedResult::Err(libc::ENOENT); - // Really deep dangling links. - deep_dangling1("dangling/a") => ExpectedResult::Err(libc::ENOENT); - deep_dangling2("dangling/b/c") => ExpectedResult::Err(libc::ENOENT); - deep_dangling3("dangling/c") => ExpectedResult::Err(libc::ENOENT); - deep_dangling4("dangling/d/e") => ExpectedResult::Err(libc::ENOENT); - deep_dangling5("dangling/e") => ExpectedResult::Err(libc::ENOENT); - deep_dangling6("dangling/g") => ExpectedResult::Err(libc::ENOENT); - deep_dangling_fileasdir1("dangling-file/a") => ExpectedResult::Err(libc::ENOTDIR); - deep_dangling_fileasdir2("dangling-file/b/c") => ExpectedResult::Err(libc::ENOTDIR); - deep_dangling_fileasdir3("dangling-file/c") => ExpectedResult::Err(libc::ENOTDIR); - deep_dangling_fileasdir4("dangling-file/d/e") => ExpectedResult::Err(libc::ENOTDIR); - deep_dangling_fileasdir5("dangling-file/e") => ExpectedResult::Err(libc::ENOTDIR); - deep_dangling_fileasdir6("dangling-file/g") => ExpectedResult::Err(libc::ENOTDIR); - // Symlink loops. - loop1("loop/link") => ExpectedResult::Err(libc::ELOOP); - loop_basic1("loop/basic-loop1") => ExpectedResult::Err(libc::ELOOP); - loop_basic2("loop/basic-loop2") => ExpectedResult::Err(libc::ELOOP); - loop_basic3("loop/basic-loop3") => ExpectedResult::Err(libc::ELOOP); + let root_dir = tests_common::create_basic_tree()? => @reopen_tests:true { + complete_root1("/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; + complete_root2("/../../../../../..", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; + complete_root_link1("root-link1", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; + complete_root_link2("root-link2", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; + complete_root_link3("root-link3", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/", file_type: libc::S_IFDIR }; + complete_dir1("a", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/a", file_type: libc::S_IFDIR }; + complete_dir2("b/c/d/e/f", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/c/d/e/f", file_type: libc::S_IFDIR }; + complete_dir3("b///././c////.//d/./././///e////.//./f//././././", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/c/d/e/f", file_type: libc::S_IFDIR }; + complete_file("b/c/file", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/c/file", file_type: libc::S_IFREG }; + complete_file_link("b-file", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/c/file", file_type: libc::S_IFREG }; + complete_fifo("b/fifo", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/fifo", file_type: libc::S_IFIFO }; + complete_sock("b/sock", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/b/sock", file_type: libc::S_IFSOCK }; + // Partial lookups. + partial_dir_basic("a/b/c/d/e/f/g/h", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + partial_dir_dotdot("a/foo/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + // Complete lookups of non_lexical symlinks. + nonlexical_basic_complete("target", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_basic_complete1("target/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_basic_complete2("target//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_basic_partial("target/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_basic_partial_dotdot("target/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level1_abs_complete1("link1/target_abs", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_abs_complete2("link1/target_abs/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_abs_complete3("link1/target_abs//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_abs_partial("link1/target_abs/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level1_abs_partial_dotdot("link1/target_abs/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level1_rel_complete1("link1/target_rel", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_rel_complete2("link1/target_rel/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_rel_complete3("link1/target_rel//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level1_rel_partial("link1/target_rel/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level1_rel_partial_dotdot("link1/target_rel/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_abs_complete1("link2/link1_abs/target_abs", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_abs_complete2("link2/link1_abs/target_abs/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_abs_complete3("link2/link1_abs/target_abs//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_abs_partial("link2/link1_abs/target_abs/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_abs_partial_dotdot("link2/link1_abs/target_abs/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_rel_complete1("link2/link1_abs/target_rel", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_rel_complete2("link2/link1_abs/target_rel/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_rel_complete3("link2/link1_abs/target_rel//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_rel_partial("link2/link1_abs/target_rel/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_rel_partial_dotdot("link2/link1_abs/target_rel/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_open_complete1("link2/link1_abs/../target", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_open_complete2("link2/link1_abs/../target/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_open_complete3("link2/link1_abs/../target//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_abs_open_partial("link2/link1_abs/../target/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_abs_open_partial_dotdot("link2/link1_abs/../target/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_abs_complete1("link2/link1_rel/target_abs", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_abs_complete2("link2/link1_rel/target_abs/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_abs_complete3("link2/link1_rel/target_abs//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_abs_partial("link2/link1_rel/target_abs/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_abs_partial_dotdot("link2/link1_rel/target_abs/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_rel_complete1("link2/link1_rel/target_rel", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_rel_complete2("link2/link1_rel/target_rel/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_rel_complete3("link2/link1_rel/target_rel//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_rel_partial("link2/link1_rel/target_rel/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_rel_partial_dotdot("link2/link1_rel/target_rel/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_open_complete1("link2/link1_rel/../target", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_open_complete2("link2/link1_rel/../target/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_open_complete3("link2/link1_rel/../target//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level2_rel_open_partial("link2/link1_rel/../target/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level2_rel_open_partial_dotdot("link2/link1_rel/../target/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level3_abs_complete1("link3/target_abs", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level3_abs_complete2("link3/target_abs/", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level3_abs_complete3("link3/target_abs//", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level3_abs_partial("link3/target_abs/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level3_abs_partial_dotdot("link3/target_abs/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level3_rel_complete("link3/target_rel", ResolverFlags::empty()) => ExpectedResult::Ok { real_path: "/target", file_type: libc::S_IFDIR }; + nonlexical_level3_rel_partial("link3/target_rel/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + nonlexical_level3_rel_partial_dotdot("link3/target_rel/../target/foo/bar/../baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + // Partial lookups due to hitting a non_directory. + partial_nondir_slash1("b/c/file/", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_slash2("b/c/file//", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_dot("b/c/file/.", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_dotdot1("b/c/file/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_dotdot2("b/c/file/../foo/bar", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_symlink_slash1("b-file/", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_symlink_slash2("b-file//", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_symlink_dot("b-file/.", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_symlink_dotdot1("b-file/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_nondir_symlink_dotdot2("b-file/../foo/bar", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_fifo_slash1("b/fifo/", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_fifo_slash2("b/fifo//", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_fifo_dot("b/fifo/.", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_fifo_dotdot1("b/fifo/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_fifo_dotdot2("b/fifo/../foo/bar", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_sock_slash1("b/sock/", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_sock_slash2("b/sock//", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_sock_dot("b/sock/.", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_sock_dotdot1("b/sock/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + partial_sock_dotdot2("b/sock/../foo/bar", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + // Dangling symlinks are treated as though they are non_existent. + dangling1_inroot_trailing("a-fake1", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling1_inroot_partial("a-fake1/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling1_inroot_partial_dotdot("a-fake1/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling1_sub_trailing("c/a-fake1", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling1_sub_partial("c/a-fake1/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling1_sub_partial_dotdot("c/a-fake1/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_inroot_trailing("a-fake2", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_inroot_partial("a-fake2/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_inroot_partial_dotdot("a-fake2/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_sub_trailing("c/a-fake2", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_sub_partial("c/a-fake2/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling2_sub_partial_dotdot("c/a-fake2/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_inroot_trailing("a-fake3", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_inroot_partial("a-fake3/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_inroot_partial_dotdot("a-fake3/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_sub_trailing("c/a-fake3", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_sub_partial("c/a-fake3/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling3_sub_partial_dotdot("c/a-fake3/../bar/baz", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + // Tricky dangling symlinks. + dangling_tricky1_trailing("link3/deep_dangling1", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling_tricky1_partial("link3/deep_dangling1/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling_tricky1_partial_dotdot("link3/deep_dangling1/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling_tricky2_trailing("link3/deep_dangling2", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling_tricky2_partial("link3/deep_dangling2/foo", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + dangling_tricky2_partial_dotdot("link3/deep_dangling2/..", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + // Really deep dangling links. + deep_dangling1("dangling/a", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling2("dangling/b/c", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling3("dangling/c", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling4("dangling/d/e", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling5("dangling/e", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling6("dangling/g", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOENT); + deep_dangling_fileasdir1("dangling-file/a", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + deep_dangling_fileasdir2("dangling-file/b/c", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + deep_dangling_fileasdir3("dangling-file/c", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + deep_dangling_fileasdir4("dangling-file/d/e", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + deep_dangling_fileasdir5("dangling-file/e", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + deep_dangling_fileasdir6("dangling-file/g", ResolverFlags::empty()) => ExpectedResult::Err(libc::ENOTDIR); + // Symlink loops. + loop1("loop/link", ResolverFlags::empty()) => ExpectedResult::Err(libc::ELOOP); + loop_basic1("loop/basic-loop1", ResolverFlags::empty()) => ExpectedResult::Err(libc::ELOOP); + loop_basic2("loop/basic-loop2", ResolverFlags::empty()) => ExpectedResult::Err(libc::ELOOP); + loop_basic3("loop/basic-loop3", ResolverFlags::empty()) => ExpectedResult::Err(libc::ELOOP); + // NO_FOLLOW. + symlink_nofollow("link3/target_abs", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: "link3/target_abs", file_type: libc::S_IFLNK }; + symlink_component_nofollow1("e/f", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: "b/c/d/e/f", file_type: libc::S_IFDIR }; + symlink_component_nofollow2("link2/link1_abs/target_rel", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: "link1/target_rel", file_type: libc::S_IFLNK }; + loop_nofollow("loop/link", ResolverFlags::NO_FOLLOW_TRAILING) => ExpectedResult::Ok { real_path: "loop/link", file_type: libc::S_IFLNK }; + // RESOLVE_NO_SYMLINKS. + dir_nosym("b/c/d/e", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Ok { real_path: "b/c/d/e", file_type: libc::S_IFDIR }; + symlink_nosym("link3/target_abs", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + symlink_component_nosym1("e/f", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + symlink_component_nosym2("link2/link1_abs/target_rel", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + loop_nosym("loop/link", ResolverFlags::NO_SYMLINKS) => ExpectedResult::Err(libc::ELOOP); + } } mod utils { @@ -343,6 +397,7 @@ mod utils { root_dir: P, unsafe_path: Q, expected: &ExpectedResult, + reopen_tests: bool, ) -> Result<(), Error> where P: AsRef, @@ -394,22 +449,24 @@ mod utils { let real_file_type = meta.st_mode() & libc::S_IFMT; assert_eq!(real_file_type, expected_file_type, "file type mismatch",); - match real_file_type { - libc::S_IFDIR => { - check_reopen(&handle, OpenFlags::O_RDONLY, None)?; - check_reopen(&handle, OpenFlags::O_DIRECTORY, None)?; - } - libc::S_IFREG => { - check_reopen(&handle, OpenFlags::O_RDWR, None)?; - check_reopen(&handle, OpenFlags::O_DIRECTORY, Some(libc::ENOTDIR))?; - } - _ => { - check_reopen(&handle, OpenFlags::O_PATH, None)?; - check_reopen( - &handle, - OpenFlags::O_PATH | OpenFlags::O_DIRECTORY, - Some(libc::ENOTDIR), - )?; + if reopen_tests { + match real_file_type { + libc::S_IFDIR => { + check_reopen(&handle, OpenFlags::O_RDONLY, None)?; + check_reopen(&handle, OpenFlags::O_DIRECTORY, None)?; + } + libc::S_IFREG => { + check_reopen(&handle, OpenFlags::O_RDWR, None)?; + check_reopen(&handle, OpenFlags::O_DIRECTORY, Some(libc::ENOTDIR))?; + } + _ => { + check_reopen(&handle, OpenFlags::O_PATH, None)?; + check_reopen( + &handle, + OpenFlags::O_PATH | OpenFlags::O_DIRECTORY, + Some(libc::ENOTDIR), + )?; + } } } @@ -421,6 +478,7 @@ mod utils { root: &File, unsafe_path: P, expected: &ExpectedResult, + reopen_tests: bool, ) -> Result<(), Error> where P: AsRef, @@ -436,6 +494,7 @@ mod utils { root.as_unsafe_path()?, unsafe_path, expected, + reopen_tests, ) } @@ -443,6 +502,7 @@ mod utils { root: &Root, unsafe_path: P, expected: &ExpectedResult, + reopen_tests: bool, ) -> Result<(), Error> where P: AsRef, @@ -453,6 +513,7 @@ mod utils { root.as_file().as_unsafe_path()?, unsafe_path, expected, + reopen_tests, ) } }