starry_api/file/
mod.rs

1mod fs;
2mod net;
3mod pipe;
4mod stdio;
5
6use core::{any::Any, ffi::c_int};
7
8use alloc::{sync::Arc, vec::Vec};
9use axerrno::{LinuxError, LinuxResult};
10use axio::PollState;
11use axns::{ResArc, def_resource};
12use flatten_objects::FlattenObjects;
13use linux_raw_sys::general::{stat, statx};
14use spin::RwLock;
15
16pub use self::{
17    fs::{Directory, File},
18    net::Socket,
19    pipe::Pipe,
20};
21
22pub const AX_FILE_LIMIT: usize = 1024;
23
24#[derive(Debug, Clone, Copy)]
25pub struct Kstat {
26    ino: u64,
27    nlink: u32,
28    uid: u32,
29    gid: u32,
30    mode: u32,
31    size: u64,
32    blocks: u64,
33    blksize: u32,
34}
35
36impl Default for Kstat {
37    fn default() -> Self {
38        Self {
39            ino: 1,
40            nlink: 1,
41            uid: 1,
42            gid: 1,
43            mode: 0,
44            size: 0,
45            blocks: 0,
46            blksize: 4096,
47        }
48    }
49}
50
51impl From<Kstat> for stat {
52    fn from(value: Kstat) -> Self {
53        // SAFETY: valid for stat
54        let mut stat: stat = unsafe { core::mem::zeroed() };
55        stat.st_ino = value.ino as _;
56        stat.st_nlink = value.nlink as _;
57        stat.st_mode = value.mode as _;
58        stat.st_uid = value.uid as _;
59        stat.st_gid = value.gid as _;
60        stat.st_size = value.size as _;
61        stat.st_blksize = value.blksize as _;
62        stat.st_blocks = value.blocks as _;
63
64        stat
65    }
66}
67
68impl From<Kstat> for statx {
69    fn from(value: Kstat) -> Self {
70        // SAFETY: valid for statx
71        let mut statx: statx = unsafe { core::mem::zeroed() };
72        statx.stx_blksize = value.blksize as _;
73        statx.stx_attributes = value.mode as _;
74        statx.stx_nlink = value.nlink as _;
75        statx.stx_uid = value.uid as _;
76        statx.stx_gid = value.gid as _;
77        statx.stx_mode = value.mode as _;
78        statx.stx_ino = value.ino as _;
79        statx.stx_size = value.size as _;
80        statx.stx_blocks = value.blocks as _;
81
82        statx
83    }
84}
85
86#[allow(dead_code)]
87pub trait FileLike: Send + Sync {
88    fn read(&self, buf: &mut [u8]) -> LinuxResult<usize>;
89    fn write(&self, buf: &[u8]) -> LinuxResult<usize>;
90    fn stat(&self) -> LinuxResult<Kstat>;
91    fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
92    fn poll(&self) -> LinuxResult<PollState>;
93    fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult;
94
95    fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>>
96    where
97        Self: Sized + 'static,
98    {
99        get_file_like(fd)?
100            .into_any()
101            .downcast::<Self>()
102            .map_err(|_| LinuxError::EINVAL)
103    }
104
105    fn add_to_fd_table(self) -> LinuxResult<c_int>
106    where
107        Self: Sized + 'static,
108    {
109        add_file_like(Arc::new(self))
110    }
111}
112
113def_resource! {
114    pub static FD_TABLE: ResArc<RwLock<FlattenObjects<Arc<dyn FileLike>, AX_FILE_LIMIT>>> = ResArc::new();
115}
116
117impl FD_TABLE {
118    /// Return a copy of the inner table.
119    pub fn copy_inner(&self) -> RwLock<FlattenObjects<Arc<dyn FileLike>, AX_FILE_LIMIT>> {
120        let table = self.read();
121        let mut new_table = FlattenObjects::new();
122        for id in table.ids() {
123            let _ = new_table.add_at(id, table.get(id).unwrap().clone());
124        }
125        RwLock::new(new_table)
126    }
127
128    pub fn clear(&self) {
129        let mut table = self.write();
130        let ids = table.ids().collect::<Vec<_>>();
131        for id in ids {
132            let _ = table.remove(id);
133        }
134    }
135}
136
137/// Get a file-like object by `fd`.
138pub fn get_file_like(fd: c_int) -> LinuxResult<Arc<dyn FileLike>> {
139    FD_TABLE
140        .read()
141        .get(fd as usize)
142        .cloned()
143        .ok_or(LinuxError::EBADF)
144}
145
146/// Add a file to the file descriptor table.
147pub fn add_file_like(f: Arc<dyn FileLike>) -> LinuxResult<c_int> {
148    Ok(FD_TABLE.write().add(f).map_err(|_| LinuxError::EMFILE)? as c_int)
149}
150
151/// Close a file by `fd`.
152pub fn close_file_like(fd: c_int) -> LinuxResult {
153    let f = FD_TABLE
154        .write()
155        .remove(fd as usize)
156        .ok_or(LinuxError::EBADF)?;
157    debug!("close_file_like <= count: {}", Arc::strong_count(&f));
158    Ok(())
159}
160
161#[ctor_bare::register_ctor]
162fn init_stdio() {
163    let mut fd_table = flatten_objects::FlattenObjects::new();
164    fd_table
165        .add_at(0, Arc::new(stdio::stdin()) as _)
166        .unwrap_or_else(|_| panic!()); // stdin
167    fd_table
168        .add_at(1, Arc::new(stdio::stdout()) as _)
169        .unwrap_or_else(|_| panic!()); // stdout
170    fd_table
171        .add_at(2, Arc::new(stdio::stdout()) as _)
172        .unwrap_or_else(|_| panic!()); // stderr
173    FD_TABLE.init_new(spin::RwLock::new(fd_table));
174}