use crate::{
error::ErrorKind,
flags::ResolverFlags,
resolvers::{PartialLookup, ResolverBackend},
syscalls, Root,
};
use anyhow::Error;
macro_rules! resolve_tests {
([$with_root_fn:ident] $(#[cfg($ignore_meta:meta)])* fn $test_name:ident (mut $root_var:ident : Root) $body:block) => {
paste::paste! {
#[test]
$(#[cfg_attr(not($ignore_meta), ignore)])*
fn [<root_ $test_name _default>]() -> Result<(), Error> {
utils::$with_root_fn(|mut $root_var: Root| {
assert_eq!(
$root_var.resolver_backend(),
ResolverBackend::default(),
"ResolverBackend not the default despite not being configured"
);
{ $body }
$root_var.set_resolver_flags($root_var.resolver_flags());
Ok(())
})
}
#[test]
$(#[cfg_attr(not($ignore_meta), ignore)])*
fn [<root_ $test_name _openat2>]() -> Result<(), Error> {
utils::$with_root_fn(|mut $root_var: Root| {
$root_var.set_resolver_backend(ResolverBackend::KernelOpenat2);
assert_eq!(
$root_var.resolver_backend(),
ResolverBackend::KernelOpenat2,
"incorrect ResolverBackend despite using set_resolver_backend"
);
if !$root_var.resolver_backend().supported() {
return Ok(());
}
{ $body }
Ok(())
})
}
#[test]
$(#[cfg_attr(not($ignore_meta), ignore)])*
fn [<root_ $test_name _opath>]() -> Result<(), Error> {
if syscalls::openat2::openat2_is_not_supported() {
return Ok(());
}
utils::$with_root_fn(|mut $root_var: Root| {
$root_var.set_resolver_backend(ResolverBackend::EmulatedOpath);
assert_eq!(
$root_var.resolver_backend(),
ResolverBackend::EmulatedOpath,
"incorrect ResolverBackend despite using set_resolver_backend"
);
assert!(
$root_var.resolver_backend().supported(),
"emulated opath is always supported",
);
{ $body }
Ok(())
})
}
}
};
([$with_root_fn:ident] $(#[cfg($ignore_meta:meta)])* @impl $test_name:ident $op_name:ident ($path:expr, $rflags:expr, $no_follow_trailing:expr) => $expected:expr) => {
paste::paste! {
resolve_tests! {
[$with_root_fn]
$(#[cfg($ignore_meta)])*
fn [<$op_name _ $test_name>](mut root: Root) {
root.set_resolver_flags($rflags);
let expected = $expected;
utils::[<check_root_ $op_name>](
&root,
$path,
$no_follow_trailing,
expected,
)?;
}
}
}
};
([$with_root_fn:ident] $(#[cfg($ignore_meta:meta)])* @impl $test_name:ident $op_name:ident ($path:expr, rflags = $($rflag:ident)|+) => $expected:expr ) => {
resolve_tests! {
[$with_root_fn]
$(#[cfg($ignore_meta)])*
@impl $test_name $op_name($path, $(ResolverFlags::$rflag)|*, false) => $expected
}
};
([$with_root_fn:ident] $(#[cfg($ignore_meta:meta)])* @impl $test_name:ident $op_name:ident ($path:expr, no_follow_trailing = $no_follow_trailing:expr) => $expected:expr ) => {
resolve_tests! {
[$with_root_fn]
$(#[cfg($ignore_meta)])*
@impl $test_name $op_name($path, ResolverFlags::empty(), $no_follow_trailing) => $expected
}
};
([$with_root_fn:ident] $(#[cfg($ignore_meta:meta)])* @impl $test_name:ident $op_name:ident ($path:expr) => $expected:expr ) => {
resolve_tests! {
[$with_root_fn]
$(#[cfg($ignore_meta)])*
@impl $test_name $op_name($path, ResolverFlags::empty(), false) => $expected
}
};
($([$with_root_fn:ident] { $($(#[cfg($ignore_meta:meta)])* $test_name:ident : $op_name:ident ($($args:tt)*) => $expected:expr);* $(;)? });* $(;)?) => {
$( $(
resolve_tests! {
[$with_root_fn]
$(#[cfg($ignore_meta)])*
@impl $test_name $op_name ($($args)*) => $expected
}
)* )*
}
}
resolve_tests! {
[with_basic_tree] {
complete_root1: resolve_partial("/") => Ok(PartialLookup::Complete(("/", libc::S_IFDIR)));
complete_root2: resolve_partial("/../../../../../..") => Ok(PartialLookup::Complete(("/", libc::S_IFDIR)));
complete_root_link1: resolve_partial("root-link1") => Ok(PartialLookup::Complete(("/", libc::S_IFDIR)));
complete_root_link2: resolve_partial("root-link2") => Ok(PartialLookup::Complete(("/", libc::S_IFDIR)));
complete_root_link3: resolve_partial("root-link3") => Ok(PartialLookup::Complete(("/", libc::S_IFDIR)));
complete_dir1: resolve_partial("a") => Ok(PartialLookup::Complete(("/a", libc::S_IFDIR)));
complete_dir2: resolve_partial("b/c/d/e/f") => Ok(PartialLookup::Complete(("/b/c/d/e/f", libc::S_IFDIR)));
complete_dir3: resolve_partial("b///././c////.//d/./././///e////.//./f//././././") => Ok(PartialLookup::Complete(("/b/c/d/e/f", libc::S_IFDIR)));
complete_file: resolve_partial("b/c/file") => Ok(PartialLookup::Complete(("/b/c/file", libc::S_IFREG)));
complete_file_link: resolve_partial("b-file") => Ok(PartialLookup::Complete(("/b/c/file", libc::S_IFREG)));
complete_fifo: resolve_partial("b/fifo") => Ok(PartialLookup::Complete(("/b/fifo", libc::S_IFIFO)));
complete_sock: resolve_partial("b/sock") => Ok(PartialLookup::Complete(("/b/sock", libc::S_IFSOCK)));
partial_dir_basic: resolve_partial("a/b/c/d/e/f/g/h") => Ok(PartialLookup::Partial {
handle: ("a", libc::S_IFDIR),
remaining: "b/c/d/e/f/g/h".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
partial_dir_dotdot: resolve_partial("a/foo/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("a", libc::S_IFDIR),
remaining: "foo/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_basic_complete: resolve_partial("target") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_basic_complete1: resolve_partial("target/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_basic_complete2: resolve_partial("target//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_basic_partial: resolve_partial("target/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_basic_partial_dotdot: resolve_partial("target/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level1_abs_complete1: resolve_partial("link1/target_abs") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_abs_complete2: resolve_partial("link1/target_abs/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_abs_complete3: resolve_partial("link1/target_abs//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_abs_partial: resolve_partial("link1/target_abs/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level1_abs_partial_dotdot: resolve_partial("link1/target_abs/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level1_rel_complete1: resolve_partial("link1/target_rel") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_rel_complete2: resolve_partial("link1/target_rel/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_rel_complete3: resolve_partial("link1/target_rel//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level1_rel_partial: resolve_partial("link1/target_rel/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level1_rel_partial_dotdot: resolve_partial("link1/target_rel/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_abs_complete1: resolve_partial("link2/link1_abs/target_abs") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_abs_complete2: resolve_partial("link2/link1_abs/target_abs/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_abs_complete3: resolve_partial("link2/link1_abs/target_abs//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_abs_partial: resolve_partial("link2/link1_abs/target_abs/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_abs_partial_dotdot: resolve_partial("link2/link1_abs/target_abs/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_rel_complete1: resolve_partial("link2/link1_abs/target_rel") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_rel_complete2: resolve_partial("link2/link1_abs/target_rel/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_rel_complete3: resolve_partial("link2/link1_abs/target_rel//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_rel_partial: resolve_partial("link2/link1_abs/target_rel/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_rel_partial_dotdot: resolve_partial("link2/link1_abs/target_rel/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_open_complete1: resolve_partial("link2/link1_abs/../target") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_open_complete2: resolve_partial("link2/link1_abs/../target/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_open_complete3: resolve_partial("link2/link1_abs/../target//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_abs_open_partial: resolve_partial("link2/link1_abs/../target/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_abs_open_partial_dotdot: resolve_partial("link2/link1_abs/../target/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_abs_complete1: resolve_partial("link2/link1_rel/target_abs") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_abs_complete2: resolve_partial("link2/link1_rel/target_abs/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_abs_complete3: resolve_partial("link2/link1_rel/target_abs//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_abs_partial: resolve_partial("link2/link1_rel/target_abs/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_abs_partial_dotdot: resolve_partial("link2/link1_rel/target_abs/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_rel_complete1: resolve_partial("link2/link1_rel/target_rel") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_rel_complete2: resolve_partial("link2/link1_rel/target_rel/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_rel_complete3: resolve_partial("link2/link1_rel/target_rel//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_rel_partial: resolve_partial("link2/link1_rel/target_rel/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_rel_partial_dotdot: resolve_partial("link2/link1_rel/target_rel/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_open_complete1: resolve_partial("link2/link1_rel/../target") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_open_complete2: resolve_partial("link2/link1_rel/../target/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_open_complete3: resolve_partial("link2/link1_rel/../target//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level2_rel_open_partial: resolve_partial("link2/link1_rel/../target/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level2_rel_open_partial_dotdot: resolve_partial("link2/link1_rel/../target/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level3_abs_complete1: resolve_partial("link3/target_abs") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level3_abs_complete2: resolve_partial("link3/target_abs/") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level3_abs_complete3: resolve_partial("link3/target_abs//") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level3_abs_partial: resolve_partial("link3/target_abs/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level3_abs_partial_dotdot: resolve_partial("link3/target_abs/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level3_rel_complete: resolve_partial("link3/target_rel") => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
nonlexical_level3_rel_partial: resolve_partial("link3/target_rel/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
nonlexical_level3_rel_partial_dotdot: resolve_partial("link3/target_rel/../target/foo/bar/../baz") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo/bar/../baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
partial_nondir_slash1: resolve_partial("b/c/file/") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_slash2: resolve_partial("b/c/file//") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "/".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_dot: resolve_partial("b/c/file/.") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: ".".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_dotdot1: resolve_partial("b/c/file/..") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_dotdot2: resolve_partial("b/c/file/../foo/bar") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "../foo/bar".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_symlink_slash1: resolve_partial("b-file/") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_symlink_slash2: resolve_partial("b-file//") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "/".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_symlink_dot: resolve_partial("b-file/.") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: ".".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_symlink_dotdot1: resolve_partial("b-file/..") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_nondir_symlink_dotdot2: resolve_partial("b-file/../foo/bar") => Ok(PartialLookup::Partial {
handle: ("b/c/file", libc::S_IFREG),
remaining: "../foo/bar".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_fifo_slash1: resolve_partial("b/fifo/") => Ok(PartialLookup::Partial {
handle: ("b/fifo", libc::S_IFIFO),
remaining: "".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_fifo_slash2: resolve_partial("b/fifo//") => Ok(PartialLookup::Partial {
handle: ("b/fifo", libc::S_IFIFO),
remaining: "/".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_fifo_dot: resolve_partial("b/fifo/.") => Ok(PartialLookup::Partial {
handle: ("b/fifo", libc::S_IFIFO),
remaining: ".".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_fifo_dotdot1: resolve_partial("b/fifo/..") => Ok(PartialLookup::Partial {
handle: ("b/fifo", libc::S_IFIFO),
remaining: "..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_fifo_dotdot2: resolve_partial("b/fifo/../foo/bar") => Ok(PartialLookup::Partial {
handle: ("b/fifo", libc::S_IFIFO),
remaining: "../foo/bar".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_sock_slash1: resolve_partial("b/sock/") => Ok(PartialLookup::Partial {
handle: ("b/sock", libc::S_IFSOCK),
remaining: "".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_sock_slash2: resolve_partial("b/sock//") => Ok(PartialLookup::Partial {
handle: ("b/sock", libc::S_IFSOCK),
remaining: "/".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_sock_dot: resolve_partial("b/sock/.") => Ok(PartialLookup::Partial {
handle: ("b/sock", libc::S_IFSOCK),
remaining: ".".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_sock_dotdot1: resolve_partial("b/sock/..") => Ok(PartialLookup::Partial {
handle: ("b/sock", libc::S_IFSOCK),
remaining: "..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_sock_dotdot2: resolve_partial("b/sock/../foo/bar") => Ok(PartialLookup::Partial {
handle: ("b/sock", libc::S_IFSOCK),
remaining: "../foo/bar".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
partial_symlink_nofollow_slash1: resolve_partial("link3/target_abs/", no_follow_trailing = true) => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
partial_symlink_nofollow_slash2: resolve_partial("link3/target_abs//", no_follow_trailing = true) => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
partial_symlink_nofollow_dot: resolve_partial("link3/target_abs/.", no_follow_trailing = true) => Ok(PartialLookup::Complete(("/target", libc::S_IFDIR)));
dangling1_inroot_trailing: resolve_partial("a-fake1") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake1".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling1_inroot_partial: resolve_partial("a-fake1/foo") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake1/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling1_inroot_partial_dotdot: resolve_partial("a-fake1/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake1/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling1_sub_trailing: resolve_partial("c/a-fake1") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake1".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling1_sub_partial: resolve_partial("c/a-fake1/foo") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake1/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling1_sub_partial_dotdot: resolve_partial("c/a-fake1/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake1/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_inroot_trailing: resolve_partial("a-fake2") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake2".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_inroot_partial: resolve_partial("a-fake2/foo") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake2/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_inroot_partial_dotdot: resolve_partial("a-fake2/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake2/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_sub_trailing: resolve_partial("c/a-fake2") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake2".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_sub_partial: resolve_partial("c/a-fake2/foo") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake2/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling2_sub_partial_dotdot: resolve_partial("c/a-fake2/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake2/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_inroot_trailing: resolve_partial("a-fake3") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake3".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_inroot_partial: resolve_partial("a-fake3/foo") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake3/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_inroot_partial_dotdot: resolve_partial("a-fake3/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "a-fake3/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_sub_trailing: resolve_partial("c/a-fake3") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake3".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_sub_partial: resolve_partial("c/a-fake3/foo") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake3/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling3_sub_partial_dotdot: resolve_partial("c/a-fake3/../bar/baz") => Ok(PartialLookup::Partial {
handle: ("c", libc::S_IFDIR),
remaining: "a-fake3/../bar/baz".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky1_trailing: resolve_partial("link3/deep_dangling1") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling1".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky1_partial: resolve_partial("link3/deep_dangling1/foo") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling1/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky1_partial_dotdot: resolve_partial("link3/deep_dangling1/..") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling1/..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky2_trailing: resolve_partial("link3/deep_dangling2") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling2".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky2_partial: resolve_partial("link3/deep_dangling2/foo") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling2/foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
dangling_tricky2_partial_dotdot: resolve_partial("link3/deep_dangling2/..") => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "deep_dangling2/..".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling1: resolve_partial("dangling/a") => Ok(PartialLookup::Partial {
handle: ("dangling", libc::S_IFDIR),
remaining: "a".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling2: resolve_partial("dangling/b/c") => Ok(PartialLookup::Partial {
handle: ("dangling/b", libc::S_IFDIR),
remaining: "c".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling3: resolve_partial("dangling/c") => Ok(PartialLookup::Partial {
handle: ("dangling", libc::S_IFDIR),
remaining: "c".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling4: resolve_partial("dangling/d/e") => Ok(PartialLookup::Partial {
handle: ("dangling/d", libc::S_IFDIR),
remaining: "e".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling5: resolve_partial("dangling/e") => Ok(PartialLookup::Partial {
handle: ("dangling", libc::S_IFDIR),
remaining: "e".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling6: resolve_partial("dangling/g") => Ok(PartialLookup::Partial {
handle: ("dangling", libc::S_IFDIR),
remaining: "g".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
deep_dangling_fileasdir1: resolve_partial("dangling-file/a") => Ok(PartialLookup::Partial {
handle: ("dangling-file", libc::S_IFDIR),
remaining: "a".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
deep_dangling_fileasdir2: resolve_partial("dangling-file/b/c") => Ok(PartialLookup::Partial {
handle: ("dangling-file/b", libc::S_IFDIR),
remaining: "c".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
deep_dangling_fileasdir3: resolve_partial("dangling-file/c") => Ok(PartialLookup::Partial {
handle: ("dangling-file", libc::S_IFDIR),
remaining: "c".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
deep_dangling_fileasdir4: resolve_partial("dangling-file/d/e") => Ok(PartialLookup::Partial {
handle: ("dangling-file/d", libc::S_IFDIR),
remaining: "e".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
deep_dangling_fileasdir5: resolve_partial("dangling-file/e") => Ok(PartialLookup::Partial {
handle: ("dangling-file", libc::S_IFDIR),
remaining: "e".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
deep_dangling_fileasdir6: resolve_partial("dangling-file/g") => Ok(PartialLookup::Partial {
handle: ("dangling-file", libc::S_IFDIR),
remaining: "g".into(),
last_error: ErrorKind::OsError(Some(libc::ENOTDIR)),
});
loop1: resolve_partial("loop/link") => Ok(PartialLookup::Partial {
handle: ("loop", libc::S_IFDIR),
remaining: "link".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
loop_basic1: resolve_partial("loop/basic-loop1") => Ok(PartialLookup::Partial {
handle: ("loop", libc::S_IFDIR),
remaining: "basic-loop1".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
loop_basic2: resolve_partial("loop/basic-loop2") => Ok(PartialLookup::Partial {
handle: ("loop", libc::S_IFDIR),
remaining: "basic-loop2".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
loop_basic3: resolve_partial("loop/basic-loop3") => Ok(PartialLookup::Partial {
handle: ("loop", libc::S_IFDIR),
remaining: "basic-loop3".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
symlink_nofollow: resolve_partial("link3/target_abs", no_follow_trailing = true) => Ok(PartialLookup::Complete(("link3/target_abs", libc::S_IFLNK)));
symlink_component_nofollow1: resolve_partial("e/f", no_follow_trailing = true) => Ok(PartialLookup::Complete(("b/c/d/e/f", libc::S_IFDIR)));
symlink_component_nofollow2: resolve_partial("link2/link1_abs/target_rel", no_follow_trailing = true) => Ok(PartialLookup::Complete(("link1/target_rel", libc::S_IFLNK)));
loop_nofollow: resolve_partial("loop/link", no_follow_trailing = true) => Ok(PartialLookup::Complete(("loop/link", libc::S_IFLNK)));
dir_nosym: resolve_partial("b/c/d/e", rflags = NO_SYMLINKS) => Ok(PartialLookup::Complete(("b/c/d/e", libc::S_IFDIR)));
symlink_nosym: resolve_partial("link3/target_abs", rflags = NO_SYMLINKS) => Ok(PartialLookup::Partial {
handle: ("link3", libc::S_IFDIR),
remaining: "target_abs".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
symlink_component_nosym1: resolve_partial("e/f", rflags = NO_SYMLINKS) => Ok(PartialLookup::Partial {
handle: ("", libc::S_IFDIR),
remaining: "e/f".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
symlink_component_nosym2: resolve_partial("link2/link1_abs/target_rel", rflags = NO_SYMLINKS) => Ok(PartialLookup::Partial {
handle: ("link2", libc::S_IFDIR),
remaining: "link1_abs/target_rel".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
loop_nosym: resolve_partial("loop/link", rflags = NO_SYMLINKS) => Ok(PartialLookup::Partial {
handle: ("loop", libc::S_IFDIR),
remaining: "link".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
noop_dotdot_link1: resolve_partial("escape-link1/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
noop_dotdot_link2: resolve_partial("escape-link2/foo") => Ok(PartialLookup::Partial {
handle: ("target", libc::S_IFDIR),
remaining: "foo".into(),
last_error: ErrorKind::OsError(Some(libc::ENOENT)),
});
}
}
resolve_tests! {
[with_nosymfollow_root] {
#[cfg(feature = "_test_as_root")]
symlink_regular_symfollow: resolve_partial("nosymfollow/goodlink") => Ok(PartialLookup::Complete((".", libc::S_IFDIR)));
#[cfg(feature = "_test_as_root")]
symlink_itself_nosymfollow: resolve_partial("nosymfollow/badlink") => Ok(PartialLookup::Partial{
handle: ("nosymfollow", libc::S_IFDIR),
remaining: "badlink".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
#[cfg(feature = "_test_as_root")]
symlink_inside_nosymfollow_dir: resolve_partial("nosymfollow/nosymdir/dir/badlink") => Ok(PartialLookup::Partial{
handle: ("nosymfollow/nosymdir/dir", libc::S_IFDIR),
remaining: "badlink".into(),
last_error: ErrorKind::OsError(Some(libc::ELOOP)),
});
#[cfg(feature = "_test_as_root")]
symlink_itself_nested_clear_symfollow: resolve_partial("nosymfollow/nosymdir/dir/goodlink") => Ok(PartialLookup::Complete((".", libc::S_IFDIR)));
#[cfg(feature = "_test_as_root")]
symlink_inside_nested_clear_symfollow: resolve_partial("nosymfollow/nosymdir/dir/foo/yessymdir/bar/goodlink") => Ok(PartialLookup::Complete((".", libc::S_IFDIR)));
}
}
mod utils {
use crate::{
error::ErrorKind,
flags::OpenFlags,
resolvers::PartialLookup,
tests::{
common::{self as tests_common, LookupResult},
traits::RootImpl,
},
utils::FdExt,
Root,
};
use std::{os::unix::fs::MetadataExt, path::Path};
use anyhow::{Context, Error};
use pretty_assertions::assert_eq;
pub(super) fn with_basic_tree<F>(func: F) -> Result<(), Error>
where
F: FnOnce(Root) -> Result<(), Error>,
{
let root_dir = tests_common::create_basic_tree()?;
let root = Root::open(&root_dir)?;
let res = func(root);
let _root_dir = root_dir; res
}
pub(super) fn with_nosymfollow_root<F>(func: F) -> Result<(), Error>
where
F: FnOnce(Root) -> Result<(), Error>,
{
tests_common::in_mnt_ns(|| {
let root_dir = tests_common::create_basic_tree()?;
let root = Root::open(&root_dir)?;
tests_common::mask_nosymfollow(root_dir.path())?;
let res = func(root);
let _root_dir = root_dir; res
})
}
pub(super) fn check_root_resolve_partial(
root: &Root,
unsafe_path: impl AsRef<Path>,
no_follow_trailing: bool,
expected: Result<PartialLookup<LookupResult, ErrorKind>, ErrorKind>,
) -> Result<(), Error> {
let root_dir = root.as_unsafe_path_unchecked()?;
let unsafe_path = unsafe_path.as_ref();
let result = root
.resolver()
.resolve_partial(root, unsafe_path, no_follow_trailing)
.map(|lookup_result| {
let (path, file_type) = {
let file = lookup_result.as_inner_handle();
(
file.as_unsafe_path_unchecked()
.expect("should be able to get real path of handle"),
file.metadata()
.expect("should be able to fstat handle")
.mode()
& libc::S_IFMT,
)
};
match lookup_result {
PartialLookup::Complete(handle) => {
(handle, PartialLookup::Complete((path, file_type)))
}
PartialLookup::Partial {
handle,
remaining,
last_error,
} => (
handle,
PartialLookup::Partial {
handle: (path, file_type),
remaining,
last_error: last_error.kind(),
},
),
}
});
let ((handle, lookup_result), expected_lookup_result) = match (result, expected) {
(Ok((handle, lookup_result)), Ok(expected_lookup_result)) => {
let (path, file_type) = {
let (path, file_type) = expected_lookup_result.as_inner_handle();
(root_dir.join(path.trim_start_matches('/')), *file_type)
};
let expected_lookup_result = match expected_lookup_result {
PartialLookup::Complete(_) => PartialLookup::Complete((path, file_type)),
PartialLookup::Partial {
handle: _,
remaining,
last_error,
} => PartialLookup::Partial {
handle: (path, file_type),
remaining,
last_error,
},
};
((handle, lookup_result), expected_lookup_result)
}
(result, expected) => {
tests_common::check_err(&result, &expected)
.with_context(|| format!("resolve partial {unsafe_path:?}"))?;
assert!(
result.is_err(),
"we should never see an Ok(handle) after check_err if we expected {expected:?}"
);
return Ok(());
}
};
assert_eq!(
lookup_result, expected_lookup_result,
"partial_lookup({unsafe_path:?}, {no_follow_trailing}) result mismatch",
);
let (_, real_file_type) = lookup_result.as_inner_handle();
match *real_file_type {
libc::S_IFDIR => {
tests_common::check_reopen(&handle, OpenFlags::O_RDONLY, None)?;
tests_common::check_reopen(&handle, OpenFlags::O_DIRECTORY, None)?;
}
libc::S_IFREG => {
tests_common::check_reopen(&handle, OpenFlags::O_RDWR, None)?;
tests_common::check_reopen(&handle, OpenFlags::O_DIRECTORY, Some(libc::ENOTDIR))?;
}
libc::S_IFLNK => {
assert!(
no_follow_trailing,
"we must only get a symlink handle if no_follow_trailing is set"
);
tests_common::check_reopen(&handle, OpenFlags::O_RDONLY, Some(libc::ELOOP))
.context("reopen(O_RDONLY) of a symlink handle should fail with ELOOP")?;
tests_common::check_reopen(&handle, OpenFlags::O_PATH, Some(libc::ELOOP))
.context("reopen(O_PATH) of a symlink handle should fail with ELOOP")?;
tests_common::check_reopen(
&handle,
OpenFlags::O_PATH | OpenFlags::O_NOFOLLOW,
Some(libc::ELOOP),
)
.context("reopen(O_PATH|O_NOFOLLOW) of a symlink handle should fail with ELOOP")?;
}
_ => {
tests_common::check_reopen(&handle, OpenFlags::O_PATH, None)?;
tests_common::check_reopen(
&handle,
OpenFlags::O_PATH | OpenFlags::O_DIRECTORY,
Some(libc::ENOTDIR),
)?;
}
}
Ok(())
}
}