use super::super::super::fs::{c_str, compute_oflags};
#[cfg(racy_asserts)]
use crate::fs::is_same_file;
use crate::fs::{errors, manually, OpenOptions};
use io_lifetimes::FromFd;
use posish::fs::{openat2, Mode, OFlags, RawMode, ResolveFlags};
use std::{
fs, io,
path::Path,
sync::atomic::{AtomicBool, Ordering::Relaxed},
};
pub(crate) fn open_impl(
start: &fs::File,
path: &Path,
options: &OpenOptions,
) -> io::Result<fs::File> {
let result = open_beneath(start, path, options);
if let Err(err) = &result {
if Some(posish::io::Error::NOSYS.raw_os_error()) == err.raw_os_error() {
return manually::open(start, path, options);
}
}
result
}
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) {
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()
};
let path_c_str = c_str(path)?;
for _ in 0..4 {
match openat2(
start,
path_c_str.as_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 {
posish::io::Error::AGAIN => continue,
posish::io::Error::XDEV => return Err(errors::escape_attempt()),
posish::io::Error::PERM => break,
posish::io::Error::NOSYS => {
INVALID.store(true, Relaxed);
break;
}
_ => return Err(err.into()),
},
}
}
}
Err(posish::io::Error::NOSYS.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"
);
}