axfs/
fops.rs

1//! Low-level filesystem operations.
2
3use axerrno::{AxError, AxResult, ax_err, ax_err_type};
4use axfs_vfs::{VfsError, VfsNodeRef};
5use axio::SeekFrom;
6use cap_access::{Cap, WithCap};
7use core::fmt;
8
9#[cfg(feature = "myfs")]
10pub use crate::dev::Disk;
11#[cfg(feature = "myfs")]
12pub use crate::fs::myfs::MyFileSystemIf;
13
14/// Alias of [`axfs_vfs::VfsNodeType`].
15pub type FileType = axfs_vfs::VfsNodeType;
16/// Alias of [`axfs_vfs::VfsDirEntry`].
17pub type DirEntry = axfs_vfs::VfsDirEntry;
18/// Alias of [`axfs_vfs::VfsNodeAttr`].
19pub type FileAttr = axfs_vfs::VfsNodeAttr;
20/// Alias of [`axfs_vfs::VfsNodePerm`].
21pub type FilePerm = axfs_vfs::VfsNodePerm;
22
23/// An opened file object, with open permissions and a cursor.
24pub struct File {
25    node: WithCap<VfsNodeRef>,
26    is_append: bool,
27    offset: u64,
28}
29
30/// An opened directory object, with open permissions and a cursor for
31/// [`read_dir`](Directory::read_dir).
32pub struct Directory {
33    node: WithCap<VfsNodeRef>,
34    entry_idx: usize,
35}
36
37/// Options and flags which can be used to configure how a file is opened.
38#[derive(Clone)]
39pub struct OpenOptions {
40    // generic
41    read: bool,
42    write: bool,
43    append: bool,
44    truncate: bool,
45    create: bool,
46    create_new: bool,
47    // system-specific
48    _custom_flags: i32,
49    _mode: u32,
50}
51
52impl Default for OpenOptions {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl OpenOptions {
59    /// Creates a blank new set of options ready for configuration.
60    pub const fn new() -> Self {
61        Self {
62            // generic
63            read: false,
64            write: false,
65            append: false,
66            truncate: false,
67            create: false,
68            create_new: false,
69            // system-specific
70            _custom_flags: 0,
71            _mode: 0o666,
72        }
73    }
74    /// Sets the option for read access.
75    pub fn read(&mut self, read: bool) {
76        self.read = read;
77    }
78    /// Sets the option for write access.
79    pub fn write(&mut self, write: bool) {
80        self.write = write;
81    }
82    /// Sets the option for the append mode.
83    pub fn append(&mut self, append: bool) {
84        self.append = append;
85    }
86    /// Sets the option for truncating a previous file.
87    pub fn truncate(&mut self, truncate: bool) {
88        self.truncate = truncate;
89    }
90    /// Sets the option to create a new file, or open it if it already exists.
91    pub fn create(&mut self, create: bool) {
92        self.create = create;
93    }
94    /// Sets the option to create a new file, failing if it already exists.
95    pub fn create_new(&mut self, create_new: bool) {
96        self.create_new = create_new;
97    }
98
99    const fn is_valid(&self) -> bool {
100        if !self.read && !self.write && !self.append {
101            return false;
102        }
103        match (self.write, self.append) {
104            (true, false) => {}
105            (false, false) => {
106                if self.truncate || self.create || self.create_new {
107                    return false;
108                }
109            }
110            (_, true) => {
111                if self.truncate && !self.create_new {
112                    return false;
113                }
114            }
115        }
116        true
117    }
118}
119
120impl File {
121    fn access_node(&self, cap: Cap) -> AxResult<&VfsNodeRef> {
122        self.node.access_or_err(cap, AxError::PermissionDenied)
123    }
124
125    fn _open_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult<Self> {
126        debug!("open file: {} {:?}", path, opts);
127        if !opts.is_valid() {
128            return ax_err!(InvalidInput);
129        }
130
131        let node_option = crate::root::lookup(dir, path);
132        let node = if opts.create || opts.create_new {
133            match node_option {
134                Ok(node) => {
135                    // already exists
136                    if opts.create_new {
137                        return ax_err!(AlreadyExists);
138                    }
139                    node
140                }
141                // not exists, create new
142                Err(VfsError::NotFound) => crate::root::create_file(dir, path)?,
143                Err(e) => return Err(e),
144            }
145        } else {
146            // just open the existing
147            node_option?
148        };
149
150        let attr = node.get_attr()?;
151        if attr.is_dir()
152            && (opts.create || opts.create_new || opts.write || opts.append || opts.truncate)
153        {
154            return ax_err!(IsADirectory);
155        }
156        let access_cap = opts.into();
157        if !perm_to_cap(attr.perm()).contains(access_cap) {
158            return ax_err!(PermissionDenied);
159        }
160
161        node.open()?;
162        if opts.truncate {
163            node.truncate(0)?;
164        }
165        Ok(Self {
166            node: WithCap::new(node, access_cap),
167            is_append: opts.append,
168            offset: 0,
169        })
170    }
171
172    /// Opens a file at the path relative to the current directory. Returns a
173    /// [`File`] object.
174    pub fn open(path: &str, opts: &OpenOptions) -> AxResult<Self> {
175        Self::_open_at(None, path, opts)
176    }
177
178    /// Truncates the file to the specified size.
179    pub fn truncate(&self, size: u64) -> AxResult {
180        self.access_node(Cap::WRITE)?.truncate(size)?;
181        Ok(())
182    }
183
184    /// Reads the file at the current position. Returns the number of bytes
185    /// read.
186    ///
187    /// After the read, the cursor will be advanced by the number of bytes read.
188    pub fn read(&mut self, buf: &mut [u8]) -> AxResult<usize> {
189        let node = self.access_node(Cap::READ)?;
190        let read_len = node.read_at(self.offset, buf)?;
191        self.offset += read_len as u64;
192        Ok(read_len)
193    }
194
195    /// Reads the file at the given position. Returns the number of bytes read.
196    ///
197    /// It does not update the file cursor.
198    pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> AxResult<usize> {
199        let node = self.access_node(Cap::READ)?;
200        let read_len = node.read_at(offset, buf)?;
201        Ok(read_len)
202    }
203
204    /// Writes the file at the current position. Returns the number of bytes
205    /// written.
206    ///
207    /// After the write, the cursor will be advanced by the number of bytes
208    /// written.
209    pub fn write(&mut self, buf: &[u8]) -> AxResult<usize> {
210        let offset = if self.is_append {
211            self.get_attr()?.size()
212        } else {
213            self.offset
214        };
215        let node = self.access_node(Cap::WRITE)?;
216        let write_len = node.write_at(offset, buf)?;
217        self.offset = offset + write_len as u64;
218        Ok(write_len)
219    }
220
221    /// Writes the file at the given position. Returns the number of bytes
222    /// written.
223    ///
224    /// It does not update the file cursor.
225    pub fn write_at(&self, offset: u64, buf: &[u8]) -> AxResult<usize> {
226        let node = self.access_node(Cap::WRITE)?;
227        let write_len = node.write_at(offset, buf)?;
228        Ok(write_len)
229    }
230
231    /// Flushes the file, writes all buffered data to the underlying device.
232    pub fn flush(&self) -> AxResult {
233        self.access_node(Cap::WRITE)?.fsync()?;
234        Ok(())
235    }
236
237    /// Sets the cursor of the file to the specified offset. Returns the new
238    /// position after the seek.
239    pub fn seek(&mut self, pos: SeekFrom) -> AxResult<u64> {
240        let size = self.get_attr()?.size();
241        let new_offset = match pos {
242            SeekFrom::Start(pos) => Some(pos),
243            SeekFrom::Current(off) => self.offset.checked_add_signed(off),
244            SeekFrom::End(off) => size.checked_add_signed(off),
245        }
246        .ok_or_else(|| ax_err_type!(InvalidInput))?;
247        self.offset = new_offset;
248        Ok(new_offset)
249    }
250
251    /// Gets the file attributes.
252    pub fn get_attr(&self) -> AxResult<FileAttr> {
253        self.access_node(Cap::empty())?.get_attr()
254    }
255}
256
257impl Directory {
258    fn access_node(&self, cap: Cap) -> AxResult<&VfsNodeRef> {
259        self.node.access_or_err(cap, AxError::PermissionDenied)
260    }
261
262    fn _open_dir_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult<Self> {
263        debug!("open dir: {}", path);
264        if !opts.read {
265            return ax_err!(InvalidInput);
266        }
267        if opts.create || opts.create_new || opts.write || opts.append || opts.truncate {
268            return ax_err!(InvalidInput);
269        }
270
271        let node = crate::root::lookup(dir, path)?;
272        let attr = node.get_attr()?;
273        if !attr.is_dir() {
274            return ax_err!(NotADirectory);
275        }
276        let access_cap = opts.into();
277        if !perm_to_cap(attr.perm()).contains(access_cap) {
278            return ax_err!(PermissionDenied);
279        }
280
281        node.open()?;
282        Ok(Self {
283            node: WithCap::new(node, access_cap),
284            entry_idx: 0,
285        })
286    }
287
288    fn access_at(&self, path: &str) -> AxResult<Option<&VfsNodeRef>> {
289        if path.starts_with('/') {
290            Ok(None)
291        } else {
292            Ok(Some(self.access_node(Cap::EXECUTE)?))
293        }
294    }
295
296    /// Opens a directory at the path relative to the current directory.
297    /// Returns a [`Directory`] object.
298    pub fn open_dir(path: &str, opts: &OpenOptions) -> AxResult<Self> {
299        Self::_open_dir_at(None, path, opts)
300    }
301
302    /// Opens a directory at the path relative to this directory. Returns a
303    /// [`Directory`] object.
304    pub fn open_dir_at(&self, path: &str, opts: &OpenOptions) -> AxResult<Self> {
305        Self::_open_dir_at(self.access_at(path)?, path, opts)
306    }
307
308    /// Opens a file at the path relative to this directory. Returns a [`File`]
309    /// object.
310    pub fn open_file_at(&self, path: &str, opts: &OpenOptions) -> AxResult<File> {
311        File::_open_at(self.access_at(path)?, path, opts)
312    }
313
314    /// Creates an empty file at the path relative to this directory.
315    pub fn create_file(&self, path: &str) -> AxResult<VfsNodeRef> {
316        crate::root::create_file(self.access_at(path)?, path)
317    }
318
319    /// Creates an empty directory at the path relative to this directory.
320    pub fn create_dir(&self, path: &str) -> AxResult {
321        crate::root::create_dir(self.access_at(path)?, path)
322    }
323
324    /// Removes a file at the path relative to this directory.
325    pub fn remove_file(&self, path: &str) -> AxResult {
326        crate::root::remove_file(self.access_at(path)?, path)
327    }
328
329    /// Removes a directory at the path relative to this directory.
330    pub fn remove_dir(&self, path: &str) -> AxResult {
331        crate::root::remove_dir(self.access_at(path)?, path)
332    }
333
334    /// Reads directory entries starts from the current position into the
335    /// given buffer. Returns the number of entries read.
336    ///
337    /// After the read, the cursor will be advanced by the number of entries
338    /// read.
339    pub fn read_dir(&mut self, dirents: &mut [DirEntry]) -> AxResult<usize> {
340        let n = self
341            .access_node(Cap::READ)?
342            .read_dir(self.entry_idx, dirents)?;
343        self.entry_idx += n;
344        Ok(n)
345    }
346
347    /// Rename a file or directory to a new name.
348    /// Delete the original file if `old` already exists.
349    ///
350    /// This only works then the new path is in the same mounted fs.
351    pub fn rename(&self, old: &str, new: &str) -> AxResult {
352        crate::root::rename(old, new)
353    }
354}
355
356impl Drop for File {
357    fn drop(&mut self) {
358        unsafe { self.node.access_unchecked().release().ok() };
359    }
360}
361
362impl Drop for Directory {
363    fn drop(&mut self) {
364        unsafe { self.node.access_unchecked().release().ok() };
365    }
366}
367
368impl fmt::Debug for OpenOptions {
369    #[allow(unused_assignments)]
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        let mut written = false;
372        macro_rules! fmt_opt {
373            ($field: ident, $label: literal) => {
374                if self.$field {
375                    if written {
376                        write!(f, " | ")?;
377                    }
378                    write!(f, $label)?;
379                    written = true;
380                }
381            };
382        }
383        fmt_opt!(read, "READ");
384        fmt_opt!(write, "WRITE");
385        fmt_opt!(append, "APPEND");
386        fmt_opt!(truncate, "TRUNC");
387        fmt_opt!(create, "CREATE");
388        fmt_opt!(create_new, "CREATE_NEW");
389        Ok(())
390    }
391}
392
393impl From<&OpenOptions> for Cap {
394    fn from(opts: &OpenOptions) -> Cap {
395        let mut cap = Cap::empty();
396        if opts.read {
397            cap |= Cap::READ;
398        }
399        if opts.write | opts.append {
400            cap |= Cap::WRITE;
401        }
402        cap
403    }
404}
405
406fn perm_to_cap(perm: FilePerm) -> Cap {
407    let mut cap = Cap::empty();
408    if perm.owner_readable() {
409        cap |= Cap::READ;
410    }
411    if perm.owner_writable() {
412        cap |= Cap::WRITE;
413    }
414    if perm.owner_executable() {
415        cap |= Cap::EXECUTE;
416    }
417    cap
418}