arceos_posix_api/imp/
fs.rs

1use alloc::sync::Arc;
2use core::ffi::{c_char, c_int};
3
4use axerrno::{LinuxError, LinuxResult};
5use axfs::fops::OpenOptions;
6use axio::{PollState, SeekFrom};
7use axsync::Mutex;
8
9use super::fd_ops::{FileLike, get_file_like};
10use crate::{ctypes, utils::char_ptr_to_str};
11
12pub struct File {
13    inner: Mutex<axfs::fops::File>,
14}
15
16impl File {
17    fn new(inner: axfs::fops::File) -> Self {
18        Self {
19            inner: Mutex::new(inner),
20        }
21    }
22
23    fn add_to_fd_table(self) -> LinuxResult<c_int> {
24        super::fd_ops::add_file_like(Arc::new(self))
25    }
26
27    fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
28        let f = super::fd_ops::get_file_like(fd)?;
29        f.into_any()
30            .downcast::<Self>()
31            .map_err(|_| LinuxError::EINVAL)
32    }
33}
34
35impl FileLike for File {
36    fn read(&self, buf: &mut [u8]) -> LinuxResult<usize> {
37        Ok(self.inner.lock().read(buf)?)
38    }
39
40    fn write(&self, buf: &[u8]) -> LinuxResult<usize> {
41        Ok(self.inner.lock().write(buf)?)
42    }
43
44    fn stat(&self) -> LinuxResult<ctypes::stat> {
45        let metadata = self.inner.lock().get_attr()?;
46        let ty = metadata.file_type() as u8;
47        let perm = metadata.perm().bits() as u32;
48        let st_mode = ((ty as u32) << 12) | perm;
49        Ok(ctypes::stat {
50            st_ino: 1,
51            st_nlink: 1,
52            st_mode,
53            st_uid: 1000,
54            st_gid: 1000,
55            st_size: metadata.size() as _,
56            st_blocks: metadata.blocks() as _,
57            st_blksize: 512,
58            ..Default::default()
59        })
60    }
61
62    fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync> {
63        self
64    }
65
66    fn poll(&self) -> LinuxResult<PollState> {
67        Ok(PollState {
68            readable: true,
69            writable: true,
70        })
71    }
72
73    fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
74        Ok(())
75    }
76}
77
78/// Convert open flags to [`OpenOptions`].
79fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions {
80    let flags = flags as u32;
81    let mut options = OpenOptions::new();
82    match flags & 0b11 {
83        ctypes::O_RDONLY => options.read(true),
84        ctypes::O_WRONLY => options.write(true),
85        _ => {
86            options.read(true);
87            options.write(true);
88        }
89    };
90    if flags & ctypes::O_APPEND != 0 {
91        options.append(true);
92    }
93    if flags & ctypes::O_TRUNC != 0 {
94        options.truncate(true);
95    }
96    if flags & ctypes::O_CREAT != 0 {
97        options.create(true);
98    }
99    if flags & ctypes::O_EXEC != 0 {
100        options.create_new(true);
101    }
102    options
103}
104
105/// Open a file by `filename` and insert it into the file descriptor table.
106///
107/// Return its index in the file table (`fd`). Return `EMFILE` if it already
108/// has the maximum number of files open.
109pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int {
110    let filename = char_ptr_to_str(filename);
111    debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode);
112    syscall_body!(sys_open, {
113        let options = flags_to_options(flags, mode);
114        let file = axfs::fops::File::open(filename?, &options)?;
115        File::new(file).add_to_fd_table()
116    })
117}
118
119/// Set the position of the file indicated by `fd`.
120///
121/// Return its position after seek.
122pub fn sys_lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t {
123    debug!("sys_lseek <= {} {} {}", fd, offset, whence);
124    syscall_body!(sys_lseek, {
125        let pos = match whence {
126            0 => SeekFrom::Start(offset as _),
127            1 => SeekFrom::Current(offset as _),
128            2 => SeekFrom::End(offset as _),
129            _ => return Err(LinuxError::EINVAL),
130        };
131        let off = File::from_fd(fd)?.inner.lock().seek(pos)?;
132        Ok(off)
133    })
134}
135
136/// Get the file metadata by `path` and write into `buf`.
137///
138/// Return 0 if success.
139pub unsafe fn sys_stat(path: *const c_char, buf: *mut ctypes::stat) -> c_int {
140    let path = char_ptr_to_str(path);
141    debug!("sys_stat <= {:?} {:#x}", path, buf as usize);
142    syscall_body!(sys_stat, {
143        if buf.is_null() {
144            return Err(LinuxError::EFAULT);
145        }
146        let mut options = OpenOptions::new();
147        options.read(true);
148        let file = axfs::fops::File::open(path?, &options)?;
149        let st = File::new(file).stat()?;
150        unsafe { *buf = st };
151        Ok(0)
152    })
153}
154
155/// Get file metadata by `fd` and write into `buf`.
156///
157/// Return 0 if success.
158pub unsafe fn sys_fstat(fd: c_int, buf: *mut ctypes::stat) -> c_int {
159    debug!("sys_fstat <= {} {:#x}", fd, buf as usize);
160    syscall_body!(sys_fstat, {
161        if buf.is_null() {
162            return Err(LinuxError::EFAULT);
163        }
164
165        unsafe { *buf = get_file_like(fd)?.stat()? };
166        Ok(0)
167    })
168}
169
170/// Get the metadata of the symbolic link and write into `buf`.
171///
172/// Return 0 if success.
173pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t {
174    let path = char_ptr_to_str(path);
175    debug!("sys_lstat <= {:?} {:#x}", path, buf as usize);
176    syscall_body!(sys_lstat, {
177        if buf.is_null() {
178            return Err(LinuxError::EFAULT);
179        }
180        unsafe { *buf = Default::default() }; // TODO
181        Ok(0)
182    })
183}
184
185/// Get the path of the current directory.
186#[allow(clippy::unnecessary_cast)] // `c_char` is either `i8` or `u8`
187pub fn sys_getcwd(buf: *mut c_char, size: usize) -> *mut c_char {
188    debug!("sys_getcwd <= {:#x} {}", buf as usize, size);
189    syscall_body!(sys_getcwd, {
190        if buf.is_null() {
191            return Ok(core::ptr::null::<c_char>() as _);
192        }
193        let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, size as _) };
194        let cwd = axfs::api::current_dir()?;
195        let cwd = cwd.as_bytes();
196        if cwd.len() < size {
197            dst[..cwd.len()].copy_from_slice(cwd);
198            dst[cwd.len()] = 0;
199            Ok(buf)
200        } else {
201            Err(LinuxError::ERANGE)
202        }
203    })
204}
205
206/// Rename `old` to `new`
207/// If new exists, it is first removed.
208///
209/// Return 0 if the operation succeeds, otherwise return -1.
210pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int {
211    syscall_body!(sys_rename, {
212        let old_path = char_ptr_to_str(old)?;
213        let new_path = char_ptr_to_str(new)?;
214        debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path);
215        axfs::api::rename(old_path, new_path)?;
216        Ok(0)
217    })
218}