use crate::error::{Error, Result};
use crate::raw;
use crate::{IoctlFlags, Uffd};
use bitflags::bitflags;
use nix::errno::Errno;
use std::fs::{File, OpenOptions};
use std::io::ErrorKind;
use std::os::fd::AsRawFd;
const UFFD_DEVICE_PATH: &str = "/dev/userfaultfd";
cfg_if::cfg_if! {
if #[cfg(any(feature = "linux5_7", feature = "linux4_14"))] {
bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
const SIGBUS = raw::UFFD_FEATURE_SIGBUS;
const THREAD_ID = raw::UFFD_FEATURE_THREAD_ID;
}
}
} else {
bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
}
}
}
}
pub struct UffdBuilder {
close_on_exec: bool,
non_blocking: bool,
user_mode_only: bool,
req_features: FeatureFlags,
req_ioctls: IoctlFlags,
}
impl UffdBuilder {
pub fn new() -> UffdBuilder {
UffdBuilder {
close_on_exec: false,
non_blocking: false,
user_mode_only: true,
req_features: FeatureFlags::empty(),
req_ioctls: IoctlFlags::empty(),
}
}
pub fn close_on_exec(&mut self, close_on_exec: bool) -> &mut Self {
self.close_on_exec = close_on_exec;
self
}
pub fn non_blocking(&mut self, non_blocking: bool) -> &mut Self {
self.non_blocking = non_blocking;
self
}
pub fn user_mode_only(&mut self, user_mode_only: bool) -> &mut Self {
self.user_mode_only = user_mode_only;
self
}
pub fn require_features(&mut self, feature: FeatureFlags) -> &mut Self {
self.req_features |= feature;
self
}
pub fn require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self {
self.req_ioctls |= ioctls;
self
}
fn uffd_from_dev(&self, file: &mut File, flags: i32) -> Result<Uffd> {
match unsafe { raw::new_uffd(file.as_raw_fd(), flags) } {
Err(err) => Err(err.into()),
Ok(fd) => Ok(Uffd { fd }),
}
}
fn uffd_from_syscall(&self, flags: i32) -> Result<Uffd> {
let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
Ok(fd) => fd,
Err(Errno::EINVAL) if self.user_mode_only => Errno::result(unsafe {
raw::userfaultfd(flags & !raw::UFFD_USER_MODE_ONLY as i32)
})?,
Err(e) => return Err(e.into()),
};
Ok(Uffd { fd })
}
fn open_file_descriptor(&self, flags: i32) -> Result<Uffd> {
match OpenOptions::new()
.read(true)
.write(true)
.open(UFFD_DEVICE_PATH)
{
Ok(mut file) => self.uffd_from_dev(&mut file, flags),
Err(err) if err.kind() == ErrorKind::NotFound => self.uffd_from_syscall(flags),
Err(err) => Err(Error::OpenDevUserfaultfd(err)),
}
}
pub fn create(&self) -> Result<Uffd> {
let mut flags = 0;
if self.close_on_exec {
flags |= libc::O_CLOEXEC;
}
if self.non_blocking {
flags |= libc::O_NONBLOCK;
}
if self.user_mode_only {
flags |= raw::UFFD_USER_MODE_ONLY as i32;
}
let uffd = self.open_file_descriptor(flags)?;
let mut api = raw::uffdio_api {
api: raw::UFFD_API,
features: self.req_features.bits(),
ioctls: 0,
};
unsafe {
raw::api(uffd.fd, &mut api as *mut raw::uffdio_api)?;
}
let supported = IoctlFlags::from_bits_retain(api.ioctls);
if !supported.contains(self.req_ioctls) {
Err(Error::UnsupportedIoctls(supported))
} else {
Ok(uffd)
}
}
}