#[cfg(racy_asserts)]
use crate::fs::is_same_file;
use crate::fs::{manually, OpenOptions};
use std::path::Path;
use std::{fs, io};
#[cfg(target_os = "linux")]
use {
super::super::super::fs::compute_oflags,
crate::fs::errors,
io_lifetimes::FromFd,
rustix::fs::{openat2, Mode, OFlags, RawMode, ResolveFlags},
rustix::path::Arg,
std::sync::atomic::AtomicBool,
std::sync::atomic::Ordering::Relaxed,
};
pub(crate) fn open_impl(
start: &fs::File,
path: &Path,
options: &OpenOptions,
) -> io::Result<fs::File> {
#[cfg(target_os = "linux")]
{
let result = open_beneath(start, path, options);
match result {
Err(err) if err.raw_os_error() == Some(rustix::io::Errno::NOSYS.raw_os_error()) => {}
Err(err) => return Err(err),
Ok(fd) => return Ok(fd),
}
}
manually::open(start, path, options)
}
#[cfg(target_os = "linux")]
pub(crate) fn open_beneath(
start: &fs::File,
path: &Path,
options: &OpenOptions,
) -> io::Result<fs::File> {
static INVALID: AtomicBool = AtomicBool::new(false);
if INVALID.load(Relaxed) {
return Err(rustix::io::Errno::NOSYS.into());
}
let oflags = compute_oflags(options)?;
let mode = if oflags.contains(OFlags::CREATE) || oflags.contains(OFlags::TMPFILE) {
Mode::from_bits((options.ext.mode & 0o7777) as RawMode).unwrap()
} else {
Mode::empty()
};
path.into_with_c_str(|path_c_str| {
for _ in 0..4 {
match openat2(
start,
path_c_str,
oflags,
mode,
ResolveFlags::BENEATH | ResolveFlags::NO_MAGICLINKS,
) {
Ok(file) => {
let file = fs::File::from_into_fd(file);
#[cfg(racy_asserts)]
check_open(start, path, options, &file);
return Ok(file);
}
Err(err) => match err {
rustix::io::Errno::AGAIN => continue,
rustix::io::Errno::PERM => break,
rustix::io::Errno::NOSYS => {
INVALID.store(true, Relaxed);
break;
}
_ => return Err(err),
},
}
}
Err(rustix::io::Errno::NOSYS)
})
.map_err(|err| match err {
rustix::io::Errno::XDEV => errors::escape_attempt(),
err => err.into(),
})
}
#[cfg(racy_asserts)]
fn check_open(start: &fs::File, path: &Path, options: &OpenOptions, file: &fs::File) {
let check = manually::open(
start,
path,
options
.clone()
.create(false)
.create_new(false)
.truncate(false),
)
.expect("manually::open failed when open_openat2 succeeded");
assert!(
is_same_file(file, &check).unwrap(),
"manually::open should open the same inode as open_openat2"
);
}