Skip to main content

firefly_sudo/
sudo.rs

1//! Functions available only to privileged apps.
2
3#[cfg(feature = "alloc")]
4use alloc::boxed::Box;
5#[cfg(feature = "alloc")]
6use alloc::vec;
7use firefly_rust::*;
8
9#[cfg(feature = "alloc")]
10pub struct DirBuf {
11    raw: Box<[u8]>,
12}
13
14#[cfg(feature = "alloc")]
15impl DirBuf {
16    /// List all subdirectories in the given directory.
17    #[must_use]
18    pub fn list_dirs(name: &str) -> Self {
19        let size = Dir::list_dirs_buf_size(name);
20        let mut buf = vec![0; size];
21        Dir::list_dirs(name, &mut buf);
22        Self {
23            raw: buf.into_boxed_slice(),
24        }
25    }
26
27    /// List all files in the given directory.
28    #[must_use]
29    pub fn list_files(name: &str) -> Self {
30        let size = Dir::list_files_buf_size(name);
31        let mut buf = vec![0; size];
32        Dir::list_files(name, &mut buf);
33        Self {
34            raw: buf.into_boxed_slice(),
35        }
36    }
37
38    /// Iterate over all loaded entries in the directory.
39    #[must_use]
40    pub fn iter(&self) -> DirIter<'_> {
41        DirIter { raw: &self.raw }
42    }
43}
44
45pub struct Dir<'a> {
46    raw: &'a [u8],
47}
48
49impl<'a> Dir<'a> {
50    #[must_use]
51    pub fn list_dirs_buf_size(name: &str) -> usize {
52        let path_ptr = name.as_ptr();
53        let size = unsafe { b::list_dirs_buf_size(path_ptr as u32, name.len() as u32) };
54        size as usize
55    }
56
57    #[must_use]
58    pub fn list_files_buf_size(name: &str) -> usize {
59        let path_ptr = name.as_ptr();
60        let size = unsafe { b::list_files_buf_size(path_ptr as u32, name.len() as u32) };
61        size as usize
62    }
63
64    pub fn list_dirs(name: &str, buf: &'a mut [u8]) -> Self {
65        let path_ptr = name.as_ptr();
66        let buf_ptr = buf.as_mut_ptr();
67        unsafe {
68            b::list_dirs(
69                path_ptr as u32,
70                name.len() as u32,
71                buf_ptr as u32,
72                buf.len() as u32,
73            );
74        }
75        Self { raw: buf }
76    }
77
78    pub fn list_files(name: &str, buf: &'a mut [u8]) -> Self {
79        let path_ptr = name.as_ptr();
80        let buf_ptr = buf.as_mut_ptr();
81        unsafe {
82            b::list_files(
83                path_ptr as u32,
84                name.len() as u32,
85                buf_ptr as u32,
86                buf.len() as u32,
87            );
88        }
89        Self { raw: buf }
90    }
91
92    /// Iterate over all loaded entries in the directory.
93    #[must_use]
94    pub const fn iter(&self) -> DirIter<'a> {
95        DirIter { raw: self.raw }
96    }
97}
98
99pub struct DirIter<'a> {
100    raw: &'a [u8],
101}
102
103impl<'a> Iterator for DirIter<'a> {
104    type Item = &'a str;
105
106    fn next(&mut self) -> Option<Self::Item> {
107        let len = self.raw.first()?;
108        let len = *len as usize;
109        let raw_name = self.raw.get(1..=len)?;
110        // Security: do NOT return None on invalid string. Otherwise,
111        // adding an invalid name into the directory would hide all files
112        // that go after that.
113        let name = core::str::from_utf8(raw_name).unwrap_or("");
114        self.raw = self.raw.get((len + 1)..).unwrap_or(&[]);
115        Some(name)
116    }
117}
118
119/// Tell runtime to exit the current app and replace it with the given one.
120///
121/// The exit will be executed after the current frame is rendered.
122/// Calling this function does not interrup the current app execution.
123pub fn run_app(author_id: &str, app_id: &str) {
124    let author_ptr = author_id.as_ptr() as u32;
125    let app_ptr = app_id.as_ptr() as u32;
126    let author_len = author_id.len() as u32;
127    let app_len = app_id.len() as u32;
128    unsafe {
129        b::run_app(author_ptr, author_len, app_ptr, app_len);
130    }
131}
132
133#[must_use]
134pub fn get_file_size(path: &str) -> usize {
135    let path_ptr = path.as_ptr() as u32;
136    let path_len = path.len() as u32;
137    let size = unsafe { b::get_file_size(path_ptr, path_len) };
138    size as usize
139}
140
141/// Read a file from the filesystem into the buffer.
142///
143/// The path must be relative to the root of the FS.
144/// For example: `sys/launcher`.
145pub fn load_file<'a>(path: &str, buf: &'a mut [u8]) -> FileRef<'a> {
146    let path_ptr = path.as_ptr() as u32;
147    let path_len = path.len() as u32;
148    let buf_ptr = buf.as_mut_ptr() as u32;
149    let buf_len = buf.len() as u32;
150    unsafe {
151        b::load_file(path_ptr, path_len, buf_ptr, buf_len);
152    }
153    unsafe { FileRef::from_bytes(buf) }
154}
155
156/// Like [`load_file`] but takes care of the buffer allocation and ownership.
157///
158/// [`None`] is returned if the file does not exist.
159#[cfg(feature = "alloc")]
160#[must_use]
161pub fn load_file_buf(path: &str) -> Option<FileBuf> {
162    let size = get_file_size(path);
163    if size == 0 {
164        return None;
165    }
166    let mut buf = vec![0u8; size];
167    let path_ptr = path.as_ptr() as u32;
168    let path_len = path.len() as u32;
169    let buf_ptr = buf.as_mut_ptr() as u32;
170    let buf_len = buf.len() as u32;
171    unsafe {
172        b::load_file(path_ptr, path_len, buf_ptr, buf_len);
173    }
174    let buf = buf.into_boxed_slice();
175    Some(unsafe { FileBuf::from_bytes(buf) })
176}
177
178pub fn dump_file(path: &str, buf: &[u8]) {
179    let path_ptr = path.as_ptr() as u32;
180    let path_len = path.len() as u32;
181    let buf_ptr = buf.as_ptr() as u32;
182    let buf_len = buf.len() as u32;
183    unsafe {
184        b::dump_file(path_ptr, path_len, buf_ptr, buf_len);
185    }
186}
187
188pub fn append_file(path: &str, buf: &[u8]) {
189    let path_ptr = path.as_ptr() as u32;
190    let path_len = path.len() as u32;
191    let buf_ptr = buf.as_ptr() as u32;
192    let buf_len = buf.len() as u32;
193    unsafe {
194        b::append_file(path_ptr, path_len, buf_ptr, buf_len);
195    }
196}
197
198pub fn remove_file(path: &str) {
199    let path_ptr = path.as_ptr() as u32;
200    let path_len = path.len() as u32;
201    unsafe {
202        b::remove_file(path_ptr, path_len);
203    }
204}
205
206pub fn remove_dir(path: &str) {
207    let path_ptr = path.as_ptr() as u32;
208    let path_len = path.len() as u32;
209    unsafe {
210        b::remove_dir(path_ptr, path_len);
211    }
212}
213
214pub fn create_dir(path: &str) {
215    let path_ptr = path.as_ptr() as u32;
216    let path_len = path.len() as u32;
217    unsafe {
218        b::create_dir(path_ptr, path_len);
219    }
220}
221
222/// Low-level bindings for host-defined "sudo" module.
223mod b {
224    #[link(wasm_import_module = "sudo")]
225    unsafe extern "C" {
226        pub(super) fn list_dirs_buf_size(path_ptr: u32, path_len: u32) -> u32;
227        pub(super) fn list_dirs(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
228        pub(super) fn list_files_buf_size(path_ptr: u32, path_len: u32) -> u32;
229        pub(super) fn list_files(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
230        pub(super) fn run_app(author_ptr: u32, author_len: u32, app_ptr: u32, app_len: u32);
231        pub(super) fn get_file_size(path_ptr: u32, path_len: u32) -> u32;
232        pub(super) fn load_file(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
233        pub(super) fn dump_file(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
234        pub(super) fn append_file(path_ptr: u32, path_len: u32, buf_ptr: u32, buf_len: u32) -> u32;
235        pub(super) fn remove_file(path_ptr: u32, path_len: u32);
236        pub(super) fn remove_dir(path_ptr: u32, path_len: u32);
237        pub(super) fn create_dir(path_ptr: u32, path_len: u32);
238    }
239}