memfd/
memfd.rs

1use crate::sealing;
2use rustix::fs::{MemfdFlags, SealFlags};
3use std::fs;
4use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
5
6/// A `Memfd` builder, providing advanced options and flags for specifying its behavior.
7#[derive(Clone, Debug)]
8pub struct MemfdOptions {
9    allow_sealing: bool,
10    cloexec: bool,
11    hugetlb: Option<HugetlbSize>,
12}
13
14impl MemfdOptions {
15    /// Default set of options for `Memfd` creation.
16    ///
17    /// The default options are:
18    ///  * [`FileSeal::SealSeal`] (i.e. no further sealing);
19    ///  * close-on-exec is enabled;
20    ///  * hugetlb is disabled.
21    ///
22    /// [`FileSeal::SealSeal`]: sealing::FileSeal::SealSeal
23    pub const fn new() -> Self {
24        Self {
25            allow_sealing: false,
26            cloexec: true,
27            hugetlb: None,
28        }
29    }
30
31    /// Whether to allow adding seals to the created `Memfd`.
32    pub const fn allow_sealing(mut self, value: bool) -> Self {
33        self.allow_sealing = value;
34        self
35    }
36
37    /// Whether to set the `FD_CLOEXEC` flag on the created `Memfd`.
38    pub const fn close_on_exec(mut self, value: bool) -> Self {
39        self.cloexec = value;
40        self
41    }
42
43    /// Optional hugetlb support and page size for the created `Memfd`.
44    pub const fn hugetlb(mut self, size: Option<HugetlbSize>) -> Self {
45        self.hugetlb = size;
46        self
47    }
48
49    /// Translate the current options into a bitflags value for `memfd_create`.
50    fn bitflags(&self) -> MemfdFlags {
51        let mut bits = MemfdFlags::empty();
52        if self.allow_sealing {
53            bits |= MemfdFlags::ALLOW_SEALING;
54        }
55        if self.cloexec {
56            bits |= MemfdFlags::CLOEXEC;
57        }
58        if let Some(ref hugetlb) = self.hugetlb {
59            bits |= hugetlb.bitflags();
60            bits |= MemfdFlags::HUGETLB;
61        }
62        bits
63    }
64
65    /// Create a [`Memfd`] according to configuration.
66    ///
67    /// [`Memfd`]: Memfd
68    pub fn create<T: AsRef<str>>(&self, name: T) -> Result<Memfd, crate::Error> {
69        let flags = self.bitflags();
70        let fd = rustix::fs::memfd_create(name.as_ref(), flags)
71            .map_err(Into::into)
72            .map_err(crate::Error::Create)?;
73        Ok(Memfd { file: fd.into() })
74    }
75}
76
77impl Default for MemfdOptions {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83/// Page size for a hugetlb anonymous file.
84#[derive(Copy, Clone, Debug)]
85pub enum HugetlbSize {
86    /// 64KB hugetlb page.
87    Huge64KB,
88    /// 64KB hugetlb page.
89    Huge512KB,
90    /// 1MB hugetlb page.
91    Huge1MB,
92    /// 2MB hugetlb page.
93    Huge2MB,
94    /// 8MB hugetlb page.
95    Huge8MB,
96    /// 16MB hugetlb page.
97    Huge16MB,
98    /// 256MB hugetlb page.
99    Huge256MB,
100    /// 1GB hugetlb page.
101    Huge1GB,
102    /// 2GB hugetlb page.
103    Huge2GB,
104    /// 16GB hugetlb page.
105    Huge16GB,
106}
107
108impl HugetlbSize {
109    const fn bitflags(self) -> MemfdFlags {
110        match self {
111            Self::Huge64KB => MemfdFlags::HUGE_64KB,
112            Self::Huge512KB => MemfdFlags::HUGE_512KB,
113            Self::Huge1MB => MemfdFlags::HUGE_1MB,
114            Self::Huge2MB => MemfdFlags::HUGE_2MB,
115            Self::Huge8MB => MemfdFlags::HUGE_8MB,
116            Self::Huge16MB => MemfdFlags::HUGE_16MB,
117            Self::Huge256MB => MemfdFlags::HUGE_256MB,
118            Self::Huge1GB => MemfdFlags::HUGE_1GB,
119            Self::Huge2GB => MemfdFlags::HUGE_2GB,
120            Self::Huge16GB => MemfdFlags::HUGE_16GB,
121        }
122    }
123}
124
125/// An anonymous volatile file, with sealing capabilities.
126#[derive(Debug)]
127pub struct Memfd {
128    file: fs::File,
129}
130
131impl Memfd {
132    /// Try to convert an object that owns a file descriptor into a `Memfd`.
133    ///
134    /// This function consumes the ownership of the specified object. If the underlying
135    /// file-descriptor is compatible with memfd/sealing, a `Memfd` object is returned.
136    /// Otherwise the supplied object is returned as error.
137    pub fn try_from_fd<F>(fd: F) -> Result<Self, F>
138    where
139        F: AsRawFd + IntoRawFd,
140    {
141        if is_memfd(&fd) {
142            // SAFETY: from_raw_fd requires a valid, uniquely owned file descriptor.
143            // The IntoRawFd trait guarantees both conditions.
144            let file = unsafe { fs::File::from_raw_fd(fd.into_raw_fd()) };
145            Ok(Self { file })
146        } else {
147            Err(fd)
148        }
149    }
150
151    /// Try to convert a [`File`] object into a `Memfd`.
152    ///
153    /// This function consumes the ownership of the specified `File`.  If the underlying
154    /// file-descriptor is compatible with memfd/sealing, a `Memfd` object is returned.
155    /// Otherwise the supplied `File` is returned for further usage.
156    ///
157    /// [`File`]: fs::File
158    pub fn try_from_file(file: fs::File) -> Result<Self, fs::File> {
159        Self::try_from_fd(file)
160    }
161
162    /// Return a reference to the backing [`File`].
163    ///
164    /// [`File`]: fs::File
165    pub const fn as_file(&self) -> &fs::File {
166        &self.file
167    }
168
169    /// Convert `Memfd` to the backing [`File`].
170    ///
171    /// [`File`]: fs::File
172    pub fn into_file(self) -> fs::File {
173        self.file
174    }
175
176    /// Obtain the current set of seals for the `Memfd`.
177    pub fn seals(&self) -> Result<sealing::SealsHashSet, crate::Error> {
178        let flags = Self::file_get_seals(&self.file)?;
179        Ok(sealing::bitflags_to_seals(flags))
180    }
181
182    /// Add a seal to the existing set of seals.
183    pub fn add_seal(&self, seal: sealing::FileSeal) -> Result<(), crate::Error> {
184        let flags = seal.bitflags();
185        self.add_seal_flags(flags)
186    }
187
188    /// Add some seals to the existing set of seals.
189    pub fn add_seals<'a>(
190        &self,
191        seals: impl IntoIterator<Item = &'a sealing::FileSeal>,
192    ) -> Result<(), crate::Error> {
193        let flags = sealing::seals_to_bitflags(seals);
194        self.add_seal_flags(flags)
195    }
196
197    fn add_seal_flags(&self, flags: rustix::fs::SealFlags) -> Result<(), crate::Error> {
198        rustix::fs::fcntl_add_seals(&self.file, flags)
199            .map_err(Into::into)
200            .map_err(crate::Error::AddSeals)?;
201        Ok(())
202    }
203
204    /// Return the current sealing bitflags.
205    fn file_get_seals(fp: &fs::File) -> Result<SealFlags, crate::Error> {
206        let r = rustix::fs::fcntl_get_seals(fp)
207            .map_err(Into::into)
208            .map_err(crate::Error::GetSeals)?;
209        Ok(r)
210    }
211}
212
213impl FromRawFd for Memfd {
214    /// Convert a raw file-descriptor to a [`Memfd`].
215    ///
216    /// This function consumes ownership of the specified file descriptor. `Memfd` will take
217    /// responsibility for closing it when the object goes out of scope.
218    ///
219    /// # Safety
220    ///
221    /// `fd` must be a valid file descriptor representing a memfd file.
222    ///
223    /// [`Memfd`]: Memfd
224    unsafe fn from_raw_fd(fd: RawFd) -> Self {
225        let file = fs::File::from_raw_fd(fd);
226        Self { file }
227    }
228}
229
230impl AsRawFd for Memfd {
231    fn as_raw_fd(&self) -> RawFd {
232        self.file.as_raw_fd()
233    }
234}
235
236impl IntoRawFd for Memfd {
237    fn into_raw_fd(self) -> RawFd {
238        self.into_file().into_raw_fd()
239    }
240}
241
242/// Check if a file descriptor is a memfd.
243///
244/// Implemented by trying to retrieve the seals.
245/// If that fails, the fd is not a memfd.
246fn is_memfd<F: AsRawFd>(fd: &F) -> bool {
247    // SAFETY: For now, we trust the file descriptor returned by `as_raw_fd()`
248    // is valid. Once `AsFd` is stabilized in std, we can use that instead of
249    // `AsRawFd`, and eliminate this `unsafe` block.
250    let fd = unsafe { rustix::fd::BorrowedFd::borrow_raw(fd.as_raw_fd()) };
251    rustix::fs::fcntl_get_seals(fd).is_ok()
252}