ureeves_userfaultfd/
builder.rs1use crate::error::{Error, Result};
2use crate::raw;
3use crate::{IoctlFlags, Uffd};
4use bitflags::bitflags;
5use nix::errno::Errno;
6use std::fs::{File, OpenOptions};
7use std::io::ErrorKind;
8use std::os::fd::AsRawFd;
9
10const UFFD_DEVICE_PATH: &str = "/dev/userfaultfd";
11
12cfg_if::cfg_if! {
13 if #[cfg(any(feature = "linux5_7", feature = "linux4_14"))] {
14 bitflags! {
15 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17 pub struct FeatureFlags: u64 {
18 const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
19 const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
20 const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
21 const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
22 const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
23 const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
24 const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
25 const SIGBUS = raw::UFFD_FEATURE_SIGBUS;
26 const THREAD_ID = raw::UFFD_FEATURE_THREAD_ID;
27 }
28 }
29 } else {
30 bitflags! {
31 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
33 pub struct FeatureFlags: u64 {
34 const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
35 const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
36 const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
37 const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
38 const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
39 const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
40 const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
41 }
42 }
43 }
44}
45pub struct UffdBuilder {
58 close_on_exec: bool,
59 non_blocking: bool,
60 user_mode_only: bool,
61 req_features: FeatureFlags,
62 req_ioctls: IoctlFlags,
63}
64
65impl UffdBuilder {
66 pub fn new() -> UffdBuilder {
69 UffdBuilder {
70 close_on_exec: false,
71 non_blocking: false,
72 user_mode_only: true,
73 req_features: FeatureFlags::empty(),
74 req_ioctls: IoctlFlags::empty(),
75 }
76 }
77
78 pub fn close_on_exec(&mut self, close_on_exec: bool) -> &mut Self {
81 self.close_on_exec = close_on_exec;
82 self
83 }
84
85 pub fn non_blocking(&mut self, non_blocking: bool) -> &mut Self {
90 self.non_blocking = non_blocking;
91 self
92 }
93
94 pub fn user_mode_only(&mut self, user_mode_only: bool) -> &mut Self {
103 self.user_mode_only = user_mode_only;
104 self
105 }
106
107 pub fn require_features(&mut self, feature: FeatureFlags) -> &mut Self {
111 self.req_features |= feature;
112 self
113 }
114
115 pub fn require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self {
119 self.req_ioctls |= ioctls;
120 self
121 }
122
123 fn uffd_from_dev(&self, file: &mut File, flags: i32) -> Result<Uffd> {
124 match unsafe { raw::new_uffd(file.as_raw_fd(), flags) } {
125 Err(err) => Err(err.into()),
126 Ok(fd) => Ok(Uffd { fd }),
127 }
128 }
129
130 fn uffd_from_syscall(&self, flags: i32) -> Result<Uffd> {
131 let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
132 Ok(fd) => fd,
133 Err(Errno::EINVAL) if self.user_mode_only => Errno::result(unsafe {
137 raw::userfaultfd(flags & !raw::UFFD_USER_MODE_ONLY as i32)
138 })?,
139 Err(e) => return Err(e.into()),
140 };
141
142 Ok(Uffd { fd })
144 }
145
146 fn open_file_descriptor(&self, flags: i32) -> Result<Uffd> {
149 match OpenOptions::new()
154 .read(true)
155 .write(true)
156 .open(UFFD_DEVICE_PATH)
157 {
158 Ok(mut file) => self.uffd_from_dev(&mut file, flags),
159 Err(err) if err.kind() == ErrorKind::NotFound => self.uffd_from_syscall(flags),
160 Err(err) => Err(Error::OpenDevUserfaultfd(err)),
161 }
162 }
163
164 pub fn create(&self) -> Result<Uffd> {
166 let mut flags = 0;
168 if self.close_on_exec {
169 flags |= libc::O_CLOEXEC;
170 }
171 if self.non_blocking {
172 flags |= libc::O_NONBLOCK;
173 }
174
175 if self.user_mode_only {
176 flags |= raw::UFFD_USER_MODE_ONLY as i32;
177 }
178
179 let uffd = self.open_file_descriptor(flags)?;
180
181 let mut api = raw::uffdio_api {
183 api: raw::UFFD_API,
184 features: self.req_features.bits(),
185 ioctls: 0,
186 };
187 unsafe {
188 raw::api(uffd.fd, &mut api as *mut raw::uffdio_api)?;
189 }
190 let supported = IoctlFlags::from_bits_retain(api.ioctls);
191 if !supported.contains(self.req_ioctls) {
192 Err(Error::UnsupportedIoctls(supported))
193 } else {
194 Ok(uffd)
195 }
196 }
197}