arceos_posix_api/imp/
fs.rs

1use alloc::string::{String, ToString};
2use alloc::sync::Arc;
3use core::ffi::{c_char, c_int};
4
5use axerrno::{LinuxError, LinuxResult};
6use axfs::fops::OpenOptions;
7use axio::{PollState, SeekFrom};
8use axsync::Mutex;
9
10use super::fd_ops::{FileLike, get_file_like};
11use crate::AT_FDCWD;
12use crate::{ctypes, utils::char_ptr_to_str};
13
14/// File wrapper for `axfs::fops::File`.
15pub struct File {
16    inner: Mutex<axfs::fops::File>,
17    path: String,
18}
19
20impl File {
21    fn new(inner: axfs::fops::File, path: String) -> Self {
22        Self {
23            inner: Mutex::new(inner),
24            path,
25        }
26    }
27
28    fn add_to_fd_table(self) -> LinuxResult<c_int> {
29        super::fd_ops::add_file_like(Arc::new(self))
30    }
31
32    fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
33        let f = super::fd_ops::get_file_like(fd)?;
34        f.into_any()
35            .downcast::<Self>()
36            .map_err(|_| LinuxError::EINVAL)
37    }
38
39    /// Get the path of the file.
40    pub fn path(&self) -> &str {
41        &self.path
42    }
43
44    /// Get the inner node of the file.    
45    pub fn inner(&self) -> &Mutex<axfs::fops::File> {
46        &self.inner
47    }
48}
49
50impl FileLike for File {
51    fn read(&self, buf: &mut [u8]) -> LinuxResult<usize> {
52        Ok(self.inner.lock().read(buf)?)
53    }
54
55    fn write(&self, buf: &[u8]) -> LinuxResult<usize> {
56        Ok(self.inner.lock().write(buf)?)
57    }
58
59    fn stat(&self) -> LinuxResult<ctypes::stat> {
60        let metadata = self.inner.lock().get_attr()?;
61        let ty = metadata.file_type() as u8;
62        let perm = metadata.perm().bits() as u32;
63        let st_mode = ((ty as u32) << 12) | perm;
64        Ok(ctypes::stat {
65            st_ino: 1,
66            st_nlink: 1,
67            st_mode,
68            st_uid: 1000,
69            st_gid: 1000,
70            st_size: metadata.size() as _,
71            st_blocks: metadata.blocks() as _,
72            st_blksize: 512,
73            ..Default::default()
74        })
75    }
76
77    fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync> {
78        self
79    }
80
81    fn poll(&self) -> LinuxResult<PollState> {
82        Ok(PollState {
83            readable: true,
84            writable: true,
85        })
86    }
87
88    fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
89        Ok(())
90    }
91}
92
93/// Convert open flags to [`OpenOptions`].
94fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions {
95    let flags = flags as u32;
96    let mut options = OpenOptions::new();
97    match flags & 0b11 {
98        ctypes::O_RDONLY => options.read(true),
99        ctypes::O_WRONLY => options.write(true),
100        _ => {
101            options.read(true);
102            options.write(true);
103        }
104    };
105    if flags & ctypes::O_APPEND != 0 {
106        options.append(true);
107    }
108    if flags & ctypes::O_TRUNC != 0 {
109        options.truncate(true);
110    }
111    if flags & ctypes::O_CREAT != 0 {
112        options.create(true);
113    }
114    if flags & ctypes::O_EXEC != 0 {
115        //options.create_new(true);
116        options.execute(true);
117    }
118    if flags & ctypes::O_DIRECTORY != 0 {
119        options.directory(true);
120    }
121    options
122}
123
124/// Open a file by `filename` and insert it into the file descriptor table.
125///
126/// Return its index in the file table (`fd`). Return `EMFILE` if it already
127/// has the maximum number of files open.
128pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int {
129    let filename = char_ptr_to_str(filename);
130    debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode);
131    syscall_body!(sys_open, {
132        add_file_or_directory_fd(
133            axfs::fops::File::open,
134            axfs::fops::Directory::open_dir,
135            filename?,
136            &flags_to_options(flags, mode),
137        )
138    })
139}
140
141/// Open or create a file.
142/// fd: file descriptor
143/// filename: file path to be opened or created
144/// flags: open flags
145/// mode: see man 7 inode
146/// return new file descriptor if succeed, or return -1.
147pub fn sys_openat(
148    dirfd: c_int,
149    filename: *const c_char,
150    flags: c_int,
151    mode: ctypes::mode_t,
152) -> c_int {
153    let filename = match char_ptr_to_str(filename) {
154        Ok(s) => s,
155        Err(_) => return -1,
156    };
157
158    debug!(
159        "sys_openat <= {} {:?} {:#o} {:#o}",
160        dirfd, filename, flags, mode
161    );
162
163    if filename.starts_with('/') || dirfd == AT_FDCWD as _ {
164        return sys_open(filename.as_ptr() as _, flags, mode);
165    }
166
167    match Directory::from_fd(dirfd).and_then(|dir| {
168        add_file_or_directory_fd(
169            |filename, options| dir.inner.lock().open_file_at(filename, options),
170            |filename, options| dir.inner.lock().open_dir_at(filename, options),
171            filename,
172            &flags_to_options(flags, mode),
173        )
174    }) {
175        Ok(fd) => fd,
176        Err(e) => {
177            debug!("sys_openat => {}", e);
178            -1
179        }
180    }
181}
182
183/// Use the function to open file or directory, then add into file descriptor table.
184/// First try opening files, if fails, try directory.
185fn add_file_or_directory_fd<F, D, E>(
186    open_file: F,
187    open_dir: D,
188    filename: &str,
189    options: &OpenOptions,
190) -> LinuxResult<c_int>
191where
192    E: Into<LinuxError>,
193    F: FnOnce(&str, &OpenOptions) -> Result<axfs::fops::File, E>,
194    D: FnOnce(&str, &OpenOptions) -> Result<axfs::fops::Directory, E>,
195{
196    if !options.has_directory() {
197        match open_file(filename, options)
198            .map_err(Into::into)
199            .and_then(|f| File::new(f, filename.into()).add_to_fd_table())
200        {
201            Err(LinuxError::EISDIR) => {}
202            r => return r,
203        }
204    }
205
206    Directory::new(
207        open_dir(filename, options).map_err(Into::into)?,
208        filename.to_string(),
209    )
210    .add_to_fd_table()
211}
212
213/// Set the position of the file indicated by `fd`.
214///
215/// Return its position after seek.
216pub fn sys_lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t {
217    debug!("sys_lseek <= {} {} {}", fd, offset, whence);
218    syscall_body!(sys_lseek, {
219        let pos = match whence {
220            0 => SeekFrom::Start(offset as _),
221            1 => SeekFrom::Current(offset as _),
222            2 => SeekFrom::End(offset as _),
223            _ => return Err(LinuxError::EINVAL),
224        };
225        let off = File::from_fd(fd)?.inner.lock().seek(pos)?;
226        Ok(off)
227    })
228}
229
230/// Get the file metadata by `path` and write into `buf`.
231///
232/// Return 0 if success.
233pub unsafe fn sys_stat(path: *const c_char, buf: *mut ctypes::stat) -> c_int {
234    let path = char_ptr_to_str(path);
235    debug!("sys_stat <= {:?} {:#x}", path, buf as usize);
236    syscall_body!(sys_stat, {
237        if buf.is_null() {
238            return Err(LinuxError::EFAULT);
239        }
240        let mut options = OpenOptions::new();
241        options.read(true);
242        let file = axfs::fops::File::open(path?, &options)?;
243        let st = File::new(file, path?.to_string()).stat()?;
244        unsafe { *buf = st };
245        Ok(0)
246    })
247}
248
249/// Get file metadata by `fd` and write into `buf`.
250///
251/// Return 0 if success.
252pub unsafe fn sys_fstat(fd: c_int, buf: *mut ctypes::stat) -> c_int {
253    debug!("sys_fstat <= {} {:#x}", fd, buf as usize);
254    syscall_body!(sys_fstat, {
255        if buf.is_null() {
256            return Err(LinuxError::EFAULT);
257        }
258
259        unsafe { *buf = get_file_like(fd)?.stat()? };
260        Ok(0)
261    })
262}
263
264/// Get the metadata of the symbolic link and write into `buf`.
265///
266/// Return 0 if success.
267pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t {
268    let path = char_ptr_to_str(path);
269    debug!("sys_lstat <= {:?} {:#x}", path, buf as usize);
270    syscall_body!(sys_lstat, {
271        if buf.is_null() {
272            return Err(LinuxError::EFAULT);
273        }
274        unsafe { *buf = Default::default() }; // TODO
275        Ok(0)
276    })
277}
278
279/// Get the path of the current directory.
280pub fn sys_getcwd(buf: *mut c_char, size: usize) -> *mut c_char {
281    debug!("sys_getcwd <= {:#x} {}", buf as usize, size);
282    syscall_body!(sys_getcwd, {
283        if buf.is_null() {
284            return Ok(core::ptr::null::<c_char>() as _);
285        }
286        let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, size as _) };
287        let cwd = axfs::api::current_dir()?;
288        let cwd = cwd.as_bytes();
289        if cwd.len() < size {
290            dst[..cwd.len()].copy_from_slice(cwd);
291            dst[cwd.len()] = 0;
292            Ok(buf)
293        } else {
294            Err(LinuxError::ERANGE)
295        }
296    })
297}
298
299/// Rename `old` to `new`
300/// If new exists, it is first removed.
301///
302/// Return 0 if the operation succeeds, otherwise return -1.
303pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int {
304    syscall_body!(sys_rename, {
305        let old_path = char_ptr_to_str(old)?;
306        let new_path = char_ptr_to_str(new)?;
307        debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path);
308        axfs::api::rename(old_path, new_path)?;
309        Ok(0)
310    })
311}
312
313/// Directory wrapper for `axfs::fops::Directory`.
314pub struct Directory {
315    inner: Mutex<axfs::fops::Directory>,
316    path: String,
317}
318
319impl Directory {
320    fn new(inner: axfs::fops::Directory, path: String) -> Self {
321        Self {
322            inner: Mutex::new(inner),
323            path,
324        }
325    }
326
327    fn add_to_fd_table(self) -> LinuxResult<c_int> {
328        super::fd_ops::add_file_like(Arc::new(self))
329    }
330
331    /// Open a directory by `fd`.
332    pub fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
333        let f = super::fd_ops::get_file_like(fd)?;
334        f.into_any()
335            .downcast::<Self>()
336            .map_err(|_| LinuxError::EINVAL)
337    }
338
339    /// Get the path of the directory.
340    pub fn path(&self) -> &str {
341        &self.path
342    }
343}
344
345impl FileLike for Directory {
346    fn read(&self, _buf: &mut [u8]) -> LinuxResult<usize> {
347        Err(LinuxError::EBADF)
348    }
349
350    fn write(&self, _buf: &[u8]) -> LinuxResult<usize> {
351        Err(LinuxError::EBADF)
352    }
353
354    fn stat(&self) -> LinuxResult<ctypes::stat> {
355        Err(LinuxError::EBADF)
356    }
357
358    fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync> {
359        self
360    }
361
362    fn poll(&self) -> LinuxResult<PollState> {
363        Ok(PollState {
364            readable: true,
365            writable: false,
366        })
367    }
368
369    fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
370        Ok(())
371    }
372}