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