use std::fmt;
use log::warn;
#[derive(Default, Clone)]
pub struct HyperFileFlags {
pub read: bool,
pub write: bool,
pub creat: bool,
pub excl: bool,
pub append: bool,
pub trunc: bool,
pub sync: bool,
pub dsync: bool,
pub direct: bool,
pub noatime: bool,
sync_flush_mode: bool,
}
impl HyperFileFlags {
pub fn from_flags(f: FileFlags) -> Self {
let (read, write) = if f.is_rdonly() {
(true, false)
} else if f.is_wronly() {
(false, true)
} else if f.is_rdwr() {
(true, true)
} else {
warn!("invalid file flags, both read and write bit not been set");
(false, false)
};
Self {
read: read,
write: write,
creat: f.is_creat(),
excl: f.is_excl(),
append: f.is_append(),
trunc: f.is_trunc(),
sync: f.is_sync(),
dsync: f.is_dsync(),
direct: f.is_direct(),
noatime: f.is_noatime(),
sync_flush_mode: f.is_direct() | f.is_sync() | f.is_dsync(),
}
}
pub fn rdonly() -> Self {
let mut f = Self::default();
f.read = true;
f
}
pub fn wronly() -> Self {
let mut f = Self::default();
f.write = true;
f
}
pub fn all() -> Self {
Self {
read: true,
write: true,
creat: true,
excl: true,
append: true,
trunc: true,
sync: true,
dsync: true,
direct: true,
noatime: true,
sync_flush_mode: true,
}
}
pub fn is_sync(&self) -> bool {
self.sync
}
pub fn is_dsync(&self) -> bool {
self.dsync
}
pub fn is_rdonly(&self) -> bool {
self.read && !self.write && !self.append
}
pub fn is_writable(&self) -> bool {
self.write
}
pub fn is_readable(&self) -> bool {
self.read
}
pub fn is_direct(&self) -> bool {
self.direct
}
pub fn is_append(&self) -> bool {
self.append
}
pub fn is_trunc(&self) -> bool {
self.trunc
}
pub fn is_excl(&self) -> bool {
self.excl
}
pub fn is_noatime(&self) -> bool {
self.noatime
}
pub fn is_sync_flush_mode(&self) -> bool {
self.sync_flush_mode
}
}
pub struct FileFlags(libc::c_int);
impl fmt::Display for FileFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_rdonly() {
write!(f, "O_RDONLY")?;
}
if self.is_wronly() {
write!(f, "O_WRONLY")?;
}
if self.is_rdwr() {
write!(f, "O_RDWR")?;
}
if self.is_append() {
write!(f, " | O_APPEND")?;
}
if self.is_async() {
write!(f, " | O_ASYNC")?;
}
if self.is_cloexec() {
write!(f, " | O_CLOEXEC")?;
}
if self.is_creat() {
write!(f, " | O_CREAT")?;
}
if self.is_direct() {
write!(f, " | O_DIRECT")?;
}
if self.is_directory() {
write!(f, " | O_DIRECTORY")?;
}
if self.is_dsync() {
write!(f, " | O_DSYNC")?;
}
if self.is_excl() {
write!(f, " | O_EXCL")?;
}
if self.is_largefile() {
write!(f, " | O_LARGEFILE")?;
}
if self.is_noatime() {
write!(f, " | O_NOATIME")?;
}
if self.is_noctty() {
write!(f, " | O_NOCTTY")?;
}
if self.is_nofollow() {
write!(f, " | O_NOFOLLOW")?;
}
if self.is_nonblock() {
write!(f, " | O_NONBLOCK")?;
}
if self.is_ndelay() {
write!(f, " | O_NDELAY")?;
}
if self.is_path() {
write!(f, " | O_PATH")?;
}
if self.is_sync() {
write!(f, " | O_SYNC")?;
}
if self.is_trunc() {
write!(f, " | O_TRUNC")?;
}
write!(f, "")
}
}
impl FileFlags {
pub fn from(flags: libc::c_int) -> Self {
Self(flags)
}
pub fn rdonly() -> Self {
Self(libc::O_RDONLY)
}
pub fn wronly() -> Self {
Self(libc::O_WRONLY)
}
pub fn rdwr() -> Self {
Self(libc::O_RDWR)
}
pub fn is_rdonly(&self) -> bool {
(self.0 & libc::O_ACCMODE) == libc::O_RDONLY
}
pub fn is_wronly(&self) -> bool {
(self.0 & libc::O_ACCMODE) == libc::O_WRONLY
}
pub fn is_rdwr(&self) -> bool {
(self.0 & libc::O_ACCMODE) == libc::O_RDWR
}
pub fn is_append(&self) -> bool {
(self.0 & libc::O_APPEND) == libc::O_APPEND
}
pub fn is_async(&self) -> bool {
(self.0 & libc::O_ASYNC) == libc::O_ASYNC
}
pub fn is_cloexec(&self) -> bool {
(self.0 & libc::O_CLOEXEC) == libc::O_CLOEXEC
}
pub fn is_creat(&self) -> bool {
(self.0 & libc::O_CREAT) == libc::O_CREAT
}
pub fn is_direct(&self) -> bool {
(self.0 & libc::O_DIRECT) == libc::O_DIRECT
}
pub fn is_directory(&self) -> bool {
(self.0 & libc::O_DIRECTORY) == libc::O_DIRECTORY
}
pub fn is_dsync(&self) -> bool {
(self.0 & libc::O_DSYNC) == libc::O_DSYNC
}
pub fn is_excl(&self) -> bool {
(self.0 & libc::O_EXCL) == libc::O_EXCL
}
pub fn is_largefile(&self) -> bool {
(self.0 & libc::O_LARGEFILE) == libc::O_LARGEFILE
}
pub fn is_noatime(&self) -> bool {
(self.0 & libc::O_NOATIME) == libc::O_NOATIME
}
pub fn is_noctty(&self) -> bool {
(self.0 & libc::O_NOCTTY) == libc::O_NOCTTY
}
pub fn is_nofollow(&self) -> bool {
(self.0 & libc::O_NOFOLLOW) == libc::O_NOFOLLOW
}
pub fn is_nonblock(&self) -> bool {
(self.0 & libc::O_NONBLOCK) == libc::O_NONBLOCK
}
pub fn is_ndelay(&self) -> bool {
(self.0 & libc::O_NDELAY) == libc::O_NDELAY
}
pub fn is_path(&self) -> bool {
(self.0 & libc::O_PATH) == libc::O_PATH
}
pub fn is_sync(&self) -> bool {
(self.0 & libc::O_SYNC) == libc::O_SYNC
}
pub fn is_trunc(&self) -> bool {
(self.0 & libc::O_TRUNC) == libc::O_TRUNC
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn file_flags_rdonly() {
let f = FileFlags::rdonly();
assert!(f.is_rdonly());
assert!(!f.is_wronly());
assert!(!f.is_rdwr());
}
#[test]
fn file_flags_wronly() {
let f = FileFlags::wronly();
assert!(f.is_wronly());
assert!(!f.is_rdonly());
assert!(!f.is_rdwr());
}
#[test]
fn file_flags_rdwr() {
let f = FileFlags::rdwr();
assert!(f.is_rdwr());
assert!(!f.is_rdonly());
assert!(!f.is_wronly());
}
#[test]
fn file_flags_combined() {
let f = FileFlags::from(libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC | libc::O_APPEND);
assert!(f.is_rdwr());
assert!(f.is_creat());
assert!(f.is_trunc());
assert!(f.is_append());
assert!(!f.is_direct());
assert!(!f.is_sync());
}
#[test]
fn file_flags_direct_sync_dsync() {
let f = FileFlags::from(libc::O_WRONLY | libc::O_DIRECT | libc::O_SYNC | libc::O_DSYNC);
assert!(f.is_wronly());
assert!(f.is_direct());
assert!(f.is_sync());
assert!(f.is_dsync());
}
#[test]
fn hyper_flags_from_rdonly() {
let hf = HyperFileFlags::from_flags(FileFlags::rdonly());
assert!(hf.read);
assert!(!hf.write);
assert!(hf.is_rdonly());
assert!(!hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_from_rdwr() {
let hf = HyperFileFlags::from_flags(FileFlags::rdwr());
assert!(hf.read);
assert!(hf.write);
assert!(!hf.is_rdonly());
}
#[test]
fn hyper_flags_from_wronly() {
let hf = HyperFileFlags::from_flags(FileFlags::wronly());
assert!(!hf.read);
assert!(hf.write);
}
#[test]
fn hyper_flags_creat_trunc_append() {
let f = FileFlags::from(libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC | libc::O_APPEND);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.creat);
assert!(hf.is_trunc());
assert!(hf.is_append());
}
#[test]
fn hyper_flags_sync_flush_mode_from_direct() {
let f = FileFlags::from(libc::O_WRONLY | libc::O_DIRECT);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_direct());
assert!(hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_sync_flush_mode_from_sync() {
let f = FileFlags::from(libc::O_WRONLY | libc::O_SYNC);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_sync());
assert!(hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_sync_flush_mode_from_dsync() {
let f = FileFlags::from(libc::O_WRONLY | libc::O_DSYNC);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_dsync());
assert!(hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_rdonly_constructor() {
let hf = HyperFileFlags::rdonly();
assert!(hf.is_rdonly());
assert!(!hf.write);
}
#[test]
fn hyper_flags_wronly_constructor() {
let hf = HyperFileFlags::wronly();
assert!(hf.write);
assert!(!hf.read);
}
#[test]
fn hyper_flags_all() {
let hf = HyperFileFlags::all();
assert!(hf.read);
assert!(hf.write);
assert!(hf.creat);
assert!(hf.is_append());
assert!(hf.is_trunc());
assert!(hf.is_sync());
assert!(hf.is_dsync());
assert!(hf.is_direct());
assert!(hf.is_noatime());
assert!(hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_noatime_off_by_default() {
let hf = HyperFileFlags::from_flags(FileFlags::rdonly());
assert!(!hf.is_noatime());
let hf = HyperFileFlags::from_flags(FileFlags::wronly());
assert!(!hf.is_noatime());
let hf = HyperFileFlags::from_flags(FileFlags::rdwr());
assert!(!hf.is_noatime());
}
#[test]
fn hyper_flags_noatime_set_when_o_noatime() {
let f = FileFlags::from(libc::O_RDONLY | libc::O_NOATIME);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_noatime());
assert!(!hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_noatime_orthogonal_to_other_flags() {
let f = FileFlags::from(libc::O_RDWR | libc::O_NOATIME | libc::O_DIRECT);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_noatime());
assert!(hf.is_direct());
assert!(hf.is_sync_flush_mode());
}
#[test]
fn hyper_flags_default_noatime_false() {
let hf = HyperFileFlags::default();
assert!(!hf.is_noatime());
let hf = HyperFileFlags::rdonly();
assert!(!hf.is_noatime());
let hf = HyperFileFlags::wronly();
assert!(!hf.is_noatime());
}
#[test]
fn hyper_flags_excl_off_by_default() {
let hf = HyperFileFlags::from_flags(FileFlags::rdonly());
assert!(!hf.is_excl());
let hf = HyperFileFlags::from_flags(FileFlags::wronly());
assert!(!hf.is_excl());
let hf = HyperFileFlags::from_flags(FileFlags::rdwr());
assert!(!hf.is_excl());
}
#[test]
fn hyper_flags_excl_set_when_o_excl() {
let f = FileFlags::from(libc::O_RDWR | libc::O_CREAT | libc::O_EXCL);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_excl());
assert!(hf.creat);
}
#[test]
fn hyper_flags_excl_without_creat_still_carries_through() {
let f = FileFlags::from(libc::O_RDWR | libc::O_EXCL);
let hf = HyperFileFlags::from_flags(f);
assert!(hf.is_excl());
assert!(!hf.creat);
}
#[test]
fn hyper_flags_default_excl_false() {
assert!(!HyperFileFlags::default().is_excl());
assert!(!HyperFileFlags::rdonly().is_excl());
assert!(!HyperFileFlags::wronly().is_excl());
}
#[test]
fn hyper_flags_all_includes_excl() {
assert!(HyperFileFlags::all().is_excl());
}
#[test]
fn hyper_flags_rdonly_is_not_writable() {
assert!(!HyperFileFlags::from_flags(FileFlags::rdonly()).is_writable());
}
#[test]
fn hyper_flags_wronly_and_rdwr_are_writable() {
assert!(HyperFileFlags::from_flags(FileFlags::wronly()).is_writable());
assert!(HyperFileFlags::from_flags(FileFlags::rdwr()).is_writable());
}
#[test]
fn hyper_flags_rdonly_append_is_not_writable() {
let f = HyperFileFlags::from_flags(FileFlags(libc::O_RDONLY | libc::O_APPEND));
assert!(!f.is_writable(), "O_RDONLY|O_APPEND must not be writable");
}
#[test]
fn hyper_flags_wronly_is_not_readable() {
assert!(!HyperFileFlags::from_flags(FileFlags::wronly()).is_readable());
}
#[test]
fn hyper_flags_rdonly_and_rdwr_are_readable() {
assert!(HyperFileFlags::from_flags(FileFlags::rdonly()).is_readable());
assert!(HyperFileFlags::from_flags(FileFlags::rdwr()).is_readable());
}
#[test]
fn hyper_flags_access_mode_matrix() {
for (flags, name, readable, writable) in [
(FileFlags::rdonly(), "O_RDONLY", true, false),
(FileFlags::wronly(), "O_WRONLY", false, true),
(FileFlags::rdwr(), "O_RDWR", true, true),
] {
let f = HyperFileFlags::from_flags(flags);
assert_eq!(f.is_readable(), readable, "{name} is_readable");
assert_eq!(f.is_writable(), writable, "{name} is_writable");
}
}
}