blob: 0b1d86fe2656ded8527c83f06f420727c25ac23d [file] [log] [blame]
use rustix::fd::AsFd;
use rustix::fs::{cwd, mkdirat, openat, openat2, symlinkat, Mode, OFlags, ResolveFlags};
use rustix::io::OwnedFd;
use rustix::{io, path};
use std::os::unix::io::AsRawFd;
/// Like `openat2`, but keep retrying until it fails or succeeds.
fn openat2_more<Fd: AsFd, P: path::Arg>(
dirfd: Fd,
path: P,
oflags: OFlags,
mode: Mode,
resolve: ResolveFlags,
) -> io::Result<OwnedFd> {
let path = path.as_cow_c_str().unwrap().into_owned();
loop {
match openat2(dirfd.as_fd(), &path, oflags, mode, resolve) {
Ok(file) => return Ok(file),
Err(io::Errno::AGAIN) => continue,
Err(err) => return Err(err),
}
}
}
#[test]
fn test_openat2() {
let tmp = tempfile::tempdir().unwrap();
let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap();
// Detect whether `openat2` is available.
match openat2(
&dir,
".",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
) {
Ok(_file) => (),
Err(io::Errno::NOSYS) => return,
Err(_err) => return,
}
// Create a file.
let _ = openat2_more(
&dir,
"test.txt",
OFlags::WRONLY | OFlags::CREATE | OFlags::TRUNC | OFlags::CLOEXEC,
Mode::RUSR,
ResolveFlags::empty(),
)
.unwrap();
// Test `NO_SYMLINKS`.
symlinkat("test.txt", &dir, "symlink.txt").unwrap();
let _ = openat2_more(
&dir,
"symlink.txt",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&dir,
"symlink.txt",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_MAGICLINKS,
)
.unwrap();
let _ = openat2_more(
&dir,
"symlink.txt",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_SYMLINKS,
)
.unwrap_err();
// Test `NO_MAGICLINKS`.
let test = openat2_more(
&dir,
"test.txt",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&dir,
&format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()),
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&dir,
&format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()),
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_SYMLINKS,
)
.unwrap_err();
let _ = openat2_more(
&dir,
&format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()),
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_MAGICLINKS,
)
.unwrap_err();
// Test `NO_XDEV`.
let root = openat2_more(
&dir,
"/",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&root,
"proc",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&root,
"proc",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::NO_XDEV,
)
.unwrap_err();
// Test `BENEATH`.
let _ = openat2_more(
&dir,
"..",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&dir,
"..",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::BENEATH,
)
.unwrap_err();
// Test `IN_ROOT`.
let _ = openat2_more(
&dir,
"/proc",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::empty(),
)
.unwrap();
let _ = openat2_more(
&dir,
"/proc",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::IN_ROOT,
)
.unwrap_err();
mkdirat(&dir, "proc", Mode::RUSR | Mode::XUSR).unwrap();
let _ = openat2_more(
&dir,
"/proc",
OFlags::RDONLY | OFlags::CLOEXEC,
Mode::empty(),
ResolveFlags::IN_ROOT,
)
.unwrap();
}