starry_api/imp/fs/
stat.rs

1use core::ffi::{c_char, c_int};
2
3use axerrno::{AxError, LinuxError, LinuxResult};
4use axfs::fops::OpenOptions;
5use linux_raw_sys::general::{AT_EMPTY_PATH, stat, statx};
6
7use crate::{
8    file::{Directory, File, FileLike, Kstat, get_file_like},
9    path::handle_file_path,
10    ptr::{UserConstPtr, UserPtr, nullable},
11};
12
13fn stat_at_path(path: &str) -> LinuxResult<Kstat> {
14    let opts = OpenOptions::new().set_read(true);
15    match axfs::fops::File::open(path, &opts) {
16        Ok(file) => File::new(file, path.into()).stat(),
17        Err(AxError::IsADirectory) => {
18            let dir = axfs::fops::Directory::open_dir(path, &opts)?;
19            Directory::new(dir, path.into()).stat()
20        }
21        Err(e) => Err(e.into()),
22    }
23}
24
25/// Get the file metadata by `path` and write into `statbuf`.
26///
27/// Return 0 if success.
28pub fn sys_stat(path: UserConstPtr<c_char>, statbuf: UserPtr<stat>) -> LinuxResult<isize> {
29    let path = path.get_as_str()?;
30    debug!("sys_stat <= path: {}", path);
31
32    *statbuf.get_as_mut()? = stat_at_path(path)?.into();
33
34    Ok(0)
35}
36
37/// Get file metadata by `fd` and write into `statbuf`.
38///
39/// Return 0 if success.
40pub fn sys_fstat(fd: i32, statbuf: UserPtr<stat>) -> LinuxResult<isize> {
41    debug!("sys_fstat <= fd: {}", fd);
42    *statbuf.get_as_mut()? = get_file_like(fd)?.stat()?.into();
43    Ok(0)
44}
45
46/// Get the metadata of the symbolic link and write into `buf`.
47///
48/// Return 0 if success.
49pub fn sys_lstat(path: UserConstPtr<c_char>, statbuf: UserPtr<stat>) -> LinuxResult<isize> {
50    // TODO: symlink
51    sys_stat(path, statbuf)
52}
53
54pub fn sys_fstatat(
55    dirfd: c_int,
56    path: UserConstPtr<c_char>,
57    statbuf: UserPtr<stat>,
58    flags: u32,
59) -> LinuxResult<isize> {
60    let path = nullable!(path.get_as_str())?;
61    debug!(
62        "sys_fstatat <= dirfd: {}, path: {:?}, flags: {}",
63        dirfd, path, flags
64    );
65
66    *statbuf.get_as_mut()? = if path.is_none_or(|s| s.is_empty()) {
67        if (flags & AT_EMPTY_PATH) == 0 {
68            return Err(LinuxError::ENOENT);
69        }
70        let f = get_file_like(dirfd)?;
71        f.stat()?.into()
72    } else {
73        let path = handle_file_path(dirfd, path.unwrap_or_default())?;
74        stat_at_path(path.as_str())?.into()
75    };
76
77    Ok(0)
78}
79
80pub fn sys_statx(
81    dirfd: c_int,
82    path: UserConstPtr<c_char>,
83    flags: u32,
84    _mask: u32,
85    statxbuf: UserPtr<statx>,
86) -> LinuxResult<isize> {
87    // `statx()` uses pathname, dirfd, and flags to identify the target
88    // file in one of the following ways:
89
90    // An absolute pathname(situation 1)
91    //        If pathname begins with a slash, then it is an absolute
92    //        pathname that identifies the target file.  In this case,
93    //        dirfd is ignored.
94
95    // A relative pathname(situation 2)
96    //        If pathname is a string that begins with a character other
97    //        than a slash and dirfd is AT_FDCWD, then pathname is a
98    //        relative pathname that is interpreted relative to the
99    //        process's current working directory.
100
101    // A directory-relative pathname(situation 3)
102    //        If pathname is a string that begins with a character other
103    //        than a slash and dirfd is a file descriptor that refers to
104    //        a directory, then pathname is a relative pathname that is
105    //        interpreted relative to the directory referred to by dirfd.
106    //        (See openat(2) for an explanation of why this is useful.)
107
108    // By file descriptor(situation 4)
109    //        If pathname is an empty string (or NULL since Linux 6.11)
110    //        and the AT_EMPTY_PATH flag is specified in flags (see
111    //        below), then the target file is the one referred to by the
112    //        file descriptor dirfd.
113
114    let path = nullable!(path.get_as_str())?;
115    debug!(
116        "sys_statx <= dirfd: {}, path: {:?}, flags: {}",
117        dirfd, path, flags
118    );
119
120    *statxbuf.get_as_mut()? = if path.is_none_or(|s| s.is_empty()) {
121        if (flags & AT_EMPTY_PATH) == 0 {
122            return Err(LinuxError::ENOENT);
123        }
124        let f = get_file_like(dirfd)?;
125        f.stat()?.into()
126    } else {
127        let path = handle_file_path(dirfd, path.unwrap_or_default())?;
128        stat_at_path(path.as_str())?.into()
129    };
130
131    Ok(0)
132}