#![forbid(unsafe_code)]
use crate::syscalls;
use bitflags::bitflags;
bitflags! {
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy)]
pub struct OpenFlags: libc::c_int {
const O_RDWR = libc::O_RDWR;
const O_RDONLY = libc::O_RDONLY;
const O_WRONLY = libc::O_WRONLY;
const O_PATH = libc::O_PATH;
const O_CLOEXEC = libc::O_CLOEXEC;
const O_NOFOLLOW = libc::O_NOFOLLOW;
const O_DIRECTORY = libc::O_DIRECTORY;
const O_NOCTTY = libc::O_NOCTTY;
const O_TMPFILE = libc::O_TMPFILE;
const O_CREAT = libc::O_CREAT;
const O_EXCL = libc::O_EXCL;
const O_TRUNC = libc::O_TRUNC;
const O_APPEND = libc::O_APPEND;
const O_SYNC = libc::O_SYNC;
const O_ASYNC = libc::O_ASYNC;
const O_DSYNC = libc::O_DSYNC;
#[cfg(not(target_env = "musl"))] const O_FSYNC = libc::O_FSYNC;
const O_RSYNC = libc::O_RSYNC;
const O_DIRECT = libc::O_DIRECT;
const O_NDELAY = libc::O_NDELAY;
const O_NOATIME = libc::O_NOATIME;
const O_NONBLOCK = libc::O_NONBLOCK;
const _ = !0;
}
}
impl From<OpenFlags> for rustix::fs::OFlags {
fn from(flags: OpenFlags) -> Self {
Self::from_bits_retain(flags.bits() as u32)
}
}
impl OpenFlags {
#[inline]
pub fn access_mode(self) -> Option<libc::c_int> {
if self.contains(OpenFlags::O_PATH) {
None
} else {
Some(self.bits() & libc::O_ACCMODE)
}
}
#[inline]
pub fn wants_read(self) -> bool {
match self.access_mode() {
None => false, Some(acc) => acc == libc::O_RDONLY || acc == libc::O_RDWR,
}
}
#[inline]
pub fn wants_write(self) -> bool {
match self.access_mode() {
None => false, Some(acc) => {
acc == libc::O_WRONLY
|| acc == libc::O_RDWR
|| !self
.intersection(OpenFlags::O_TRUNC | OpenFlags::O_CREAT)
.is_empty()
}
}
}
}
bitflags! {
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy)]
pub struct RenameFlags: libc::c_uint {
const RENAME_EXCHANGE = libc::RENAME_EXCHANGE;
const RENAME_NOREPLACE = libc::RENAME_NOREPLACE;
const RENAME_WHITEOUT = libc::RENAME_WHITEOUT;
const _ = !0;
}
}
impl From<RenameFlags> for rustix::fs::RenameFlags {
fn from(flags: RenameFlags) -> Self {
Self::from_bits_retain(flags.bits())
}
}
impl RenameFlags {
pub fn is_supported(self) -> bool {
self.is_empty() || *syscalls::RENAME_FLAGS_SUPPORTED
}
}
#[cfg(test)]
mod tests {
use crate::{
flags::{OpenFlags, RenameFlags},
syscalls,
};
macro_rules! openflags_tests {
($($test_name:ident ( $($flag:ident)|+ ) == {accmode: $accmode:expr, read: $wants_read:expr, write: $wants_write:expr} );+ $(;)?) => {
$(
paste::paste! {
#[test]
fn [<openflags_ $test_name _access_mode>]() {
let flags = $(OpenFlags::$flag)|*;
let accmode: Option<i32> = $accmode;
assert_eq!(flags.access_mode(), accmode, "{flags:?} access mode should be {:?}", accmode.map(OpenFlags::from_bits_retain));
}
#[test]
fn [<openflags_ $test_name _wants_read>]() {
let flags = $(OpenFlags::$flag)|*;
assert_eq!(flags.wants_read(), $wants_read, "{flags:?} wants_read should be {:?}", $wants_read);
}
#[test]
fn [<openflags_ $test_name _wants_write>]() {
let flags = $(OpenFlags::$flag)|*;
assert_eq!(flags.wants_write(), $wants_write, "{flags:?} wants_write should be {:?}", $wants_write);
}
}
)*
}
}
openflags_tests! {
plain_rdonly(O_RDONLY) == {accmode: Some(libc::O_RDONLY), read: true, write: false};
plain_wronly(O_WRONLY) == {accmode: Some(libc::O_WRONLY), read: false, write: true};
plain_rdwr(O_RDWR) == {accmode: Some(libc::O_RDWR), read: true, write: true};
plain_opath(O_PATH) == {accmode: None, read: false, write: false};
rdwr_opath(O_RDWR|O_PATH) == {accmode: None, read: false, write: false};
wronly_opath(O_WRONLY|O_PATH) == {accmode: None, read: false, write: false};
trunc_rdonly(O_RDONLY|O_TRUNC) == {accmode: Some(libc::O_RDONLY), read: true, write: true};
trunc_wronly(O_WRONLY|O_TRUNC) == {accmode: Some(libc::O_WRONLY), read: false, write: true};
trunc_rdwr(O_RDWR|O_TRUNC) == {accmode: Some(libc::O_RDWR), read: true, write: true};
trunc_path(O_PATH|O_TRUNC) == {accmode: None, read: false, write: false};
creat_rdonly(O_RDONLY|O_CREAT) == {accmode: Some(libc::O_RDONLY), read: true, write: true};
creat_wronly(O_WRONLY|O_CREAT) == {accmode: Some(libc::O_WRONLY), read: false, write: true};
creat_rdwr(O_RDWR|O_CREAT) == {accmode: Some(libc::O_RDWR), read: true, write: true};
creat_path(O_PATH|O_CREAT) == {accmode: None, read: false, write: false};
}
#[test]
fn rename_flags_is_supported() {
assert!(
RenameFlags::empty().is_supported(),
"empty flags should be supported"
);
assert_eq!(
RenameFlags::RENAME_EXCHANGE.is_supported(),
*syscalls::RENAME_FLAGS_SUPPORTED,
"rename flags being supported should be identical to RENAME_FLAGS_SUPPORTED"
);
}
}
bitflags! {
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct ResolverFlags: u64 {
const NO_SYMLINKS = libc::RESOLVE_NO_SYMLINKS;
}
}