aboutsummaryrefslogtreecommitdiff
path: root/src/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/builder.rs')
-rw-r--r--src/builder.rs162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/builder.rs b/src/builder.rs
new file mode 100644
index 0000000..b89efb4
--- /dev/null
+++ b/src/builder.rs
@@ -0,0 +1,162 @@
+use crate::error::{Error, Result};
+use crate::raw;
+use crate::{IoctlFlags, Uffd};
+use bitflags::bitflags;
+use nix::errno::Errno;
+
+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.
+ 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! {
+ /// Used with `UffdBuilder` to determine which features are available in the current kernel.
+ 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;
+ }
+ }
+ }
+}
+/// A builder for initializing `Uffd` objects.
+///
+/// ```
+/// use userfaultfd::UffdBuilder;
+///
+/// let uffd = UffdBuilder::new()
+/// .close_on_exec(true)
+/// .non_blocking(true)
+/// .user_mode_only(true)
+/// .create();
+/// assert!(uffd.is_ok());
+/// ```
+pub struct UffdBuilder {
+ close_on_exec: bool,
+ non_blocking: bool,
+ user_mode_only: bool,
+ req_features: FeatureFlags,
+ req_ioctls: IoctlFlags,
+}
+
+impl UffdBuilder {
+ /// Create a new builder with no required features or ioctls, `close_on_exec` and
+ /// `non_blocking` both set to `false`, and `user_mode_only` set to `true`.
+ pub fn new() -> UffdBuilder {
+ UffdBuilder {
+ close_on_exec: false,
+ non_blocking: false,
+ user_mode_only: true,
+ req_features: FeatureFlags::empty(),
+ req_ioctls: IoctlFlags::empty(),
+ }
+ }
+
+ /// Enable the close-on-exec flag for the new userfaultfd object (see the description of
+ /// `O_CLOEXEC` in [`open(2)`](http://man7.org/linux/man-pages/man2/open.2.html)).
+ pub fn close_on_exec(&mut self, close_on_exec: bool) -> &mut Self {
+ self.close_on_exec = close_on_exec;
+ self
+ }
+
+ /// Enable non-blocking operation for the userfaultfd object.
+ ///
+ /// If this is set to `false`, `Uffd::read_event()` will block until an event is available to
+ /// read. Otherwise, it will immediately return `None` if no event is available.
+ pub fn non_blocking(&mut self, non_blocking: bool) -> &mut Self {
+ self.non_blocking = non_blocking;
+ self
+ }
+
+ /// Enable user-mode only flag for the userfaultfd object.
+ ///
+ /// If set to `false`, the process must have the `CAP_SYS_PTRACE` capability starting with Linux 5.11
+ /// or object creation will fail with EPERM. When set to `true`, userfaultfd can't be used
+ /// to handle kernel-mode page faults such as when kernel tries copying data to userspace.
+ ///
+ /// When used with kernels older than 5.11, this has no effect; the process doesn't need
+ /// `CAP_SYS_PTRACE` and can handle kernel-mode page faults.
+ pub fn user_mode_only(&mut self, user_mode_only: bool) -> &mut Self {
+ self.user_mode_only = user_mode_only;
+ self
+ }
+
+ /// Add a requirement that a particular feature or set of features is available.
+ ///
+ /// If a required feature is unavailable, `UffdBuilder.create()` will return an error.
+ pub fn require_features(&mut self, feature: FeatureFlags) -> &mut Self {
+ self.req_features |= feature;
+ self
+ }
+
+ /// Add a requirement that a particular ioctl or set of ioctls is available.
+ ///
+ /// If a required ioctl is unavailable, `UffdBuilder.create()` will return an error.
+ pub fn require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self {
+ self.req_ioctls |= ioctls;
+ self
+ }
+
+ /// 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
+ 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 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 };
+
+ // then do the UFFDIO_API ioctl to set up and ensure features and other ioctls are available
+ 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(api.ioctls).ok_or(Error::UnrecognizedIoctls(api.ioctls))?;
+ if !supported.contains(self.req_ioctls) {
+ Err(Error::UnsupportedIoctls(supported))
+ } else {
+ Ok(uffd)
+ }
+ }
+}