little_fs/
file_system.rs

1use core::ptr::null_mut;
2
3use super::{Configuration, Directory, convert_result, littlefs};
4use crate::{
5    File,
6    attributes::InternalAttributes,
7    configuration::{self},
8};
9use alloc::{boxed::Box, ffi::CString};
10use file_system::{
11    AttributeFlags, AttributeOperations, Attributes, BaseOperations, Context, DirectBlockDevice,
12    DirectoryOperations, Entry, Error, FileOperations, FileSystemOperations, Flags, Kind,
13    MountOperations, Path, Position, Result, Size, mount::MutexMountWrapper,
14};
15use synchronization::blocking_mutex::raw::CriticalSectionRawMutex;
16
17pub struct FileSystem {
18    file_system: MutexMountWrapper<CriticalSectionRawMutex, littlefs::lfs_t>,
19}
20
21impl FileSystem {
22    pub fn new_format(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<Self> {
23        Self::format(device, cache_size)?;
24
25        Self::new(device, cache_size)
26    }
27
28    pub fn get_or_format(
29        device: &'static dyn DirectBlockDevice,
30        cache_size: usize,
31    ) -> Result<Self> {
32        match Self::new(device, cache_size) {
33            Ok(file_system) => Ok(file_system),
34            Err(_) => {
35                device.set_position(0, &Position::Start(0))?;
36
37                Self::format(device, cache_size)?;
38
39                Self::new(device, cache_size)
40            }
41        }
42    }
43
44    pub fn new(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<Self> {
45        let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
46        let block_count = device.get_block_count().map_err(|_| Error::InputOutput)?;
47
48        let configuration: littlefs::lfs_config = Configuration::new(
49            device,
50            block_size as _,
51            block_count as _,
52            cache_size,
53            cache_size,
54        )
55        .ok_or(Error::InvalidParameter)?
56        .try_into()
57        .map_err(|_| Error::InvalidParameter)?;
58
59        let configuration = Box::new(configuration);
60
61        let mut file_system = littlefs::lfs_t::default();
62
63        unsafe {
64            convert_result(littlefs::lfs_mount(
65                &mut file_system,
66                Box::leak(configuration),
67            ))?;
68
69            let result = convert_result(littlefs::lfs_getattr(
70                &mut file_system,
71                c"/".as_ptr(),
72                InternalAttributes::IDENTIFIER,
73                null_mut(),
74                0,
75            ));
76
77            if let Err(Error::NoAttribute) = result {
78                // Set root attributes if not present
79                let mut internal_attributes = InternalAttributes::new_uninitialized().assume_init();
80                internal_attributes.kind = Kind::Directory;
81
82                convert_result(littlefs::lfs_setattr(
83                    &mut file_system,
84                    c"/".as_ptr(),
85                    InternalAttributes::IDENTIFIER,
86                    &internal_attributes as *const _ as *const _,
87                    size_of::<InternalAttributes>() as u32,
88                ))?;
89            } else {
90                result?;
91            }
92        }
93
94        Ok(Self {
95            file_system: MutexMountWrapper::new_mounted(file_system),
96        })
97    }
98
99    pub fn format(device: &'static dyn DirectBlockDevice, cache_size: usize) -> Result<()> {
100        let block_size = device.get_block_size().map_err(|_| Error::InputOutput)?;
101        let block_count = device.get_block_count().map_err(|_| Error::InputOutput)?;
102
103        let configuration: littlefs::lfs_config = Configuration::new(
104            device,
105            block_size as _,
106            block_count as _,
107            cache_size,
108            cache_size,
109        )
110        .ok_or(Error::InvalidParameter)?
111        .try_into()
112        .map_err(|_| Error::InvalidParameter)?;
113
114        let mut file_system = littlefs::lfs_t::default();
115
116        convert_result(unsafe { littlefs::lfs_format(&mut file_system, &configuration) })?;
117
118        Ok(())
119    }
120
121    pub fn operation<T>(
122        &self,
123        operation: impl FnOnce(&mut littlefs::lfs_t) -> Result<T>,
124    ) -> Result<T> {
125        let mut file_system = self.file_system.try_get()?;
126
127        operation(&mut file_system)
128    }
129
130    pub fn operation_with_context<I: 'static, T>(
131        &self,
132        context: &mut Context,
133        operation: impl FnOnce(&mut littlefs::lfs_t, &mut I) -> Result<T>,
134    ) -> Result<T> {
135        let mut file_system = self.file_system.try_get()?;
136
137        let file = context
138            .get_private_data_mutable_of_type::<I>()
139            .ok_or(Error::InvalidParameter)?;
140
141        operation(&mut file_system, file)
142    }
143}
144
145unsafe impl Send for FileSystem {}
146unsafe impl Sync for FileSystem {}
147
148impl MountOperations for FileSystem {
149    fn unmount(&self) -> Result<()> {
150        self.file_system.unmount()
151    }
152}
153
154impl FileSystemOperations for FileSystem {
155    fn rename(&self, source: &Path, destination: &Path) -> Result<()> {
156        self.operation(|file_system| {
157            let source = CString::new(source.as_str()).map_err(|_| Error::InvalidParameter)?;
158
159            let destination =
160                CString::new(destination.as_str()).map_err(|_| Error::InvalidParameter)?;
161
162            convert_result(unsafe {
163                littlefs::lfs_rename(file_system, source.as_ptr(), destination.as_ptr())
164            })?;
165
166            Ok(())
167        })
168    }
169
170    fn remove(&self, path: &Path) -> Result<()> {
171        let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
172
173        self.operation(|file_system| {
174            convert_result(unsafe { littlefs::lfs_remove(file_system, path.as_ptr()) })?;
175
176            Ok(())
177        })
178    }
179
180    fn create_directory(&self, path: &Path) -> Result<()> {
181        self.operation(|file_system| {
182            Directory::create(file_system, path)?;
183
184            Ok(())
185        })
186    }
187
188    fn lookup_directory(&self, context: &mut Context, path: &Path) -> Result<()> {
189        self.operation(|file_system| {
190            let directory = Directory::lookup(file_system, path)?;
191            context.set_private_data(directory);
192            Ok(())
193        })
194    }
195
196    fn lookup_file(&self, context: &mut Context, path: &Path, flags: Flags) -> Result<()> {
197        self.operation(|file_system| {
198            let file = File::lookup(file_system, path, flags)?;
199            context.set_private_data(file);
200            Ok(())
201        })
202    }
203
204    fn create_file(&self, path: &Path) -> Result<()> {
205        self.operation(|file_system| File::create(file_system, path))
206    }
207
208    fn get_attributes(&self, path: &Path, attributes: &mut Attributes) -> Result<()> {
209        self.operation(|file_system| {
210            let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
211
212            let mut internal_attributes =
213                unsafe { InternalAttributes::new_uninitialized().assume_init() };
214
215            convert_result(unsafe {
216                littlefs::lfs_getattr(
217                    file_system,
218                    path.as_ptr(),
219                    InternalAttributes::IDENTIFIER,
220                    &mut internal_attributes as *mut _ as *mut _,
221                    size_of::<InternalAttributes>() as u32,
222                )
223            })?;
224
225            internal_attributes.update_attributes(attributes)?;
226
227            if let Some(size) = attributes.get_mutable_size() {
228                let mut info = littlefs::lfs_info::default();
229
230                convert_result(unsafe {
231                    littlefs::lfs_stat(file_system, path.as_ptr(), &mut info)
232                })?;
233
234                *size = info.size as Size;
235            }
236
237            Ok(())
238        })
239    }
240
241    fn set_attributes(&self, path: &Path, attributes: &Attributes) -> Result<()> {
242        self.operation(|file_system| {
243            let path = CString::new(path.as_str()).map_err(|_| Error::InvalidParameter)?;
244
245            let mut internal_attributes =
246                unsafe { InternalAttributes::new_uninitialized().assume_init() };
247
248            if attributes.get_mask() != AttributeFlags::All {
249                convert_result(unsafe {
250                    littlefs::lfs_getattr(
251                        file_system,
252                        path.as_ptr(),
253                        InternalAttributes::IDENTIFIER,
254                        &mut internal_attributes as *mut _ as *mut _,
255                        size_of::<InternalAttributes>() as u32,
256                    )
257                })?;
258            }
259
260            internal_attributes.update_with_attributes(attributes)?;
261
262            convert_result(unsafe {
263                littlefs::lfs_setattr(
264                    file_system,
265                    path.as_ptr(),
266                    InternalAttributes::IDENTIFIER,
267                    &internal_attributes as *const _ as *const _,
268                    size_of::<InternalAttributes>() as u32,
269                )
270            })?;
271
272            Ok(())
273        })
274    }
275}
276
277impl AttributeOperations for FileSystem {
278    fn get_attributes(&self, context: &mut Context, attributes: &mut Attributes) -> Result<()> {
279        if let Some(file) = context.get_private_data_mutable_of_type::<File>() {
280            file.get_attributes(attributes)
281        } else if let Some(directory) = context.get_private_data_mutable_of_type::<Directory>() {
282            directory.get_attributes(attributes)
283        } else {
284            Err(Error::InvalidParameter)
285        }
286    }
287
288    fn set_attributes(&self, context: &mut Context, attributes: &Attributes) -> Result<()> {
289        if let Some(file) = context.get_private_data_mutable_of_type::<File>() {
290            file.set_attributes(attributes)
291        } else if let Some(directory) = context.get_private_data_mutable_of_type::<Directory>() {
292            directory.set_attributes(attributes)
293        } else {
294            Err(Error::InvalidParameter)
295        }
296    }
297}
298
299impl BaseOperations for FileSystem {
300    fn read(
301        &self,
302        context: &mut Context,
303        buffer: &mut [u8],
304        absolute_position: Size,
305    ) -> Result<usize> {
306        self.operation_with_context(context, |file_system, file: &mut File| {
307            file.read(file_system, buffer, absolute_position)
308        })
309    }
310
311    fn write(
312        &self,
313        context: &mut Context,
314        buffer: &[u8],
315        absolute_position: Size,
316    ) -> Result<usize> {
317        self.operation_with_context(context, |file_system, file: &mut File| {
318            file.write(file_system, buffer, absolute_position)
319        })
320    }
321
322    fn set_position(
323        &self,
324        context: &mut Context,
325        current_position: Size,
326        position: &Position,
327    ) -> Result<Size> {
328        self.operation_with_context(context, |file_system, file: &mut File| {
329            file.set_position(file_system, current_position, position)
330        })
331    }
332
333    fn flush(&self, context: &mut Context) -> Result<()> {
334        self.operation_with_context(context, |file_system, file: &mut File| {
335            file.flush(file_system)
336        })
337    }
338
339    fn clone_context(&self, context: &Context) -> Result<Context> {
340        if let Some(file) = context.get_private_data_of_type::<File>() {
341            Ok(Context::new(Some(file.clone())))
342        } else if let Some(directory) = context.get_private_data_of_type::<Directory>() {
343            Ok(Context::new(Some(directory.clone())))
344        } else {
345            Err(Error::InvalidParameter)
346        }
347    }
348
349    fn close(&self, context: &mut Context) -> Result<()> {
350        self.operation(|file_system| {
351            let mut file = context
352                .take_private_data_of_type::<File>()
353                .ok_or(Error::InvalidParameter)?;
354
355            file.close(file_system)?;
356
357            Ok(())
358        })
359    }
360}
361
362impl FileOperations for FileSystem {}
363
364impl DirectoryOperations for FileSystem {
365    fn read(&self, context: &mut Context) -> Result<Option<Entry>> {
366        self.operation_with_context(context, |file_system, directory: &mut Directory| {
367            directory.read(file_system)
368        })
369    }
370
371    fn get_position(&self, context: &mut file_system::Context) -> Result<Size> {
372        self.operation_with_context(context, |file_system, directory: &mut Directory| {
373            directory.get_position(file_system)
374        })
375    }
376
377    fn set_position(&self, context: &mut Context, position: Size) -> Result<()> {
378        self.operation_with_context(context, |file_system, directory: &mut Directory| {
379            directory.set_position(file_system, position)
380        })
381    }
382
383    fn rewind(&self, context: &mut Context) -> Result<()> {
384        self.operation_with_context(context, |file_system, directory: &mut Directory| {
385            directory.rewind(file_system)
386        })
387    }
388
389    fn close(&self, context: &mut Context) -> Result<()> {
390        self.operation(|file_system| {
391            let mut directory = context
392                .take_private_data_of_type::<Directory>()
393                .ok_or(Error::InvalidParameter)?;
394
395            directory.close(file_system)?;
396
397            Ok(())
398        })
399    }
400}
401
402impl Drop for FileSystem {
403    fn drop(&mut self) {
404        let _ = self.operation(|file_system| {
405            unsafe {
406                littlefs::lfs_unmount(file_system);
407            }
408
409            let mut configuration =
410                unsafe { Box::from_raw(file_system.cfg as *mut littlefs::lfs_config) };
411
412            unsafe {
413                configuration::Context::take_from_configuration(&mut *configuration);
414            }
415
416            Ok(())
417        });
418    }
419}
420
421#[cfg(test)]
422mod tests {
423    extern crate std;
424
425    use drivers_std;
426    use file_system::{MemoryDevice, file_system::tests::implement_file_system_tests};
427
428    use super::*;
429
430    const CACHE_SIZE: usize = 256;
431
432    drivers_std::memory::instantiate_global_allocator!();
433
434    fn initialize() -> FileSystem {
435        if !log::is_initialized() {
436            let _ = log::initialize(&drivers_std::log::Logger);
437        }
438
439        let _ = users::initialize();
440
441        task::initialize();
442
443        let _ = time::initialize(&drivers_std::devices::TimeDevice).unwrap();
444
445        let device = Box::leak(Box::new(MemoryDevice::<512>::new(2048 * 512)));
446
447        FileSystem::format(device, CACHE_SIZE).unwrap();
448
449        FileSystem::new(device, CACHE_SIZE).unwrap()
450    }
451
452    implement_file_system_tests!(initialize());
453}