Skip to content

Commit 39dde67

Browse files
committed
Use RESOLVE_NO_SYMLINKS on Linux when following symlinks is disabled
This addresses a bug where a forward slash is the last element of a symlink directory path and `open_dir_nofollow` is used. If a symlink is created to a directory without specifying an absolute path, then the path behind the symlink does not error when opened with `open_dir_nofollow`. Take these two symlinks: ``` > ls -l /home/ylk/test-dir-symlink* lrwxrwxrwx 1 ylk ylk 24 Feb 19 21:15 /home/ylk/test-dir-symlink -> /local/home/ylk/test-dir/ lrwxrwxrwx 1 ylk ylk 8 Feb 19 21:27 /home/ylk/test-dir-symlink2 -> test-dir/ ``` Without this patch, the following behavior is observed when using open_dir_nofollow: test-dir-symlink/ -> error test-dir-symlink -> error test-dir-symlink2 -> error test-dir-symlink2/ -> **no error**
1 parent dcead54 commit 39dde67

File tree

2 files changed

+22
-15
lines changed

2 files changed

+22
-15
lines changed

cap-primitives/src/rustix/linux/fs/open_impl.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
1010
#[cfg(racy_asserts)]
1111
use crate::fs::is_same_file;
12+
use crate::fs::FollowSymlinks;
1213
use crate::fs::{manually, OpenOptions};
1314
use std::path::Path;
1415
use std::{fs, io};
@@ -82,13 +83,11 @@ pub(crate) fn open_beneath(
8283
// times, because there's no limit on how often this can happen. The actual
8384
// number here is currently an arbitrarily chosen guess.
8485
for _ in 0..4 {
85-
match openat2(
86-
start,
87-
path_c_str,
88-
oflags,
89-
mode,
90-
ResolveFlags::BENEATH | ResolveFlags::NO_MAGICLINKS,
91-
) {
86+
let mut resolve_flags = ResolveFlags::BENEATH | ResolveFlags::NO_MAGICLINKS;
87+
if options.follow == FollowSymlinks::No {
88+
resolve_flags |= ResolveFlags::NO_SYMLINKS;
89+
}
90+
match openat2(start, path_c_str, oflags, mode, resolve_flags) {
9291
Ok(file) => {
9392
let file = fs::File::from_into_fd(file);
9493

tests/symlinks.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,19 +258,19 @@ fn open_dir_nofollow() {
258258
let name = format!("{}{}", symlink_dir, suffix);
259259
check!(tmpdir.open_dir(&name));
260260
// On Windows, a trailing dot is stripped early.
261-
if cfg!(not(windows)) || suffix != &"/." {
262-
check!(tmpdir.open_dir_nofollow(&name));
263-
} else {
261+
if (cfg!(windows) && suffix == &"/.") || cfg!(target_os = "linux") {
264262
assert!(tmpdir.open_dir_nofollow(&name).is_err());
263+
} else {
264+
check!(tmpdir.open_dir_nofollow(&name));
265265
}
266266
for dir_name in &["dir", "symlink_dir"] {
267267
let name = format!("{}/../{}", dir_name, name);
268268
check!(tmpdir.open_dir(&name));
269269
// On Windows, a trailing dot is stripped early.
270-
if cfg!(not(windows)) || suffix != &"/." {
271-
check!(tmpdir.open_dir_nofollow(&name));
272-
} else {
270+
if (cfg!(windows) && suffix == &"/.") || cfg!(target_os = "linux") {
273271
assert!(tmpdir.open_dir_nofollow(&name).is_err());
272+
} else {
273+
check!(tmpdir.open_dir_nofollow(&name));
274274
}
275275
}
276276
}
@@ -296,7 +296,11 @@ fn open_dir_nofollow() {
296296
#[cfg(not(windows))]
297297
{
298298
check!(tmpdir.open_dir(&name));
299-
check!(tmpdir.open_dir_nofollow(&name));
299+
if cfg!(target_os = "linux") {
300+
assert!(tmpdir.open_dir_nofollow(&name).is_err());
301+
} else {
302+
check!(tmpdir.open_dir_nofollow(&name));
303+
}
300304
}
301305
for dir_name in &["dir", "symlink_dir"] {
302306
let name = format!("{}/../{}", dir_name, name);
@@ -308,7 +312,11 @@ fn open_dir_nofollow() {
308312
#[cfg(not(windows))]
309313
{
310314
check!(tmpdir.open_dir(&name));
311-
check!(tmpdir.open_dir_nofollow(&name));
315+
if cfg!(target_os = "linux") {
316+
assert!(tmpdir.open_dir_nofollow(&name).is_err());
317+
} else {
318+
check!(tmpdir.open_dir_nofollow(&name));
319+
}
312320
}
313321
}
314322
}

0 commit comments

Comments
 (0)