Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions cap-primitives/src/fs/manually/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,23 +222,12 @@ impl<'start> Context<'start> {
dir_options()
};

// If the last path component ended in a slash, re-add the slash,
// as Rust's `Path` will have removed it, and we need it to get the
// same behavior from the OS.
let use_path: Cow<OsStr> = if self.components.is_empty() && self.trailing_slash {
let mut p = one.to_os_string();
p.push("/");
Cow::Owned(p)
} else {
Cow::Borrowed(one)
};

let dir_required = self.dir_required || use_options.dir_required;

#[allow(clippy::redundant_clone)]
match open_unchecked(
&self.base,
use_path.as_ref(),
one.as_ref(),
use_options
.clone()
.follow(FollowSymlinks::No)
Expand Down
97 changes: 97 additions & 0 deletions tests/fs_additional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#[macro_use]
mod sys_common;

use cap_std::ambient_authority;
use cap_std::fs::{Dir, DirBuilder, OpenOptions};
use cap_std::time::SystemClock;
use std::io::{self, Read, Write};
Expand Down Expand Up @@ -1400,3 +1401,99 @@ fn statat_slash() {
"a path led outside of the filesyste"
);
}

/// Test interactions between symlinks and trailing slashes.
#[test]
fn trailing_slash_symlink() {
let tmpdir = tmpdir();

check!(tmpdir.create_dir("sandbox"));
check!(symlink_dir("../outside", &tmpdir, "sandbox/hidden"));
check!(symlink_dir("hidden/", &tmpdir, "sandbox/indirect"));

let sandbox = check!(tmpdir.open_dir("sandbox"));

for path in ["hidden", "hidden/", "indirect", "indirect/"] {
error!(
sandbox.open_dir(path),
"a path led outside of the filesystem"
);
error!(
sandbox.read_dir(path),
"a path led outside of the filesystem"
);
error!(
sandbox.canonicalize(path),
"a path led outside of the filesystem"
);
}
}

/// Similar to `trailing_slash_symlink`, but populates the test directory
/// outside the sandbox, so it can cover more cases.
#[test]
fn trailing_slash_symlink_more() {
let tmpdir = tempfile::tempdir().unwrap();

check!(std::fs::create_dir(tmpdir.path().join("sandbox")));
#[cfg(unix)]
{
check!(std::os::unix::fs::symlink(
"../outside",
tmpdir.path().join("sandbox/hidden")
));
check!(std::os::unix::fs::symlink(
"hidden/",
tmpdir.path().join("sandbox/indirect")
));
check!(std::os::unix::fs::symlink(
"/.",
tmpdir.path().join("sandbox/root_link")
));
}
#[cfg(windows)]
{
check!(std::os::windows::fs::symlink_dir(
"../outside",
tmpdir.path().join("sandbox/hidden")
));
check!(std::os::windows::fs::symlink_dir(
"hidden/",
tmpdir.path().join("sandbox/indirect")
));
check!(std::os::windows::fs::symlink_dir(
"/.",
tmpdir.path().join("sandbox/root_link")
));
}
#[cfg(not(any(unix, windows)))]
{
compile_error!("not implemented yet");
}

let tmpdir = check!(Dir::open_ambient_dir(tmpdir.path(), ambient_authority()));

let sandbox = check!(tmpdir.open_dir("sandbox"));

for path in [
"hidden",
"hidden/",
"indirect",
"indirect/",
"root_link",
"root_link/",
] {
error!(
sandbox.open_dir(path),
"a path led outside of the filesystem"
);
error!(
sandbox.read_dir(path),
"a path led outside of the filesystem"
);
error!(
sandbox.canonicalize(path),
"a path led outside of the filesystem"
);
}
}
Loading