Skip to main content

littlefs_rust/
file.rs

1use alloc::boxed::Box;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::ffi::c_void;
5use core::mem::MaybeUninit;
6
7use littlefs_rust_core::{LfsFile, LfsFileConfig};
8
9use crate::error::{from_lfs_result, from_lfs_size, Error};
10use crate::filesystem::Filesystem;
11use crate::metadata::{OpenFlags, SeekFrom};
12use crate::storage::Storage;
13
14pub(crate) struct FileAllocation {
15    pub(crate) file: MaybeUninit<LfsFile>,
16    _cache: Vec<u8>,
17    pub(crate) file_config: LfsFileConfig,
18}
19
20impl FileAllocation {
21    pub(crate) fn new(cache_size: u32) -> Self {
22        let mut cache = vec![0u8; cache_size as usize];
23        let file_config = LfsFileConfig {
24            buffer: cache.as_mut_ptr() as *mut c_void,
25            attrs: core::ptr::null_mut(),
26            attr_count: 0,
27        };
28        Self {
29            file: MaybeUninit::zeroed(),
30            _cache: cache,
31            file_config,
32        }
33    }
34}
35
36/// An open file handle.
37///
38/// Obtained from [`Filesystem::open`]. Automatically closed on drop; call
39/// [`File::close`] explicitly to check for errors.
40pub struct File<'a, S: Storage> {
41    fs: &'a Filesystem<S>,
42    alloc: Box<FileAllocation>,
43    closed: bool,
44}
45
46impl<'a, S: Storage> File<'a, S> {
47    pub(crate) fn open(fs: &'a Filesystem<S>, path: &str, flags: OpenFlags) -> Result<Self, Error> {
48        let mut alloc = Box::new(FileAllocation::new(fs.cache_size()));
49        let path_bytes = null_terminate(path);
50        {
51            let mut inner = fs.inner.borrow_mut();
52            let rc = littlefs_rust_core::lfs_file_opencfg(
53                inner.lfs.as_mut_ptr(),
54                alloc.file.as_mut_ptr(),
55                path_bytes.as_ptr(),
56                flags.bits() as i32,
57                &alloc.file_config as *const LfsFileConfig,
58            );
59            from_lfs_result(rc)?;
60        }
61        Ok(File {
62            fs,
63            alloc,
64            closed: false,
65        })
66    }
67
68    /// Read up to `buf.len()` bytes from the current position.
69    /// Returns the number of bytes actually read.
70    pub fn read(&self, buf: &mut [u8]) -> Result<u32, Error> {
71        let mut inner = self.fs.inner.borrow_mut();
72        let rc = littlefs_rust_core::lfs_file_read(
73            inner.lfs.as_mut_ptr(),
74            self.alloc.file.as_ptr() as *mut LfsFile,
75            buf.as_mut_ptr() as *mut c_void,
76            buf.len() as u32,
77        );
78        drop(inner);
79        from_lfs_size(rc)
80    }
81
82    /// Write `data` at the current position. Returns the number of bytes written.
83    pub fn write(&self, data: &[u8]) -> Result<u32, Error> {
84        let mut inner = self.fs.inner.borrow_mut();
85        let rc = littlefs_rust_core::lfs_file_write(
86            inner.lfs.as_mut_ptr(),
87            self.alloc.file.as_ptr() as *mut LfsFile,
88            data.as_ptr() as *const c_void,
89            data.len() as u32,
90        );
91        drop(inner);
92        from_lfs_size(rc)
93    }
94
95    /// Seek to a position. Returns the new absolute offset.
96    pub fn seek(&self, pos: SeekFrom) -> Result<u32, Error> {
97        let (off, whence) = match pos {
98            SeekFrom::Start(n) => (
99                n as i32,
100                littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_SET,
101            ),
102            SeekFrom::Current(n) => (
103                n,
104                littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_CUR,
105            ),
106            SeekFrom::End(n) => (
107                n,
108                littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_END,
109            ),
110        };
111        let mut inner = self.fs.inner.borrow_mut();
112        let rc = littlefs_rust_core::lfs_file_seek(
113            inner.lfs.as_mut_ptr(),
114            self.alloc.file.as_ptr() as *mut LfsFile,
115            off,
116            whence,
117        );
118        drop(inner);
119        from_lfs_size(rc)
120    }
121
122    /// Return the current read/write position.
123    pub fn tell(&self) -> u32 {
124        let mut inner = self.fs.inner.borrow_mut();
125        let rc = littlefs_rust_core::lfs_file_tell(
126            inner.lfs.as_mut_ptr(),
127            self.alloc.file.as_ptr() as *mut LfsFile,
128        );
129        drop(inner);
130        rc as u32
131    }
132
133    /// Return the file size in bytes.
134    pub fn size(&self) -> u32 {
135        let mut inner = self.fs.inner.borrow_mut();
136        let rc = littlefs_rust_core::lfs_file_size(
137            inner.lfs.as_mut_ptr(),
138            self.alloc.file.as_ptr() as *mut LfsFile,
139        );
140        drop(inner);
141        rc as u32
142    }
143
144    /// Flush cached writes to storage.
145    pub fn sync(&self) -> Result<(), Error> {
146        let mut inner = self.fs.inner.borrow_mut();
147        let rc = littlefs_rust_core::lfs_file_sync(
148            inner.lfs.as_mut_ptr(),
149            self.alloc.file.as_ptr() as *mut LfsFile,
150        );
151        drop(inner);
152        from_lfs_result(rc)
153    }
154
155    /// Truncate or extend the file to `size` bytes.
156    pub fn truncate(&self, size: u32) -> Result<(), Error> {
157        let mut inner = self.fs.inner.borrow_mut();
158        let rc = littlefs_rust_core::lfs_file_truncate(
159            inner.lfs.as_mut_ptr(),
160            self.alloc.file.as_ptr() as *mut LfsFile,
161            size,
162        );
163        drop(inner);
164        from_lfs_result(rc)
165    }
166
167    /// Close the file, flushing any pending writes. Consumes `self`.
168    ///
169    /// Dropping a [`File`] also closes it, but errors are silently ignored.
170    pub fn close(mut self) -> Result<(), Error> {
171        self.closed = true;
172        let mut inner = self.fs.inner.borrow_mut();
173        let rc = littlefs_rust_core::lfs_file_close(
174            inner.lfs.as_mut_ptr(),
175            self.alloc.file.as_ptr() as *mut LfsFile,
176        );
177        from_lfs_result(rc)
178    }
179}
180
181impl<S: Storage> Drop for File<'_, S> {
182    fn drop(&mut self) {
183        if !self.closed {
184            if let Ok(mut inner) = self.fs.inner.try_borrow_mut() {
185                let _ = littlefs_rust_core::lfs_file_close(
186                    inner.lfs.as_mut_ptr(),
187                    self.alloc.file.as_ptr() as *mut LfsFile,
188                );
189            }
190        }
191    }
192}
193
194fn null_terminate(s: &str) -> Vec<u8> {
195    let mut v: Vec<u8> = s.bytes().collect();
196    v.push(0);
197    v
198}