use std::collections::HashSet;
use std::io;
use std::io::ErrorKind;
use std::iter::FromIterator;
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub enum MountOption {
FSName(String),
Subtype(String),
CUSTOM(String),
AllowOther,
AllowRoot,
AutoUnmount,
DefaultPermissions,
Dev,
NoDev,
Suid,
NoSuid,
RO,
RW,
Exec,
NoExec,
Atime,
NoAtime,
DirSync,
Sync,
Async,
}
pub fn check_option_conflicts(options: &[MountOption]) -> Result<(), io::Error> {
let mut options_set = HashSet::new();
options_set.extend(options.iter().cloned());
let conflicting = HashSet::from_iter(options.iter().map(conflicts_with).flatten());
let intersection: Vec<MountOption> = conflicting.intersection(&options_set).cloned().collect();
if !intersection.is_empty() {
Err(io::Error::new(
ErrorKind::InvalidInput,
format!("Conflicting mount options found: {:?}", intersection),
))
} else {
Ok(())
}
}
fn conflicts_with(option: &MountOption) -> Vec<MountOption> {
match option {
MountOption::FSName(_) => vec![],
MountOption::Subtype(_) => vec![],
MountOption::CUSTOM(_) => vec![],
MountOption::AllowOther => vec![MountOption::AllowRoot],
MountOption::AllowRoot => vec![MountOption::AllowOther],
MountOption::AutoUnmount => vec![],
MountOption::DefaultPermissions => vec![],
MountOption::Dev => vec![MountOption::NoDev],
MountOption::NoDev => vec![MountOption::Dev],
MountOption::Suid => vec![MountOption::NoSuid],
MountOption::NoSuid => vec![MountOption::Suid],
MountOption::RO => vec![MountOption::RW],
MountOption::RW => vec![MountOption::RO],
MountOption::Exec => vec![MountOption::NoExec],
MountOption::NoExec => vec![MountOption::Exec],
MountOption::Atime => vec![MountOption::NoAtime],
MountOption::NoAtime => vec![MountOption::Atime],
MountOption::DirSync => vec![],
MountOption::Sync => vec![MountOption::Async],
MountOption::Async => vec![MountOption::Sync],
}
}
#[derive(PartialEq)]
#[cfg(not(feature = "libfuse"))]
pub enum MountOptionGroup {
KernelOption,
KernelFlag,
Fusermount,
}
#[cfg(not(feature = "libfuse"))]
pub fn option_group(option: &MountOption) -> MountOptionGroup {
match option {
MountOption::FSName(_) => MountOptionGroup::Fusermount,
MountOption::Subtype(_) => MountOptionGroup::Fusermount,
MountOption::CUSTOM(_) => MountOptionGroup::KernelOption,
MountOption::AutoUnmount => MountOptionGroup::Fusermount,
MountOption::AllowOther => MountOptionGroup::KernelOption,
MountOption::Dev => MountOptionGroup::KernelFlag,
MountOption::NoDev => MountOptionGroup::KernelFlag,
MountOption::Suid => MountOptionGroup::KernelFlag,
MountOption::NoSuid => MountOptionGroup::KernelFlag,
MountOption::RO => MountOptionGroup::KernelFlag,
MountOption::RW => MountOptionGroup::KernelFlag,
MountOption::Exec => MountOptionGroup::KernelFlag,
MountOption::NoExec => MountOptionGroup::KernelFlag,
MountOption::Atime => MountOptionGroup::KernelFlag,
MountOption::NoAtime => MountOptionGroup::KernelFlag,
MountOption::DirSync => MountOptionGroup::KernelFlag,
MountOption::Sync => MountOptionGroup::KernelFlag,
MountOption::Async => MountOptionGroup::KernelFlag,
MountOption::AllowRoot => MountOptionGroup::KernelOption,
MountOption::DefaultPermissions => MountOptionGroup::KernelOption,
}
}
pub fn option_to_string(option: &MountOption) -> String {
match option {
MountOption::FSName(name) => format!("fsname={}", name),
MountOption::Subtype(subtype) => format!("subtype={}", subtype),
MountOption::CUSTOM(value) => value.to_string(),
MountOption::AutoUnmount => "auto_unmount".to_string(),
MountOption::AllowOther => "allow_other".to_string(),
MountOption::AllowRoot => "allow_root".to_string(),
MountOption::DefaultPermissions => "default_permissions".to_string(),
MountOption::Dev => "dev".to_string(),
MountOption::NoDev => "nodev".to_string(),
MountOption::Suid => "suid".to_string(),
MountOption::NoSuid => "nosuid".to_string(),
MountOption::RO => "ro".to_string(),
MountOption::RW => "rw".to_string(),
MountOption::Exec => "exec".to_string(),
MountOption::NoExec => "noexec".to_string(),
MountOption::Atime => "atime".to_string(),
MountOption::NoAtime => "noatime".to_string(),
MountOption::DirSync => "dirsync".to_string(),
MountOption::Sync => "sync".to_string(),
MountOption::Async => "async".to_string(),
}
}
#[cfg(all(not(feature = "libfuse"), target_os = "linux"))]
pub fn option_to_flag(option: &MountOption) -> libc::c_ulong {
match option {
MountOption::Dev => 0, MountOption::NoDev => libc::MS_NODEV,
MountOption::Suid => 0,
MountOption::NoSuid => libc::MS_NOSUID,
MountOption::RW => 0,
MountOption::RO => libc::MS_RDONLY,
MountOption::Exec => 0,
MountOption::NoExec => libc::MS_NOEXEC,
MountOption::Atime => 0,
MountOption::NoAtime => libc::MS_NOATIME,
MountOption::Async => 0,
MountOption::Sync => libc::MS_SYNCHRONOUS,
MountOption::DirSync => libc::MS_DIRSYNC,
_ => unreachable!(),
}
}
#[cfg(all(not(feature = "libfuse"), target_os = "macos"))]
pub fn option_to_flag(option: &MountOption) -> libc::c_int {
match option {
MountOption::Dev => 0, MountOption::NoDev => libc::MNT_NODEV,
MountOption::Suid => 0,
MountOption::NoSuid => libc::MNT_NOSUID,
MountOption::RW => 0,
MountOption::RO => libc::MNT_RDONLY,
MountOption::Exec => 0,
MountOption::NoExec => libc::MNT_NOEXEC,
MountOption::Atime => 0,
MountOption::NoAtime => libc::MNT_NOATIME,
MountOption::Async => 0,
MountOption::Sync => libc::MNT_SYNCHRONOUS,
_ => unreachable!(),
}
}
#[cfg(test)]
mod test {
use crate::mount_options::check_option_conflicts;
use crate::MountOption;
#[test]
fn option_checking() {
assert!(check_option_conflicts(&[MountOption::Suid, MountOption::NoSuid]).is_err());
assert!(check_option_conflicts(&[MountOption::Suid, MountOption::NoExec]).is_ok());
}
}