moto_rt/
fs.rs

1//! Filesystem RT API.
2//!
3//! While it would be great to expose something more interesting,
4//! Rust's FS PAL is a thin wrapper around POSIX FS API, and a lot
5//! of popular third-party crates assume POSIXy FS API, and
6//! when Motor OS later adds a libc, or another compatibility
7//! layer with another language/system, it will also have to
8//! expose a flavor of POSIXy FS API, so we don't try to be too
9//! different here and expose a POSIXy FS API.
10
11use crate::Error;
12use crate::Result;
13use crate::RtFd;
14use crate::RtVdsoVtable;
15use crate::error::*;
16use crate::into_result;
17use core::sync::atomic::Ordering;
18
19#[cfg(not(feature = "rustc-dep-of-std"))]
20extern crate alloc;
21
22pub const TEMP_DIR: &str = "/sys/tmp";
23pub const HANDLE_URL_PREFIX: &str = "handle://";
24
25/// The maximum length of a file/directory absolute path, in bytes.
26///
27/// Relatively short so that two paths can fit into a page, with some
28/// extra fields (for RPC rename request). Although Linux and Windows
29/// have longer max path lengths, macOS has 1024, so 1024 should also
30/// be enough for Motor OS.
31pub const MAX_PATH_LEN: usize = 1024;
32/// The maximum length of a file/directory leaf name, in bytes.
33pub const MAX_FILENAME_LEN: usize = 256;
34/// The maximum length of a single file.
35pub const MAX_FILE_LEN: u64 = i64::MAX as u64;
36
37// File types.
38pub const FILETYPE_FILE: u8 = 1;
39pub const FILETYPE_DIRECTORY: u8 = 2;
40
41// File permissions.
42pub const PERM_READ: u64 = 1;
43pub const PERM_WRITE: u64 = 2;
44
45// Open options.
46pub const O_READ: u32 = 1 << 0;
47pub const O_WRITE: u32 = 1 << 1;
48pub const O_APPEND: u32 = 1 << 2;
49pub const O_TRUNCATE: u32 = 1 << 3;
50pub const O_CREATE: u32 = 1 << 4;
51pub const O_CREATE_NEW: u32 = 1 << 5;
52pub const O_NONBLOCK: u32 = 1 << 6;
53
54// When "opening a handle".
55pub const O_HANDLE_CHILD: u32 = 1;
56
57// Seek option.
58pub const SEEK_SET: u8 = 0;
59pub const SEEK_CUR: u8 = 1;
60pub const SEEK_END: u8 = 2;
61
62#[repr(C, align(16))]
63#[derive(Clone, Copy, PartialEq, Eq, Default)]
64pub struct FileAttr {
65    pub version: u64,
66    pub size: u64,
67    pub perm: u64,
68    pub file_type: u8,
69    pub _reserved: [u8; 7],
70    pub created: u128,
71    pub modified: u128,
72    pub accessed: u128,
73}
74
75impl FileAttr {
76    pub const VERSION: u64 = 1;
77
78    fn new() -> Self {
79        Self {
80            version: Self::VERSION,
81            ..Default::default()
82        }
83    }
84}
85
86/// Returned by readdir().
87#[repr(C, align(16))]
88#[derive(Clone, Copy)]
89pub struct DirEntry {
90    pub version: u64,
91    pub _reserved: u64,
92    pub attr: FileAttr,
93    pub fname_size: u16, // Filename only, without path/ancestors.
94    pub fname: [u8; MAX_FILENAME_LEN],
95}
96
97impl DirEntry {
98    pub const VERSION: u64 = 1;
99
100    fn new() -> Self {
101        Self {
102            version: Self::VERSION,
103            _reserved: 0,
104            attr: FileAttr::new(),
105            fname_size: 0,
106            fname: [0; MAX_FILENAME_LEN],
107        }
108    }
109}
110
111pub fn is_terminal(rt_fd: RtFd) -> bool {
112    let vdso_is_terminal: extern "C" fn(i32) -> i32 = unsafe {
113        core::mem::transmute(
114            RtVdsoVtable::get().fs_is_terminal.load(Ordering::Relaxed) as usize as *const (),
115        )
116    };
117
118    match vdso_is_terminal(rt_fd) {
119        0 => false,
120        1 => true,
121        _ => panic!(),
122    }
123}
124
125pub fn duplicate(rt_fd: RtFd) -> Result<RtFd> {
126    let vdso_duplicate: extern "C" fn(RtFd) -> RtFd = unsafe {
127        core::mem::transmute(
128            RtVdsoVtable::get().fs_duplicate.load(Ordering::Relaxed) as usize as *const (),
129        )
130    };
131
132    to_result!(vdso_duplicate(rt_fd))
133}
134
135/// Opens a file at `path` with options specified by `opts`.
136pub fn open(path: &str, opts: u32) -> Result<RtFd> {
137    let vdso_open: extern "C" fn(*const u8, usize, u32) -> i32 = unsafe {
138        core::mem::transmute(
139            RtVdsoVtable::get().fs_open.load(Ordering::Relaxed) as usize as *const ()
140        )
141    };
142
143    let bytes = path.as_bytes();
144    to_result!(vdso_open(bytes.as_ptr(), bytes.len(), opts))
145}
146
147pub fn close(rt_fd: RtFd) -> Result<()> {
148    let vdso_close: extern "C" fn(i32) -> ErrorCode = unsafe {
149        core::mem::transmute(
150            RtVdsoVtable::get().fs_close.load(Ordering::Relaxed) as usize as *const (),
151        )
152    };
153
154    into_result(vdso_close(rt_fd))
155}
156
157pub fn get_file_attr(rt_fd: RtFd) -> Result<FileAttr> {
158    let vdso_get_file_attr: extern "C" fn(i32, *mut FileAttr) -> ErrorCode = unsafe {
159        core::mem::transmute(
160            RtVdsoVtable::get().fs_get_file_attr.load(Ordering::Relaxed) as usize as *const (),
161        )
162    };
163
164    let mut attr = FileAttr::new();
165    into_result(vdso_get_file_attr(rt_fd, &mut attr))?;
166    Ok(attr)
167}
168
169pub fn fsync(rt_fd: RtFd) -> Result<()> {
170    let vdso_fsync: extern "C" fn(i32) -> ErrorCode = unsafe {
171        core::mem::transmute(
172            RtVdsoVtable::get().fs_fsync.load(Ordering::Relaxed) as usize as *const (),
173        )
174    };
175
176    into_result(vdso_fsync(rt_fd))
177}
178
179pub fn datasync(rt_fd: RtFd) -> Result<()> {
180    let vdso_datasync: extern "C" fn(i32) -> ErrorCode = unsafe {
181        core::mem::transmute(
182            RtVdsoVtable::get().fs_datasync.load(Ordering::Relaxed) as usize as *const (),
183        )
184    };
185
186    into_result(vdso_datasync(rt_fd))
187}
188
189pub fn truncate(rt_fd: RtFd, size: u64) -> Result<()> {
190    let vdso_truncate: extern "C" fn(i32, u64) -> ErrorCode = unsafe {
191        core::mem::transmute(
192            RtVdsoVtable::get().fs_truncate.load(Ordering::Relaxed) as usize as *const (),
193        )
194    };
195
196    into_result(vdso_truncate(rt_fd, size))
197}
198
199pub fn read(rt_fd: RtFd, buf: &mut [u8]) -> Result<usize> {
200    let vdso_read: extern "C" fn(i32, *mut u8, usize) -> i64 = unsafe {
201        core::mem::transmute(
202            RtVdsoVtable::get().fs_read.load(Ordering::Relaxed) as usize as *const ()
203        )
204    };
205
206    to_result!(vdso_read(rt_fd, buf.as_mut_ptr(), buf.len()))
207}
208
209pub fn read_vectored(rt_fd: RtFd, bufs: &mut [&mut [u8]]) -> Result<usize> {
210    use alloc::vec::Vec;
211
212    let vdso_read_vectored: extern "C" fn(i32, *const usize, usize) -> i64 = unsafe {
213        core::mem::transmute(
214            RtVdsoVtable::get().fs_read_vectored.load(Ordering::Relaxed) as usize as *const (),
215        )
216    };
217
218    // Pack: a vector of [addr, len].
219    let mut packed = Vec::with_capacity(bufs.len() * 2);
220    for buf in &*bufs {
221        let addr = buf.as_ptr() as usize;
222        let len = buf.len();
223        packed.push(addr);
224        packed.push(len);
225    }
226
227    to_result!(vdso_read_vectored(rt_fd, packed.as_ptr(), bufs.len()))
228}
229
230pub fn write(rt_fd: RtFd, buf: &[u8]) -> Result<usize> {
231    let vdso_write: extern "C" fn(i32, *const u8, usize) -> i64 = unsafe {
232        core::mem::transmute(
233            RtVdsoVtable::get().fs_write.load(Ordering::Relaxed) as usize as *const (),
234        )
235    };
236
237    to_result!(vdso_write(rt_fd, buf.as_ptr(), buf.len()))
238}
239
240pub fn write_vectored(rt_fd: RtFd, bufs: &[&[u8]]) -> Result<usize> {
241    use alloc::vec::Vec;
242
243    let vdso_write_vectored: extern "C" fn(i32, *const usize, usize) -> i64 = unsafe {
244        core::mem::transmute(
245            RtVdsoVtable::get()
246                .fs_write_vectored
247                .load(Ordering::Relaxed) as usize as *const (),
248        )
249    };
250
251    // Pack: a vector of [addr, len].
252    let mut packed = Vec::with_capacity(bufs.len() * 2);
253    #[allow(clippy::borrow_deref_ref)]
254    for buf in &*bufs {
255        let addr = buf.as_ptr() as usize;
256        let len = buf.len();
257        packed.push(addr);
258        packed.push(len);
259    }
260
261    to_result!(vdso_write_vectored(rt_fd, packed.as_ptr(), bufs.len()))
262}
263
264pub fn flush(rt_fd: RtFd) -> Result<()> {
265    let vdso_flush: extern "C" fn(i32) -> ErrorCode = unsafe {
266        core::mem::transmute(
267            RtVdsoVtable::get().fs_flush.load(Ordering::Relaxed) as usize as *const (),
268        )
269    };
270
271    into_result(vdso_flush(rt_fd))
272}
273
274pub fn seek(rt_fd: RtFd, offset: i64, whence: u8) -> Result<u64> {
275    let vdso_seek: extern "C" fn(i32, i64, u8) -> i64 = unsafe {
276        core::mem::transmute(
277            RtVdsoVtable::get().fs_seek.load(Ordering::Relaxed) as usize as *const ()
278        )
279    };
280
281    to_result!(vdso_seek(rt_fd, offset, whence))
282}
283
284pub fn mkdir(path: &str) -> Result<()> {
285    let vdso_mkdir: extern "C" fn(*const u8, usize) -> ErrorCode = unsafe {
286        core::mem::transmute(
287            RtVdsoVtable::get().fs_mkdir.load(Ordering::Relaxed) as usize as *const (),
288        )
289    };
290
291    let bytes = path.as_bytes();
292    into_result(vdso_mkdir(bytes.as_ptr(), bytes.len()))
293}
294
295pub fn unlink(path: &str) -> Result<()> {
296    let vdso_unlink: extern "C" fn(*const u8, usize) -> ErrorCode = unsafe {
297        core::mem::transmute(
298            RtVdsoVtable::get().fs_unlink.load(Ordering::Relaxed) as usize as *const (),
299        )
300    };
301
302    let bytes = path.as_bytes();
303    into_result(vdso_unlink(bytes.as_ptr(), bytes.len()))
304}
305
306pub fn rename(old: &str, new: &str) -> Result<()> {
307    let vdso_rename: extern "C" fn(*const u8, usize, *const u8, usize) -> ErrorCode = unsafe {
308        core::mem::transmute(
309            RtVdsoVtable::get().fs_rename.load(Ordering::Relaxed) as usize as *const (),
310        )
311    };
312
313    let old = old.as_bytes();
314    let new = new.as_bytes();
315    into_result(vdso_rename(
316        old.as_ptr(),
317        old.len(),
318        new.as_ptr(),
319        new.len(),
320    ))
321}
322
323pub fn rmdir(path: &str) -> Result<()> {
324    let vdso_rmdir: extern "C" fn(*const u8, usize) -> ErrorCode = unsafe {
325        core::mem::transmute(
326            RtVdsoVtable::get().fs_rmdir.load(Ordering::Relaxed) as usize as *const (),
327        )
328    };
329
330    let bytes = path.as_bytes();
331    into_result(vdso_rmdir(bytes.as_ptr(), bytes.len()))
332}
333
334pub fn rmdir_all(path: &str) -> Result<()> {
335    let vdso_rmdir_all: extern "C" fn(*const u8, usize) -> ErrorCode = unsafe {
336        core::mem::transmute(
337            RtVdsoVtable::get().fs_rmdir_all.load(Ordering::Relaxed) as usize as *const (),
338        )
339    };
340
341    let bytes = path.as_bytes();
342    into_result(vdso_rmdir_all(bytes.as_ptr(), bytes.len()))
343}
344
345pub fn set_perm(path: &str, perm: u64) -> Result<()> {
346    let vdso_set_perm: extern "C" fn(*const u8, usize, u64) -> ErrorCode = unsafe {
347        core::mem::transmute(
348            RtVdsoVtable::get().fs_set_perm.load(Ordering::Relaxed) as usize as *const (),
349        )
350    };
351
352    let bytes = path.as_bytes();
353    into_result(vdso_set_perm(bytes.as_ptr(), bytes.len(), perm))
354}
355
356pub fn set_file_perm(rt_fd: RtFd, perm: u64) -> Result<()> {
357    let vdso_set_file_perm: extern "C" fn(RtFd, u64) -> ErrorCode = unsafe {
358        core::mem::transmute(
359            RtVdsoVtable::get().fs_set_file_perm.load(Ordering::Relaxed) as usize as *const (),
360        )
361    };
362
363    into_result(vdso_set_file_perm(rt_fd, perm))
364}
365
366pub fn stat(path: &str) -> Result<FileAttr> {
367    let vdso_stat: extern "C" fn(*const u8, usize, *mut FileAttr) -> ErrorCode = unsafe {
368        core::mem::transmute(
369            RtVdsoVtable::get().fs_stat.load(Ordering::Relaxed) as usize as *const ()
370        )
371    };
372
373    let bytes = path.as_bytes();
374    let mut attr = FileAttr::new();
375
376    into_result(vdso_stat(bytes.as_ptr(), bytes.len(), &mut attr))?;
377    Ok(attr)
378}
379
380pub fn canonicalize(path: &str) -> Result<alloc::string::String> {
381    let vdso_canonicalize: extern "C" fn(*const u8, usize, *mut u8, *mut usize) -> ErrorCode = unsafe {
382        core::mem::transmute(
383            RtVdsoVtable::get().fs_canonicalize.load(Ordering::Relaxed) as usize as *const (),
384        )
385    };
386
387    let path = path.as_bytes();
388    let mut bytes = [0_u8; MAX_PATH_LEN];
389    let mut len = 0_usize;
390
391    use alloc::borrow::ToOwned;
392    into_result(vdso_canonicalize(
393        path.as_ptr(),
394        path.len(),
395        bytes.as_mut_ptr(),
396        &mut len,
397    ))?;
398    Ok(core::str::from_utf8(&bytes[..len]).unwrap().to_owned())
399}
400
401pub fn copy(from: &str, to: &str) -> Result<u64> {
402    let vdso_copy: extern "C" fn(*const u8, usize, *const u8, usize) -> i64 = unsafe {
403        core::mem::transmute(
404            RtVdsoVtable::get().fs_copy.load(Ordering::Relaxed) as usize as *const ()
405        )
406    };
407
408    let from = from.as_bytes();
409    let to = to.as_bytes();
410    to_result!(vdso_copy(from.as_ptr(), from.len(), to.as_ptr(), to.len()))
411}
412
413pub fn opendir(path: &str) -> Result<RtFd> {
414    let vdso_opendir: extern "C" fn(*const u8, usize) -> i32 = unsafe {
415        core::mem::transmute(
416            RtVdsoVtable::get().fs_opendir.load(Ordering::Relaxed) as usize as *const (),
417        )
418    };
419
420    let bytes = path.as_bytes();
421    to_result!(vdso_opendir(bytes.as_ptr(), bytes.len()))
422}
423
424pub fn closedir(rt_fd: RtFd) -> Result<()> {
425    let vdso_closedir: extern "C" fn(i32) -> ErrorCode = unsafe {
426        core::mem::transmute(
427            RtVdsoVtable::get().fs_closedir.load(Ordering::Relaxed) as usize as *const (),
428        )
429    };
430
431    into_result(vdso_closedir(rt_fd))
432}
433
434pub fn readdir(rt_fd: RtFd) -> Result<Option<DirEntry>> {
435    let vdso_readdir: extern "C" fn(i32, *mut DirEntry) -> ErrorCode = unsafe {
436        core::mem::transmute(
437            RtVdsoVtable::get().fs_readdir.load(Ordering::Relaxed) as usize as *const (),
438        )
439    };
440
441    let mut dentry = DirEntry::new();
442    match into_result(vdso_readdir(rt_fd, &mut dentry)) {
443        Ok(()) => Ok(Some(dentry)),
444        Err(Error::NotFound) => Ok(None),
445        Err(err) => Err(err),
446    }
447}
448
449pub fn getcwd() -> Result<alloc::string::String> {
450    let vdso_getcwd: extern "C" fn(*mut u8, *mut usize) -> ErrorCode = unsafe {
451        core::mem::transmute(
452            RtVdsoVtable::get().fs_getcwd.load(Ordering::Relaxed) as usize as *const (),
453        )
454    };
455
456    let mut bytes = [0_u8; MAX_PATH_LEN];
457    let mut len = 0_usize;
458
459    use alloc::borrow::ToOwned;
460    into_result(vdso_getcwd(bytes.as_mut_ptr(), &mut len))?;
461    Ok(core::str::from_utf8(&bytes[..len]).unwrap().to_owned())
462}
463
464pub fn chdir(path: &str) -> Result<()> {
465    let vdso_chdir: extern "C" fn(*const u8, usize) -> ErrorCode = unsafe {
466        core::mem::transmute(
467            RtVdsoVtable::get().fs_chdir.load(Ordering::Relaxed) as usize as *const (),
468        )
469    };
470
471    let bytes = path.as_bytes();
472    into_result(vdso_chdir(bytes.as_ptr(), bytes.len()))
473}