a653rs_linux_core/
file.rs1use std::marker::PhantomData;
3use std::mem::{size_of, MaybeUninit};
4use std::os::unix::prelude::{AsRawFd, FileExt, IntoRawFd, RawFd};
5
6use anyhow::{anyhow, Result};
7use memfd::{FileSeal, Memfd, MemfdOptions};
8use memmap2::{Mmap, MmapMut};
9use nix::unistd::{close, dup};
10use procfs::process::{FDTarget, Process};
11
12use crate::error::{ResultExt, SystemError, TypedError, TypedResult};
13use crate::shmem::{TypedMmap, TypedMmapMut};
14
15#[derive(Debug, Clone, Copy)]
16pub struct TempFile<T: Send + Clone + Sized> {
18 fd: RawFd,
20 _p: PhantomData<T>,
21}
22
23impl<T: Send + Clone + Sized> TempFile<T> {
24 pub fn create<N: AsRef<str>>(name: N) -> TypedResult<Self> {
26 trace!("Create TempFile \"{}\"", name.as_ref());
27 let mem = MemfdOptions::default()
28 .close_on_exec(false)
29 .allow_sealing(true)
30 .create(name)
31 .typ(SystemError::Panic)?;
32 mem.as_file()
33 .set_len(
34 size_of::<T>()
35 .try_into()
36 .expect("Could not fit usize into u64"),
37 )
38 .typ(SystemError::Panic)?;
39 mem.add_seals(&[FileSeal::SealShrink, FileSeal::SealGrow])
40 .typ(SystemError::Panic)?;
41
42 Ok(Self {
43 fd: mem.into_raw_fd(),
44 _p: PhantomData,
45 })
46 }
47
48 fn get_memfd(&self) -> TypedResult<Memfd> {
50 let fd = dup(self.fd).typ(SystemError::Panic)?;
52 Memfd::try_from_fd(fd)
53 .map_err(|e| {
54 close(fd).ok();
55 anyhow!("Could not get Memfd from {e:#?}")
56 })
57 .typ(SystemError::Panic)
58 }
59
60 pub fn seal_read_only(&self) -> TypedResult<TypedMmapMut<T>> {
62 let mmap = self.get_typed_mmap_mut()?;
63
64 self.get_memfd()?
65 .add_seals(&[FileSeal::SealFutureWrite, FileSeal::SealSeal])
66 .typ(SystemError::Panic)?;
67
68 Ok(mmap)
69 }
70
71 pub fn fd(&self) -> RawFd {
73 self.fd
74 }
75
76 pub fn write(&self, value: &T) -> TypedResult<()> {
80 let bytes =
82 unsafe { std::slice::from_raw_parts(value as *const T as *const u8, size_of::<T>()) };
83 let file = self.get_memfd()?.into_file();
84 file.write_all_at(bytes, 0)
85 .map_err(anyhow::Error::from)
86 .typ(SystemError::Panic)
87 }
88
89 pub fn read(&self) -> TypedResult<T> {
92 let mut data = MaybeUninit::<T>::uninit();
94 let bytes_required = size_of::<T>();
95
96 let buf =
99 unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, size_of::<T>()) };
100
101 let file = self.get_memfd()?.into_file();
102
103 let bytes_read = file.read_at(buf, 0).typ(SystemError::Panic)?;
105
106 trace!("read {bytes_read} bytes from memfd {}", self.fd);
107 if bytes_read != bytes_required {
108 warn!(
109 "initialized {} ({bytes_required} bytes in size) with {bytes_read} bytes originating from memfd {}",
110 std::any::type_name::<T>(), self.fd()
111 );
112 }
113
114 Ok(unsafe { data.assume_init() })
115 }
116
117 pub fn get_typed_mmap_mut(&self) -> TypedResult<TypedMmapMut<T>> {
119 let fd = dup(self.fd).typ(SystemError::Panic)?;
120 unsafe {
121 MmapMut::map_mut(fd)
122 .map_err(|e| {
123 close(fd).ok();
124 anyhow!("Could not get Mmap from {e:#?}")
125 })
126 .typ(SystemError::Panic)?
127 .try_into()
128 }
129 }
130
131 pub fn get_typed_mmap(&self) -> TypedResult<TypedMmap<T>> {
133 let fd = dup(self.fd).typ(SystemError::Panic)?;
134 unsafe {
135 Mmap::map(fd)
136 .map_err(|e| {
137 close(fd).ok();
138 anyhow!("Could not get Mmap from {e:#?}")
139 })
140 .typ(SystemError::Panic)?
141 .try_into()
142 }
143 }
144}
145
146impl<T: Send + Clone> TryFrom<RawFd> for TempFile<T> {
147 type Error = TypedError;
148
149 fn try_from(fd: RawFd) -> Result<Self, Self::Error> {
150 let tf = Self {
151 fd,
152 _p: PhantomData,
153 };
154 let memfd = tf.get_memfd()?;
155 trace!("Got Memfd from {fd}. Seals: {:?}", memfd.seals());
156 Ok(tf)
157 }
158}
159
160impl<T: Send + Clone + Sized> AsRawFd for TempFile<T> {
161 fn as_raw_fd(&self) -> RawFd {
162 self.fd
163 }
164}
165
166pub fn get_memfd(name: &str) -> TypedResult<i32> {
169 let name = format!("memfd:{name}");
170 Process::myself()
171 .typ(SystemError::Panic)?
172 .fd()
173 .typ(SystemError::Panic)?
174 .flatten()
175 .find_map(|f| {
176 if let FDTarget::Path(p) = &f.target {
177 if p.to_str().expect("Got non-UTF-8 String").contains(&name) {
178 Some(f.fd)
179 } else {
180 None
181 }
182 } else {
183 None
184 }
185 })
186 .ok_or_else(|| anyhow!("No File Descriptor with Name: {name}"))
187 .typ(SystemError::Panic)
188}