pager_dynamic/
lib.rs

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
108// On drop, release the handle.
109impl 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    /// Open a new logging handle.
147    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}