1use std::{
2 path::{Path, PathBuf},
3 sync::OnceLock,
4};
5
6use libc::mode_t;
7use monitor_api::CompartmentHandle;
8use secgate::{
9 util::{Descriptor, Handle, SimpleBuffer},
10 DynamicSecGate,
11};
12use twizzler_abi::object::ObjID;
13use twizzler_rt_abi::{error::TwzError, object::MapFlags, Result};
14
15struct PagerAPI {
16 _handle: &'static CompartmentHandle,
17 open_handle: DynamicSecGate<'static, (), (Descriptor, ObjID)>,
18 close_handle: DynamicSecGate<'static, (Descriptor,), ()>,
19 enumerate_external: DynamicSecGate<'static, (Descriptor, ObjID, usize, usize), usize>,
20 lookup_external: DynamicSecGate<'static, (Descriptor, ObjID, usize), usize>,
21 create_external:
22 DynamicSecGate<'static, (Descriptor, ObjID, mode_t, usize, Option<ObjID>), usize>,
23 unlink_external: DynamicSecGate<'static, (Descriptor, ObjID, usize), ()>,
24 readlink_external: DynamicSecGate<'static, (Descriptor, ObjID), usize>,
25}
26
27static PAGER_API: OnceLock<PagerAPI> = OnceLock::new();
28
29fn pager_api() -> &'static PagerAPI {
30 PAGER_API.get_or_init(|| {
31 let handle = Box::leak(Box::new(
32 CompartmentHandle::lookup("pager-srv").expect("failed to open pager compartment"),
33 ));
34 let open_handle = unsafe {
35 handle
36 .dynamic_gate("pager_open_handle")
37 .expect("failed to find open handle gate call")
38 };
39 let close_handle = unsafe {
40 handle
41 .dynamic_gate("pager_close_handle")
42 .expect("failed to find close handle gate call")
43 };
44 let enumerate_external = unsafe {
45 handle
46 .dynamic_gate("pager_enumerate_external")
47 .expect("failed to find enumerate external gate call")
48 };
49 let lookup_external = unsafe {
50 handle
51 .dynamic_gate("pager_lookup_external")
52 .expect("failed to find lookup external gate call")
53 };
54 let create_external = unsafe {
55 handle
56 .dynamic_gate("pager_create_external")
57 .expect("failed to find create external gate call")
58 };
59 let unlink_external = unsafe {
60 handle
61 .dynamic_gate("pager_unlink_external")
62 .expect("failed to find unlink external gate call")
63 };
64 let readlink_external = unsafe {
65 handle
66 .dynamic_gate("pager_readlink_external")
67 .expect("failed to find unlink external gate call")
68 };
69 PagerAPI {
70 _handle: handle,
71 open_handle,
72 close_handle,
73 enumerate_external,
74 lookup_external,
75 create_external,
76 unlink_external,
77 readlink_external,
78 }
79 })
80}
81
82pub struct PagerHandle {
83 desc: Descriptor,
84 buffer: SimpleBuffer,
85}
86
87impl Handle for PagerHandle {
88 type OpenError = TwzError;
89
90 type OpenInfo = ();
91
92 fn open(_info: Self::OpenInfo) -> Result<Self>
93 where
94 Self: Sized,
95 {
96 let (desc, id) = (pager_api().open_handle)()?;
97 let handle =
98 twizzler_rt_abi::object::twz_rt_map_object(id, MapFlags::READ | MapFlags::WRITE)?;
99 let sb = SimpleBuffer::new(handle);
100 Ok(Self { desc, buffer: sb })
101 }
102
103 fn release(&mut self) {
104 let _ = (pager_api().close_handle)(self.desc);
105 }
106}
107
108impl Drop for PagerHandle {
110 fn drop(&mut self) {
111 self.release()
112 }
113}
114
115fn get_external_file_from_sb(sb: &SimpleBuffer, offset: usize) -> Option<(ExternalFile, usize)> {
116 let mut file = std::mem::MaybeUninit::<ExternalFileSbHdr>::uninit();
117 let ptr = file.as_mut_ptr().cast::<u8>();
118 let slice =
119 unsafe { core::slice::from_raw_parts_mut(ptr, std::mem::size_of::<ExternalFileSbHdr>()) };
120 let thislen = sb.read_offset(slice, offset);
121
122 if thislen < std::mem::size_of::<ExternalFileSbHdr>() {
123 return None;
124 }
125
126 let file = unsafe { file.assume_init() };
127
128 let mut pathbuf = [0u8; MAX_EXTERNAL_PATH];
129 let pathlen = sb.read_offset(&mut pathbuf[0..(file.pathlen as usize)], offset + thislen);
130
131 if pathlen < file.pathlen as usize {
132 return None;
133 }
134
135 Some((
136 ExternalFile::new(
137 unsafe { str::from_utf8_unchecked(&pathbuf[0..pathlen]) },
138 file.kind,
139 file.id,
140 ),
141 thislen + pathlen,
142 ))
143}
144
145impl PagerHandle {
146 pub fn new() -> Option<Self> {
148 Self::open(()).ok()
149 }
150
151 pub fn readlink_external(&mut self, id: ObjID) -> Result<String> {
152 let len = (pager_api().readlink_external)(self.desc, id)?;
153 let mut v = vec![0; len];
154 self.buffer.read(&mut v);
155 String::from_utf8(v).map_err(|_| TwzError::INVALID_ARGUMENT)
156 }
157
158 pub fn unlink_external(&mut self, id: ObjID, name: impl AsRef<Path>) -> Result<()> {
159 let name = name.as_ref().as_os_str().as_encoded_bytes();
160 if name.len() > NAME_MAX {
161 return Err(TwzError::INVALID_ARGUMENT);
162 }
163 let namelen = self.buffer.write(name);
164
165 (pager_api().unlink_external)(self.desc, id, namelen)
166 }
167
168 pub fn create_external_file(
169 &mut self,
170 dir: ObjID,
171 name: impl AsRef<Path>,
172 link_to: Option<ObjID>,
173 mode: mode_t,
174 ) -> Result<ExternalFile> {
175 let name = name.as_ref().as_os_str().as_encoded_bytes();
176 if name.len() > NAME_MAX {
177 return Err(TwzError::INVALID_ARGUMENT);
178 }
179 let namelen = self.buffer.write(name);
180
181 let _filelen = (pager_api().create_external)(self.desc, dir, mode, namelen, link_to)?;
182
183 get_external_file_from_sb(&self.buffer, 0)
184 .ok_or(TwzError::INVALID_ARGUMENT)
185 .map(|x| x.0)
186 }
187
188 pub fn lookup_external(&mut self, dir: ObjID, name: impl AsRef<Path>) -> Result<ExternalFile> {
189 let name = name.as_ref().as_os_str().as_encoded_bytes();
190 if name.len() > NAME_MAX {
191 return Err(TwzError::INVALID_ARGUMENT);
192 }
193 let namelen = self.buffer.write(name);
194
195 let _filelen = (pager_api().lookup_external)(self.desc, dir, namelen)?;
196
197 get_external_file_from_sb(&self.buffer, 0)
198 .ok_or(TwzError::INVALID_ARGUMENT)
199 .map(|x| x.0)
200 }
201
202 pub fn enumerate_external(
203 &mut self,
204 id: ObjID,
205 entries: &mut Vec<ExternalFile>,
206 skip: usize,
207 count: usize,
208 ) -> Result<()> {
209 let len = (pager_api().enumerate_external)(self.desc, id, skip, count)?;
210
211 let mut off = 0;
212 entries.clear();
213 while off < len {
214 let Some(file) = get_external_file_from_sb(&self.buffer, off) else {
215 break;
216 };
217 entries.push(file.0);
218
219 off += file.1;
220 }
221 Ok(())
222 }
223}
224
225pub fn objid_to_ino(id: u128) -> Option<u32> {
226 if id == 1 {
227 return Some(0);
228 };
229 let (hi, lo) = ((id >> 64) as u64, id as u64);
230 if hi == (1u64 << 63) {
231 let ino = lo & !(1u64 << 63);
232 Some(ino as u32)
233 } else {
234 None
235 }
236}
237
238pub fn ino_to_objid(ino: u32) -> u128 {
239 if ino == 0 {
240 return 1;
241 }
242 (1u128 << 127) | (ino as u128) | (1u128 << 63)
243}
244
245pub const MAX_EXTERNAL_PATH: usize = 4096;
246pub const NAME_MAX: usize = 256;
247
248#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
249pub struct ExternalFile {
250 pub id: u128,
251 pub path: PathBuf,
252 pub kind: ExternalKind,
253}
254
255impl ExternalFile {
256 pub fn new(path: impl AsRef<std::path::Path>, kind: ExternalKind, id: u128) -> Self {
257 Self {
258 id,
259 path: path.as_ref().to_path_buf(),
260 kind,
261 }
262 }
263
264 pub fn name(&self) -> Option<&str> {
265 self.path.to_str()
266 }
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
270#[repr(u32)]
271pub enum ExternalKind {
272 Regular,
273 Directory,
274 SymLink,
275 Other,
276}
277
278pub struct ExternalFileSbHdr {
279 pub id: u128,
280 pub kind: ExternalKind,
281 pub pathlen: u32,
282}