1use alloc::boxed::Box;
2use alloc::vec;
3use alloc::vec::Vec;
4use core::cell::RefCell;
5use core::ffi::c_void;
6use core::mem::{ManuallyDrop, MaybeUninit};
7
8use littlefs_rust_core::{Lfs, LfsConfig, LfsInfo, LFS_ERR_IO};
9
10use crate::config::Config;
11use crate::dir::{dir_entry_from_info, ReadDir};
12use crate::error::{from_lfs_result, from_lfs_size, Error};
13use crate::file::File;
14use crate::metadata::{DirEntry, Metadata, OpenFlags};
15use crate::storage::Storage;
16
17pub(crate) struct FsInner<S: Storage> {
18 pub(crate) lfs: MaybeUninit<Lfs>,
19 pub(crate) config: LfsConfig,
20 pub(crate) storage: S,
21 _read_buf: Vec<u8>,
22 _prog_buf: Vec<u8>,
23 _lookahead_buf: Vec<u8>,
24 pub(crate) mounted: bool,
25}
26
27pub struct Filesystem<S: Storage> {
41 pub(crate) inner: RefCell<Box<FsInner<S>>>,
42}
43
44unsafe extern "C" fn trampoline_read<S: Storage>(
47 cfg: *const LfsConfig,
48 block: u32,
49 off: u32,
50 buffer: *mut u8,
51 size: u32,
52) -> i32 {
53 let storage = &mut *((*cfg).context as *mut S);
54 let buf = core::slice::from_raw_parts_mut(buffer, size as usize);
55 match storage.read(block, off, buf) {
56 Ok(()) => 0,
57 Err(_) => LFS_ERR_IO,
58 }
59}
60
61unsafe extern "C" fn trampoline_prog<S: Storage>(
62 cfg: *const LfsConfig,
63 block: u32,
64 off: u32,
65 buffer: *const u8,
66 size: u32,
67) -> i32 {
68 let storage = &mut *((*cfg).context as *mut S);
69 let buf = core::slice::from_raw_parts(buffer, size as usize);
70 match storage.write(block, off, buf) {
71 Ok(()) => 0,
72 Err(_) => LFS_ERR_IO,
73 }
74}
75
76unsafe extern "C" fn trampoline_erase<S: Storage>(cfg: *const LfsConfig, block: u32) -> i32 {
77 let storage = &mut *((*cfg).context as *mut S);
78 match storage.erase(block) {
79 Ok(()) => 0,
80 Err(_) => LFS_ERR_IO,
81 }
82}
83
84unsafe extern "C" fn trampoline_sync<S: Storage>(cfg: *const LfsConfig) -> i32 {
85 let storage = &mut *((*cfg).context as *mut S);
86 match storage.sync() {
87 Ok(()) => 0,
88 Err(_) => LFS_ERR_IO,
89 }
90}
91
92fn build_inner<S: Storage>(storage: S, config: &Config) -> FsInner<S> {
95 let cache_size = config.resolve_cache_size() as usize;
96 let lookahead_size = config.resolve_lookahead_size() as usize;
97
98 let mut read_buf = vec![0u8; cache_size];
99 let mut prog_buf = vec![0u8; cache_size];
100 let mut lookahead_buf = vec![0u8; lookahead_size];
101
102 let lfs_config = LfsConfig {
103 context: core::ptr::null_mut(),
104 read: Some(trampoline_read::<S>),
105 prog: Some(trampoline_prog::<S>),
106 erase: Some(trampoline_erase::<S>),
107 sync: Some(trampoline_sync::<S>),
108 read_size: config.read_size,
109 prog_size: config.prog_size,
110 block_size: config.block_size,
111 block_count: config.block_count,
112 block_cycles: config.block_cycles,
113 cache_size: config.resolve_cache_size(),
114 lookahead_size: config.resolve_lookahead_size(),
115 compact_thresh: u32::MAX,
116 read_buffer: read_buf.as_mut_ptr() as *mut c_void,
117 prog_buffer: prog_buf.as_mut_ptr() as *mut c_void,
118 lookahead_buffer: lookahead_buf.as_mut_ptr() as *mut c_void,
119 name_max: config.name_max,
120 file_max: config.file_max,
121 attr_max: config.attr_max,
122 metadata_max: 0,
123 inline_max: 0,
124 };
125
126 FsInner {
127 lfs: MaybeUninit::zeroed(),
128 config: lfs_config,
129 storage,
130 _read_buf: read_buf,
131 _prog_buf: prog_buf,
132 _lookahead_buf: lookahead_buf,
133 mounted: false,
134 }
135}
136
137fn wire_context<S: Storage>(inner: &mut FsInner<S>) {
140 inner.config.context = &mut inner.storage as *mut S as *mut c_void;
141 inner.config.read_buffer = inner._read_buf.as_mut_ptr() as *mut c_void;
142 inner.config.prog_buffer = inner._prog_buf.as_mut_ptr() as *mut c_void;
143 inner.config.lookahead_buffer = inner._lookahead_buf.as_mut_ptr() as *mut c_void;
144}
145
146impl<S: Storage> Filesystem<S> {
149 pub fn format(storage: &mut S, config: &Config) -> Result<(), Error> {
154 let mut inner = build_inner_borrowed(storage, config);
155 wire_context_borrowed(&mut inner);
156 let rc = littlefs_rust_core::lfs_format(
157 inner.lfs.as_mut_ptr(),
158 &inner.config as *const LfsConfig,
159 );
160 from_lfs_result(rc)
161 }
162
163 pub fn mount(storage: S, config: Config) -> Result<Self, (Error, S)> {
168 let mut inner = Box::new(build_inner(storage, &config));
169 wire_context(&mut inner);
170 let rc = littlefs_rust_core::lfs_mount(
171 inner.lfs.as_mut_ptr(),
172 &inner.config as *const LfsConfig,
173 );
174 if rc != 0 {
175 return Err((Error::from(rc), inner.storage));
176 }
177 inner.mounted = true;
178 Ok(Filesystem {
179 inner: RefCell::new(inner),
180 })
181 }
182
183 pub fn unmount(self) -> Result<S, Error> {
188 let this = ManuallyDrop::new(self);
189 let mut inner = this.inner.borrow_mut();
190 let rc = if inner.mounted {
191 inner.mounted = false;
192 littlefs_rust_core::lfs_unmount(inner.lfs.as_mut_ptr())
193 } else {
194 0
195 };
196 drop(inner);
197 let fs_inner = unsafe { core::ptr::read(&this.inner) }.into_inner();
200 from_lfs_result(rc)?;
201 Ok(fs_inner.storage)
202 }
203
204 pub(crate) fn cache_size(&self) -> u32 {
205 self.inner.borrow().config.cache_size
206 }
207
208 pub fn open(&self, path: &str, flags: OpenFlags) -> Result<File<'_, S>, Error> {
215 File::open(self, path, flags)
216 }
217
218 pub fn read_to_vec(&self, path: &str) -> Result<Vec<u8>, Error> {
222 let file = self.open(path, OpenFlags::READ)?;
223 let size = file.size() as usize;
224 let mut buf = vec![0u8; size];
225 if size > 0 {
226 let n = file.read(&mut buf)?;
227 buf.truncate(n as usize);
228 }
229 Ok(buf)
230 }
231
232 pub fn write_file(&self, path: &str, data: &[u8]) -> Result<(), Error> {
234 let file = self.open(
235 path,
236 OpenFlags::WRITE | OpenFlags::CREATE | OpenFlags::TRUNC,
237 )?;
238 let mut offset = 0;
239 while offset < data.len() {
240 let n = file.write(&data[offset..])? as usize;
241 offset += n;
242 }
243 Ok(())
244 }
245
246 pub fn mkdir(&self, path: &str) -> Result<(), Error> {
250 let path_bytes = null_terminate(path);
251 let mut inner = self.inner.borrow_mut();
252 let rc = littlefs_rust_core::lfs_mkdir(inner.lfs.as_mut_ptr(), path_bytes.as_ptr());
253 from_lfs_result(rc)
254 }
255
256 pub fn remove(&self, path: &str) -> Result<(), Error> {
258 let path_bytes = null_terminate(path);
259 let mut inner = self.inner.borrow_mut();
260 let rc = littlefs_rust_core::lfs_remove(inner.lfs.as_mut_ptr(), path_bytes.as_ptr());
261 from_lfs_result(rc)
262 }
263
264 pub fn rename(&self, from: &str, to: &str) -> Result<(), Error> {
266 let from_bytes = null_terminate(from);
267 let to_bytes = null_terminate(to);
268 let mut inner = self.inner.borrow_mut();
269 let rc = littlefs_rust_core::lfs_rename(
270 inner.lfs.as_mut_ptr(),
271 from_bytes.as_ptr(),
272 to_bytes.as_ptr(),
273 );
274 from_lfs_result(rc)
275 }
276
277 pub fn stat(&self, path: &str) -> Result<Metadata, Error> {
279 let path_bytes = null_terminate(path);
280 let mut info = MaybeUninit::<LfsInfo>::zeroed();
281 {
282 let mut inner = self.inner.borrow_mut();
283 let rc = littlefs_rust_core::lfs_stat(
284 inner.lfs.as_mut_ptr(),
285 path_bytes.as_ptr(),
286 info.as_mut_ptr(),
287 );
288 from_lfs_result(rc)?;
289 }
290 let entry = dir_entry_from_info(unsafe { &*info.as_ptr() });
291 Ok(Metadata {
292 name: entry.name,
293 file_type: entry.file_type,
294 size: entry.size,
295 })
296 }
297
298 pub fn exists(&self, path: &str) -> bool {
300 self.stat(path).is_ok()
301 }
302
303 pub fn read_dir(&self, path: &str) -> Result<ReadDir<'_, S>, Error> {
308 ReadDir::open(self, path)
309 }
310
311 pub fn list_dir(&self, path: &str) -> Result<Vec<DirEntry>, Error> {
313 let dir = self.read_dir(path)?;
314 dir.collect()
315 }
316
317 pub fn fs_size(&self) -> Result<u32, Error> {
321 let mut inner = self.inner.borrow_mut();
322 let rc = littlefs_rust_core::lfs_fs_size(inner.lfs.as_mut_ptr());
323 from_lfs_size(rc)
324 }
325
326 pub fn gc(&self) -> Result<(), Error> {
328 let mut inner = self.inner.borrow_mut();
329 let rc = littlefs_rust_core::lfs_fs_gc(inner.lfs.as_mut_ptr());
330 from_lfs_result(rc)
331 }
332}
333
334impl<S: Storage> Drop for Filesystem<S> {
335 fn drop(&mut self) {
336 if let Ok(mut inner) = self.inner.try_borrow_mut() {
337 if inner.mounted {
338 let _ = littlefs_rust_core::lfs_unmount(inner.lfs.as_mut_ptr());
339 inner.mounted = false;
340 }
341 }
342 }
343}
344
345struct BorrowedFsInner<'a, S: Storage> {
348 lfs: MaybeUninit<Lfs>,
349 config: LfsConfig,
350 storage: &'a mut S,
351 _read_buf: Vec<u8>,
352 _prog_buf: Vec<u8>,
353 _lookahead_buf: Vec<u8>,
354}
355
356fn build_inner_borrowed<'a, S: Storage>(
357 storage: &'a mut S,
358 config: &Config,
359) -> BorrowedFsInner<'a, S> {
360 let cache_size = config.resolve_cache_size() as usize;
361 let lookahead_size = config.resolve_lookahead_size() as usize;
362
363 let mut read_buf = vec![0u8; cache_size];
364 let mut prog_buf = vec![0u8; cache_size];
365 let mut lookahead_buf = vec![0u8; lookahead_size];
366
367 let lfs_config = LfsConfig {
368 context: core::ptr::null_mut(),
369 read: Some(trampoline_read::<S>),
370 prog: Some(trampoline_prog::<S>),
371 erase: Some(trampoline_erase::<S>),
372 sync: Some(trampoline_sync::<S>),
373 read_size: config.read_size,
374 prog_size: config.prog_size,
375 block_size: config.block_size,
376 block_count: config.block_count,
377 block_cycles: config.block_cycles,
378 cache_size: config.resolve_cache_size(),
379 lookahead_size: config.resolve_lookahead_size(),
380 compact_thresh: u32::MAX,
381 read_buffer: read_buf.as_mut_ptr() as *mut c_void,
382 prog_buffer: prog_buf.as_mut_ptr() as *mut c_void,
383 lookahead_buffer: lookahead_buf.as_mut_ptr() as *mut c_void,
384 name_max: config.name_max,
385 file_max: config.file_max,
386 attr_max: config.attr_max,
387 metadata_max: 0,
388 inline_max: 0,
389 };
390
391 BorrowedFsInner {
392 lfs: MaybeUninit::zeroed(),
393 config: lfs_config,
394 storage,
395 _read_buf: read_buf,
396 _prog_buf: prog_buf,
397 _lookahead_buf: lookahead_buf,
398 }
399}
400
401fn wire_context_borrowed<S: Storage>(inner: &mut BorrowedFsInner<'_, S>) {
402 inner.config.context = inner.storage as *mut S as *mut c_void;
403 inner.config.read_buffer = inner._read_buf.as_mut_ptr() as *mut c_void;
404 inner.config.prog_buffer = inner._prog_buf.as_mut_ptr() as *mut c_void;
405 inner.config.lookahead_buffer = inner._lookahead_buf.as_mut_ptr() as *mut c_void;
406}
407
408fn null_terminate(s: &str) -> Vec<u8> {
409 let mut v: Vec<u8> = s.bytes().collect();
410 v.push(0);
411 v
412}