aboutsummaryrefslogtreecommitdiff
path: root/src/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/builder.rs')
-rw-r--r--src/builder.rs62
1 files changed, 49 insertions, 13 deletions
diff --git a/src/builder.rs b/src/builder.rs
index b89efb4..a200148 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -3,11 +3,17 @@ 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! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
+ #[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;
@@ -23,6 +29,7 @@ cfg_if::cfg_if! {
} else {
bitflags! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
+ #[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;
@@ -113,6 +120,47 @@ impl UffdBuilder {
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,
+ // setting the USER_MODE_ONLY flag on kernel pre-5.11 causes it to return EINVAL.
+ // If the user asks for the flag, we first try with it set, and if kernel gives
+ // EINVAL we try again without the flag set.
+ 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()),
+ };
+
+ // Wrap the fd up so that a failure in this function body closes it with the drop.
+ Ok(Uffd { fd })
+ }
+
+ // Try to get a UFFD file descriptor using `/dev/userfaultfd`. If that fails
+ // fall back to calling the system call.
+ fn open_file_descriptor(&self, flags: i32) -> Result<Uffd> {
+ // If `/dev/userfaultfd` exists we'll try to get the file descriptor from it. If the file
+ // doesn't exist we will fall back to calling the system call. This means, that if the
+ // device exists but the calling process does not have access rights to it, this will fail,
+ // i.e. we will not fall back to calling the system call.
+ 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)),
+ }
+ }
+
/// Create a `Uffd` object with the current settings of this builder.
pub fn create(&self) -> Result<Uffd> {
// first do the syscall to get the file descriptor
@@ -128,19 +176,7 @@ impl UffdBuilder {
flags |= raw::UFFD_USER_MODE_ONLY as i32;
}
- let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
- Ok(fd) => fd,
- // setting the USER_MODE_ONLY flag on kernel pre-5.11 causes it to return EINVAL.
- // If the user asks for the flag, we first try with it set, and if kernel gives
- // EINVAL we try again without the flag set.
- 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()),
- };
-
- // Wrap the fd up so that a failure in this function body closes it with the drop.
- let uffd = Uffd { fd };
+ let uffd = self.open_file_descriptor(flags)?;
// then do the UFFDIO_API ioctl to set up and ensure features and other ioctls are available
let mut api = raw::uffdio_api {